From af46feb64e67a131b69b2594f4d75c25a8bbc366 Mon Sep 17 00:00:00 2001 From: Vinicius Moreira Date: Thu, 12 May 2022 14:13:12 -0300 Subject: [PATCH] [view] improvement: settings window size rules moved to stylesheet files --- CHANGELOG.md | 1 + bauh/api/abstract/view.py | 23 +++- bauh/commons/view_utils.py | 2 +- bauh/gems/appimage/controller.py | 3 - bauh/gems/arch/controller.py | 62 ++++------ bauh/gems/debian/controller.py | 16 +-- bauh/gems/flatpak/controller.py | 4 +- bauh/gems/snap/controller.py | 2 - bauh/gems/web/controller.py | 7 +- bauh/manage.py | 2 + bauh/view/core/settings.py | 76 +++--------- bauh/view/qt/components.py | 112 ++++++++++++++++-- bauh/view/qt/settings.py | 1 + bauh/view/resources/style/default/default.qss | 51 +++++++- 14 files changed, 225 insertions(+), 137 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fdf91701..21ded36c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - UI - manage window minimum width related to the table columns - table columns width for maximized window + - settings window size rules moved to stylesheet files ### Fixes diff --git a/bauh/api/abstract/view.py b/bauh/api/abstract/view.py index db4070ad..ce9fc0ec 100644 --- a/bauh/api/abstract/view.py +++ b/bauh/api/abstract/view.py @@ -9,6 +9,16 @@ class MessageType(Enum): ERROR = 2 +class ViewComponentAlignment(Enum): + CENTER = 0 + LEFT = 1 + RIGHT = 2 + BOTTOM = 3 + TOP = 4 + HORIZONTAL_CENTER = 5 + VERTICAL_CENTER = 6 + + class ViewObserver: def on_change(self, change): @@ -19,8 +29,10 @@ class ViewComponent(ABC): """ Represents a GUI component """ - def __init__(self, id_: Optional[str], observers: Optional[List[ViewObserver]] = None): + def __init__(self, id_: Optional[str], alignment: Optional[ViewComponentAlignment] = None, + observers: Optional[List[ViewObserver]] = None): self.id = id_ + self.alignment = alignment self.observers = observers if observers else [] def add_observer(self, obs): @@ -114,10 +126,11 @@ class SelectViewType(Enum): class SingleSelectComponent(InputViewComponent): - def __init__(self, type_: SelectViewType, label: str, options: List[InputOption], default_option: InputOption = None, - max_per_line: int = 1, tooltip: str = None, max_width: int = -1, id_: str = None, - capitalize_label: bool = True): - super(SingleSelectComponent, self).__init__(id_=id_) + def __init__(self, type_: SelectViewType, label: str, options: List[InputOption], + default_option: InputOption = None, max_per_line: int = 1, tooltip: str = None, + max_width: Optional[int] = -1, id_: str = None, capitalize_label: bool = True, + alignment: Optional[ViewComponentAlignment] = None): + super(SingleSelectComponent, self).__init__(id_=id_, alignment=alignment) self.type = type_ self.label = label self.options = options diff --git a/bauh/commons/view_utils.py b/bauh/commons/view_utils.py index 726e99b6..2138aaeb 100644 --- a/bauh/commons/view_utils.py +++ b/bauh/commons/view_utils.py @@ -4,7 +4,7 @@ from typing import Tuple, Optional, Iterable from bauh.api.abstract.view import SelectViewType, InputOption, SingleSelectComponent -def new_select(label: str, tip: Optional[str], id_: str, opts: Iterable[Tuple[Optional[str], object, Optional[str]]], value: object, max_width: int, +def new_select(label: str, tip: Optional[str], id_: str, opts: Iterable[Tuple[Optional[str], object, Optional[str]]], value: object, max_width: Optional[int] = None, type_: SelectViewType = SelectViewType.RADIO, capitalize_label: bool = True): inp_opts = [InputOption(label=o[0].capitalize(), value=o[1], tooltip=o[2]) for o in opts] def_opt = [o for o in inp_opts if o.value == value] diff --git a/bauh/gems/appimage/controller.py b/bauh/gems/appimage/controller.py index bf1775ea..1abc57ae 100644 --- a/bauh/gems/appimage/controller.py +++ b/bauh/gems/appimage/controller.py @@ -852,7 +852,6 @@ class AppImageManager(SoftwareManager, SettingsController): def get_settings(self) -> Optional[Generator[SettingsView, None, None]]: config_ = self.configman.get_config() - max_width = 50 comps = [ TextInputComponent(label=self.i18n['appimage.config.database.expiration'], @@ -860,14 +859,12 @@ class AppImageManager(SoftwareManager, SettingsController): config_['database']['expiration'], int) else '', tooltip=self.i18n['appimage.config.database.expiration.tip'], only_int=True, - max_width=max_width, id_='appim_db_exp'), TextInputComponent(label=self.i18n['appimage.config.suggestions.expiration'], value=int(config_['suggestions']['expiration']) if isinstance( config_['suggestions']['expiration'], int) else '', tooltip=self.i18n['appimage.config.suggestions.expiration.tip'], only_int=True, - max_width=max_width, id_='appim_sugs_exp') ] diff --git a/bauh/gems/arch/controller.py b/bauh/gems/arch/controller.py index 7e6a266b..231ba3a8 100644 --- a/bauh/gems/arch/controller.py +++ b/bauh/gems/arch/controller.py @@ -2805,8 +2805,11 @@ class ArchManager(SoftwareManager, SettingsController): final_cmd = pkg.command.replace('%U', '') subprocess.Popen(final_cmd, shell=True) - def _gen_bool_selector(self, id_: str, label_key: str, tooltip_key: str, value: bool, max_width: int, - capitalize_label: bool = True, label_params: Optional[list] = None, tooltip_params: Optional[list] = None) -> SingleSelectComponent: + def _gen_bool_selector(self, id_: str, label_key: str, tooltip_key: str, value: bool, + max_width: Optional[int] = None, capitalize_label: bool = True, + label_params: Optional[list] = None, tooltip_params: Optional[list] = None) \ + -> SingleSelectComponent: + opts = [InputOption(label=self.i18n['yes'].capitalize(), value=True), InputOption(label=self.i18n['no'].capitalize(), value=False)] @@ -2830,12 +2833,11 @@ class ArchManager(SoftwareManager, SettingsController): id_=id_, capitalize_label=capitalize_label) - def _get_general_settings(self, arch_config: dict, max_width: int) -> SettingsView: + def _get_general_settings(self, arch_config: dict) -> SettingsView: db_sync_start = self._gen_bool_selector(id_='sync_dbs_start', label_key='arch.config.sync_dbs', tooltip_key='arch.config.sync_dbs_start.tip', - value=bool(arch_config['sync_databases_startup']), - max_width=max_width) + value=bool(arch_config['sync_databases_startup'])) db_sync_start.label += f" ({self.i18n['initialization'].capitalize()})" @@ -2843,67 +2845,55 @@ class ArchManager(SoftwareManager, SettingsController): self._gen_bool_selector(id_='repos', label_key='arch.config.repos', tooltip_key='arch.config.repos.tip', - value=bool(arch_config['repositories']), - max_width=max_width), + value=bool(arch_config['repositories'])), self._gen_bool_selector(id_='autoprovs', label_key='arch.config.automatch_providers', tooltip_key='arch.config.automatch_providers.tip', - value=bool(arch_config['automatch_providers']), - max_width=max_width), + value=bool(arch_config['automatch_providers'])), self._gen_bool_selector(id_='prefer_repo_provider', label_key='arch.config.prefer_repository_provider', tooltip_key='arch.config.prefer_repository_provider.tip', value=bool(arch_config['prefer_repository_provider']), - max_width=max_width, tooltip_params=['AUR']), self._gen_bool_selector(id_='check_dependency_breakage', label_key='arch.config.check_dependency_breakage', tooltip_key='arch.config.check_dependency_breakage.tip', - value=bool(arch_config['check_dependency_breakage']), - max_width=max_width), + value=bool(arch_config['check_dependency_breakage'])), self._gen_bool_selector(id_='mthread_download', label_key='arch.config.pacman_mthread_download', tooltip_key='arch.config.pacman_mthread_download.tip', value=arch_config['repositories_mthread_download'], - max_width=max_width, capitalize_label=True), self._gen_bool_selector(id_='sync_dbs', label_key='arch.config.sync_dbs', tooltip_key='arch.config.sync_dbs.tip', - value=bool(arch_config['sync_databases']), - max_width=max_width), + value=bool(arch_config['sync_databases'])), db_sync_start, self._gen_bool_selector(id_='clean_cached', label_key='arch.config.clean_cache', tooltip_key='arch.config.clean_cache.tip', - value=bool(arch_config['clean_cached']), - max_width=max_width), + value=bool(arch_config['clean_cached'])), self._gen_bool_selector(id_='suggest_unneeded_uninstall', label_key='arch.config.suggest_unneeded_uninstall', tooltip_params=['"{}"'.format(self.i18n['arch.config.suggest_optdep_uninstall'])], tooltip_key='arch.config.suggest_unneeded_uninstall.tip', - value=bool(arch_config['suggest_unneeded_uninstall']), - max_width=max_width), + value=bool(arch_config['suggest_unneeded_uninstall'])), self._gen_bool_selector(id_='suggest_optdep_uninstall', label_key='arch.config.suggest_optdep_uninstall', tooltip_key='arch.config.suggest_optdep_uninstall.tip', - value=bool(arch_config['suggest_optdep_uninstall']), - max_width=max_width), + value=bool(arch_config['suggest_optdep_uninstall'])), self._gen_bool_selector(id_='ref_mirs', label_key='arch.config.refresh_mirrors', tooltip_key='arch.config.refresh_mirrors.tip', - value=bool(arch_config['refresh_mirrors_startup']), - max_width=max_width), + value=bool(arch_config['refresh_mirrors_startup'])), TextInputComponent(id_='mirrors_sort_limit', label=self.i18n['arch.config.mirrors_sort_limit'], tooltip=self.i18n['arch.config.mirrors_sort_limit.tip'], only_int=True, - max_width=50, value=arch_config['mirrors_sort_limit'] if isinstance(arch_config['mirrors_sort_limit'], int) else ''), TextInputComponent(id_='arch_cats_exp', label=self.i18n['arch.config.categories_exp'], tooltip=self.i18n['arch.config.categories_exp.tip'], - max_width=50, only_int=True, capitalize_label=False, value=arch_config['categories_exp'] if isinstance(arch_config['categories_exp'], int) else ''), @@ -2911,27 +2901,24 @@ class ArchManager(SoftwareManager, SettingsController): return SettingsView(self, PanelComponent([FormComponent(fields, spaces=False)], id_="repo"), icon_path=get_repo_icon_path()) - def _get_aur_settings(self, arch_config: dict, max_width: int) -> SettingsView: + def _get_aur_settings(self, arch_config: dict) -> SettingsView: fields = [ self._gen_bool_selector(id_='aur', label_key='arch.config.aur', tooltip_key='arch.config.aur.tip', value=arch_config['aur'], - max_width=max_width, capitalize_label=False), self._gen_bool_selector(id_='opts', label_key='arch.config.optimize', tooltip_key='arch.config.optimize.tip', value=bool(arch_config['optimize']), - capitalize_label=False, - max_width=max_width), + capitalize_label=False), self._gen_bool_selector(id_='rebuild_detector', label_key='arch.config.aur_rebuild_detector', tooltip_key='arch.config.aur_rebuild_detector.tip', value=bool(arch_config['aur_rebuild_detector']), tooltip_params=["'rebuild-detector'"], - capitalize_label=False, - max_width=max_width), + capitalize_label=False), self._gen_bool_selector(id_='rebuild_detector_no_bin', label_key='arch.config.aur_rebuild_detector_no_bin', label_params=['rebuild-detector'], @@ -2939,8 +2926,7 @@ class ArchManager(SoftwareManager, SettingsController): tooltip_params=['rebuild-detector', self.i18n['arch.config.aur_rebuild_detector'].format('')], value=bool(arch_config['aur_rebuild_detector_no_bin']), - capitalize_label=False, - max_width=max_width), + capitalize_label=False), new_select(id_='aur_build_only_chosen', label=self.i18n['arch.config.aur_build_only_chosen'], tip=self.i18n['arch.config.aur_build_only_chosen.tip'], @@ -2949,7 +2935,6 @@ class ArchManager(SoftwareManager, SettingsController): (self.i18n['ask'].capitalize(), None, None), ], value=arch_config['aur_build_only_chosen'], - max_width=max_width, type_=SelectViewType.RADIO, capitalize_label=False), new_select(label=self.i18n['arch.config.edit_aur_pkgbuild'], @@ -2960,27 +2945,23 @@ class ArchManager(SoftwareManager, SettingsController): (self.i18n['ask'].capitalize(), None, None), ], value=arch_config['edit_aur_pkgbuild'], - max_width=max_width, type_=SelectViewType.RADIO, capitalize_label=False), self._gen_bool_selector(id_='aur_remove_build_dir', label_key='arch.config.aur_remove_build_dir', tooltip_key='arch.config.aur_remove_build_dir.tip', value=bool(arch_config['aur_remove_build_dir']), - max_width=max_width, capitalize_label=False), FileChooserComponent(id_='aur_build_dir', label=self.i18n['arch.config.aur_build_dir'], tooltip=self.i18n['arch.config.aur_build_dir.tip'].format( get_build_dir(arch_config, self.pkgbuilder_user)), - max_width=round(max_width * 0.65), file_path=arch_config['aur_build_dir'], capitalize_label=False, directory=True), TextInputComponent(id_='aur_idx_exp', label=self.i18n['arch.config.aur_idx_exp'], tooltip=self.i18n['arch.config.aur_idx_exp.tip'], - max_width=50, only_int=True, capitalize_label=False, value=arch_config['aur_idx_exp'] if isinstance(arch_config['aur_idx_exp'], int) else '') @@ -2992,9 +2973,8 @@ class ArchManager(SoftwareManager, SettingsController): def get_settings(self) -> Optional[Generator[SettingsView, None, None]]: arch_config = self.configman.get_config() - max_width = floor(self.context.screen_width * 0.25) - yield self._get_general_settings(arch_config, max_width) - yield self._get_aur_settings(arch_config, max_width) + yield self._get_general_settings(arch_config) + yield self._get_aur_settings(arch_config) @staticmethod def fill_general_settings(arch_config: dict, form: FormComponent): diff --git a/bauh/gems/debian/controller.py b/bauh/gems/debian/controller.py index 53f2332a..8bc8fc64 100644 --- a/bauh/gems/debian/controller.py +++ b/bauh/gems/debian/controller.py @@ -16,7 +16,7 @@ from bauh.api.abstract.handler import TaskManager, ProcessWatcher from bauh.api.abstract.model import SoftwarePackage, PackageSuggestion, PackageUpdate, PackageHistory, \ CustomSoftwareAction from bauh.api.abstract.view import TextInputComponent, PanelComponent, FormComponent, MessageType, \ - SingleSelectComponent, InputOption, SelectViewType + SingleSelectComponent, InputOption, SelectViewType, ViewComponentAlignment from bauh.api.paths import CONFIG_DIR from bauh.commons.html import bold from bauh.commons.system import ProcessHandler @@ -603,7 +603,6 @@ class DebianPackageManager(SoftwareManager, SettingsController): options=purge_opts, default_option=purge_current, type_=SelectViewType.RADIO, - max_width=200, max_per_line=2) sources_app = config_.get('pkg_sources.app') @@ -623,8 +622,8 @@ class DebianPackageManager(SoftwareManager, SettingsController): tooltip=source_auto_tip, options=source_opts, default_option=next(o for o in source_opts if o.value == sources_app), - type_=SelectViewType.COMBO, - max_width=200) + alignment=ViewComponentAlignment.CENTER, + type_=SelectViewType.COMBO) try: app_cache_exp = int(config_.get('index_apps.exp', 0)) @@ -636,8 +635,7 @@ class DebianPackageManager(SoftwareManager, SettingsController): ti_index_apps_exp = TextInputComponent(id_='index_apps.exp', label=self._i18n['debian.config.index_apps.exp'], tooltip=self._i18n['debian.config.index_apps.exp.tip'], - value=str(app_cache_exp), only_int=True, - max_width=60) + value=str(app_cache_exp), only_int=True) try: sync_pkgs_time = int(config_.get('sync_pkgs.time', 0)) @@ -649,8 +647,7 @@ class DebianPackageManager(SoftwareManager, SettingsController): ti_sync_pkgs = TextInputComponent(id_='sync_pkgs.time', label=self._i18n['debian.config.sync_pkgs.time'], tooltip=self._i18n['debian.config.sync_pkgs.time.tip'], - value=str(sync_pkgs_time), only_int=True, - max_width=60) + value=str(sync_pkgs_time), only_int=True) try: suggestions_exp = int(config_.get('suggestions.exp', 0)) @@ -662,8 +659,7 @@ class DebianPackageManager(SoftwareManager, SettingsController): ti_suggestions_exp = TextInputComponent(id_='suggestions.exp', label=self._i18n['debian.config.suggestions.exp'], tooltip=self._i18n['debian.config.suggestions.exp.tip'], - value=str(suggestions_exp), only_int=True, - max_width=60) + value=str(suggestions_exp), only_int=True) panel = PanelComponent([FormComponent([input_sources, sel_purge, ti_sync_pkgs, ti_index_apps_exp, ti_suggestions_exp])]) diff --git a/bauh/gems/flatpak/controller.py b/bauh/gems/flatpak/controller.py index 41c1de90..674aebf0 100644 --- a/bauh/gems/flatpak/controller.py +++ b/bauh/gems/flatpak/controller.py @@ -18,7 +18,7 @@ from bauh.api.abstract.handler import ProcessWatcher, TaskManager from bauh.api.abstract.model import PackageHistory, PackageUpdate, SoftwarePackage, PackageSuggestion, \ SuggestionPriority, PackageStatus, CustomSoftwareAction from bauh.api.abstract.view import MessageType, FormComponent, SingleSelectComponent, InputOption, SelectViewType, \ - PanelComponent + PanelComponent, ViewComponentAlignment from bauh.commons.boot import CreateConfigFile from bauh.commons.html import strip_html, bold from bauh.commons.system import ProcessHandler @@ -668,8 +668,8 @@ class FlatpakManager(SoftwareManager, SettingsController): options=install_opts, default_option=[o for o in install_opts if o.value == flatpak_config['installation_level']][0], max_per_line=len(install_opts), - max_width=160, type_=SelectViewType.COMBO, + alignment=ViewComponentAlignment.CENTER, id_='install')) yield SettingsView(self, PanelComponent([FormComponent(fields, self.i18n['installation'].capitalize())])) diff --git a/bauh/gems/snap/controller.py b/bauh/gems/snap/controller.py index df7d9236..02d47f83 100644 --- a/bauh/gems/snap/controller.py +++ b/bauh/gems/snap/controller.py @@ -435,13 +435,11 @@ class SnapManager(SoftwareManager, SettingsController): (self.i18n['no'].capitalize(), False, None)], value=bool(snap_config['install_channel']), id_='snap_install_channel', - max_width=200, tip=self.i18n['snap.config.install_channel.tip']) cat_exp_val = snap_config['categories_exp'] if isinstance(snap_config['categories_exp'], int) else '' categories_exp = TextInputComponent(id_='snap_cat_exp', value=cat_exp_val, - max_width=60, only_int=True, label=self.i18n['snap.config.categories_exp'], tooltip=self.i18n['snap.config.categories_exp.tip']) diff --git a/bauh/gems/web/controller.py b/bauh/gems/web/controller.py index c2cf3aed..7844df6a 100644 --- a/bauh/gems/web/controller.py +++ b/bauh/gems/web/controller.py @@ -25,7 +25,7 @@ from bauh.api.abstract.model import SoftwarePackage, CustomSoftwareAction, Packa PackageHistory, \ SuggestionPriority, PackageStatus from bauh.api.abstract.view import MessageType, MultipleSelectComponent, InputOption, SingleSelectComponent, \ - SelectViewType, TextInputComponent, FormComponent, FileChooserComponent, PanelComponent + SelectViewType, TextInputComponent, FormComponent, FileChooserComponent, PanelComponent, ViewComponentAlignment from bauh.api.paths import DESKTOP_ENTRIES_DIR from bauh.commons import resource from bauh.commons.boot import CreateConfigFile @@ -1137,7 +1137,6 @@ class WebApplicationManager(SoftwareManager, SettingsController): value=web_config['environment']['electron']['version'], tooltip=self.i18n['web.settings.electron.version.tooltip'], placeholder='{}: 7.1.0'.format(self.i18n['example.short']), - max_width=150, id_='electron_branch') native_opts = [ @@ -1150,7 +1149,7 @@ class WebApplicationManager(SoftwareManager, SettingsController): default_option=[o for o in native_opts if o.value == web_config['environment']['system']][0], type_=SelectViewType.COMBO, tooltip=self.i18n['web.settings.nativefier.tip'], - max_width=150, + alignment=ViewComponentAlignment.CENTER, id_='nativefier') env_settings_exp = TextInputComponent(label=self.i18n['web.settings.cache_exp'], @@ -1158,7 +1157,6 @@ class WebApplicationManager(SoftwareManager, SettingsController): capitalize_label=False, value=int(web_config['environment']['cache_exp']) if isinstance(web_config['environment']['cache_exp'], int) else '', only_int=True, - max_width=60, id_='web_cache_exp') sugs_exp = TextInputComponent(label=self.i18n['web.settings.suggestions.cache_exp'], @@ -1167,7 +1165,6 @@ class WebApplicationManager(SoftwareManager, SettingsController): value=int(web_config['suggestions']['cache_exp']) if isinstance( web_config['suggestions']['cache_exp'], int) else '', only_int=True, - max_width=60, id_='web_sugs_exp') form_env = FormComponent(label=self.i18n['web.settings.nativefier.env'].capitalize(), diff --git a/bauh/manage.py b/bauh/manage.py index 0f8bf736..06a70eda 100644 --- a/bauh/manage.py +++ b/bauh/manage.py @@ -59,6 +59,8 @@ def new_manage_panel(app_args: Namespace, app_config: dict, logger: logging.Logg screen_size = app.primaryScreen().size() context.screen_width, context.screen_height = screen_size.width(), screen_size.height() + logger.info(f"Screen: {screen_size.width()} x {screen_size.height()} " + f"(DPI: {int(app.primaryScreen().logicalDotsPerInch())})") if app_args.settings: # only settings window manager.cache_available_managers() diff --git a/bauh/view/core/settings.py b/bauh/view/core/settings.py index 2abf9d6d..2ac85fa7 100644 --- a/bauh/view/core/settings.py +++ b/bauh/view/core/settings.py @@ -12,7 +12,7 @@ from bauh.api.abstract.context import ApplicationContext from bauh.api.abstract.controller import SoftwareManager, SettingsController, SettingsView from bauh.api.abstract.view import TabComponent, InputOption, TextComponent, MultipleSelectComponent, \ PanelComponent, FormComponent, TabGroupComponent, SingleSelectComponent, SelectViewType, TextInputComponent, \ - FileChooserComponent, RangeInputComponent + FileChooserComponent, RangeInputComponent, ViewComponentAlignment from bauh.commons.view_utils import new_select from bauh.view.core import timeshift from bauh.view.core.config import CoreConfigManager, BACKUP_REMOVE_METHODS, BACKUP_DEFAULT_REMOVE_METHOD @@ -87,7 +87,7 @@ class GenericSettingsManager(SettingsController): id_='core.types')) tabs.append(self._gen_general_settings(core_config)) - tabs.append(self._gen_ui_settings(core_config)) + tabs.append(self._gen_interface_settings(core_config)) tabs.append(self._gen_tray_settings(core_config)) tabs.append(self._gen_adv_settings(core_config)) @@ -102,26 +102,22 @@ class GenericSettingsManager(SettingsController): return TabGroupComponent(tabs) def _gen_adv_settings(self, core_config: dict) -> TabComponent: - default_width = 300 input_data_exp = TextInputComponent(label=self.i18n['core.config.mem_cache.data_exp'], tooltip=self.i18n['core.config.mem_cache.data_exp.tip'], value=str(core_config['memory_cache']['data_expiration']), only_int=True, - max_width=60, id_="data_exp") input_icon_exp = TextInputComponent(label=self.i18n['core.config.mem_cache.icon_exp'], tooltip=self.i18n['core.config.mem_cache.icon_exp.tip'], value=str(core_config['memory_cache']['icon_expiration']), only_int=True, - max_width=60, id_="icon_exp") select_trim = new_select(label=self.i18n['core.config.trim.after_upgrade'], tip=self.i18n['core.config.trim.after_upgrade.tip'], value=core_config['disk']['trim']['after_upgrade'], - max_width=default_width, opts=[(self.i18n['yes'].capitalize(), True, None), (self.i18n['no'].capitalize(), False, None), (self.i18n['ask'].capitalize(), None, None)], @@ -130,22 +126,20 @@ class GenericSettingsManager(SettingsController): select_dep_check = self._gen_bool_component(label=self.i18n['core.config.system.dep_checking'], tooltip=self.i18n['core.config.system.dep_checking.tip'], value=core_config['system']['single_dependency_checking'], - max_width=default_width, id_='dep_check') select_dmthread = self._gen_bool_component(label=self.i18n['core.config.download.multithreaded'], tooltip=self.i18n['core.config.download.multithreaded.tip'], id_="down_mthread", - max_width=default_width, value=core_config['download']['multithreaded']) - select_mthread_client = self._gen_multithread_client_select(core_config, default_width) + select_mthread_client = self._gen_multithread_client_select(core_config) inputs = [select_dmthread, select_mthread_client, select_trim, select_dep_check, input_data_exp, input_icon_exp] - panel = PanelComponent([FormComponent(inputs, spaces=False)]) + panel = PanelComponent([FormComponent(inputs, spaces=False)], id_='advanced') return TabComponent(self.i18n['core.config.tab.advanced'].capitalize(), panel, None, 'core.adv') - def _gen_multithread_client_select(self, core_config: dict, default_width: int) -> SingleSelectComponent: + def _gen_multithread_client_select(self, core_config: dict) -> SingleSelectComponent: available_mthread_clients = self.file_downloader.list_available_multithreaded_clients() available_mthread_clients.sort() @@ -163,18 +157,14 @@ class GenericSettingsManager(SettingsController): return new_select(label=self.i18n['core.config.download.multithreaded_client'], tip=self.i18n['core.config.download.multithreaded_client.tip'], id_="mthread_client", - max_width=default_width, opts=mthread_client_opts, value=current_mthread_client) def _gen_tray_settings(self, core_config: dict) -> TabComponent: - default_width = 350 - input_update_interval = TextInputComponent(label=self.i18n['core.config.updates.interval'].capitalize(), tooltip=self.i18n['core.config.updates.interval.tip'], only_int=True, value=str(core_config['updates']['check_interval']), - max_width=60, id_="updates_interval") allowed_exts = {'png', 'svg', 'jpg', 'jpeg', 'ico', 'xpm'} @@ -183,7 +173,6 @@ class GenericSettingsManager(SettingsController): label=self.i18n["core.config.ui.tray.default_icon"], tooltip=self.i18n["core.config.ui.tray.default_icon.tip"], file_path=de_path, - max_width=default_width, allowed_extensions=allowed_exts) up_path = str(core_config['ui']['tray']['updates_icon']) if core_config['ui']['tray']['updates_icon'] else None @@ -191,27 +180,22 @@ class GenericSettingsManager(SettingsController): label=self.i18n["core.config.ui.tray.updates_icon"].capitalize(), tooltip=self.i18n["core.config.ui.tray.updates_icon.tip"].capitalize(), file_path=up_path, - max_width=default_width, allowed_extensions=allowed_exts) sub_comps = [FormComponent([select_def_icon, select_up_icon, input_update_interval], spaces=False)] return TabComponent(self.i18n['core.config.tab.tray'].capitalize(), - PanelComponent(sub_comps), None, 'core.tray') - - def _gen_ui_settings(self, core_config: dict) -> TabComponent: - default_width = 200 + PanelComponent(sub_comps, id_='tray'), None, 'core.tray') + def _gen_interface_settings(self, core_config: dict) -> TabComponent: select_hdpi = self._gen_bool_component(label=self.i18n['core.config.ui.hdpi'], tooltip=self.i18n['core.config.ui.hdpi.tip'], value=bool(core_config['ui']['hdpi']), - max_width=default_width, id_='hdpi') scale_tip = self.i18n['core.config.ui.auto_scale.tip'].format('QT_AUTO_SCREEN_SCALE_FACTOR') select_ascale = self._gen_bool_component(label=self.i18n['core.config.ui.auto_scale'], tooltip=scale_tip, value=bool(core_config['ui']['auto_scale']), - max_width=default_width, id_='auto_scale') try: @@ -224,8 +208,7 @@ class GenericSettingsManager(SettingsController): select_scale = RangeInputComponent(id_="scalef", label=self.i18n['core.config.ui.scale_factor'] + ' (%)', tooltip=self.i18n['core.config.ui.scale_factor.tip'], - min_value=100, max_value=400, step_value=5, value=int(scale * 100), - max_width=60) + min_value=100, max_value=400, step_value=5, value=int(scale * 100)) if not core_config['ui']['qt_style']: cur_style = QApplication.instance().property('qt_style') @@ -250,38 +233,34 @@ class GenericSettingsManager(SettingsController): options=style_opts, default_option=default_style, type_=SelectViewType.COMBO, - max_width=default_width, + alignment=ViewComponentAlignment.CENTER, id_="style") systheme_tip = self.i18n['core.config.ui.system_theme.tip'].format(app=__app_name__) select_system_theme = self._gen_bool_component(label=self.i18n['core.config.ui.system_theme'], tooltip=systheme_tip, value=bool(core_config['ui']['system_theme']), - max_width=default_width, id_='system_theme') input_maxd = TextInputComponent(label=self.i18n['core.config.ui.max_displayed'], tooltip=self.i18n['core.config.ui.max_displayed.tip'], only_int=True, id_="table_max", - max_width=50, value=str(core_config['ui']['table']['max_displayed'])) select_dicons = self._gen_bool_component(label=self.i18n['core.config.download.icons'], tooltip=self.i18n['core.config.download.icons.tip'], id_="down_icons", - max_width=default_width, value=core_config['download']['icons']) sub_comps = [FormComponent([select_hdpi, select_ascale, select_scale, select_dicons, select_system_theme, select_style, input_maxd], spaces=False)] - return TabComponent(self.i18n['core.config.tab.ui'].capitalize(), PanelComponent(sub_comps), None, 'core.ui') + return TabComponent(self.i18n['core.config.tab.ui'].capitalize(), + PanelComponent(sub_comps, id_='interface'), None, 'core.ui') def _gen_general_settings(self, core_config: dict) -> TabComponent: - default_width = floor(0.15 * self.context.screen_width) - locale_keys = translation.get_available_keys() locale_opts = [InputOption(label=self.i18n[f'locale.{k}'].capitalize(), value=k) for k in locale_keys] @@ -303,55 +282,49 @@ class GenericSettingsManager(SettingsController): options=locale_opts, default_option=current_locale, type_=SelectViewType.COMBO, - max_width=default_width, + alignment=ViewComponentAlignment.CENTER, id_='locale') sel_store_pwd = self._gen_bool_component(label=self.i18n['core.config.store_password'].capitalize(), tooltip=self.i18n['core.config.store_password.tip'].capitalize(), id_="store_pwd", - max_width=default_width, value=bool(core_config['store_root_password'])) notify_tip = self.i18n['core.config.system.notifications.tip'].capitalize() sel_sys_notify = self._gen_bool_component(label=self.i18n['core.config.system.notifications'].capitalize(), tooltip=notify_tip, value=bool(core_config['system']['notifications']), - max_width=default_width, id_="sys_notify") sel_load_apps = self._gen_bool_component(label=self.i18n['core.config.boot.load_apps'], tooltip=self.i18n['core.config.boot.load_apps.tip'], value=bool(core_config['boot']['load_apps']), - id_='boot.load_apps', - max_width=default_width) + id_='boot.load_apps') sel_sugs = self._gen_bool_component(label=self.i18n['core.config.suggestions.activated'].capitalize(), tooltip=self.i18n['core.config.suggestions.activated.tip'].capitalize(), id_="sugs_enabled", - max_width=default_width, value=bool(core_config['suggestions']['enabled'])) inp_sugs = TextInputComponent(label=self.i18n['core.config.suggestions.by_type'], tooltip=self.i18n['core.config.suggestions.by_type.tip'], value=str(core_config['suggestions']['by_type']), only_int=True, - max_width=50, id_="sugs_by_type") inp_reboot = new_select(label=self.i18n['core.config.updates.reboot'], tip=self.i18n['core.config.updates.reboot.tip'], id_='ask_for_reboot', - max_width=default_width, + max_width=None, value=bool(core_config['updates']['ask_for_reboot']), opts=[(self.i18n['ask'].capitalize(), True, None), (self.i18n['no'].capitalize(), False, None)]) inputs = [sel_locale, sel_store_pwd, sel_sys_notify, sel_load_apps, inp_reboot, sel_sugs, inp_sugs] - panel = PanelComponent([FormComponent(inputs, spaces=False)]) + panel = PanelComponent([FormComponent(inputs, spaces=False)], id_='general') return TabComponent(self.i18n['core.config.tab.general'].capitalize(), panel, None, 'core.gen') - def _gen_bool_component(self, label: str, tooltip: Optional[str], value: bool, id_: str, max_width: int = 200) \ - -> SingleSelectComponent: + def _gen_bool_component(self, label: str, tooltip: Optional[str], value: bool, id_: str) -> SingleSelectComponent: opts = [InputOption(label=self.i18n['yes'].capitalize(), value=True), InputOption(label=self.i18n['no'].capitalize(), value=False)] @@ -362,7 +335,6 @@ class GenericSettingsManager(SettingsController): type_=SelectViewType.RADIO, tooltip=tooltip, max_per_line=len(opts), - max_width=max_width, id_=id_) def _save_settings(self, general: PanelComponent, @@ -570,15 +542,12 @@ class GenericSettingsManager(SettingsController): self.logger.info(f"Saving all settings took {tf - ti:.8f} seconds") return success, warnings - def _gen_backup_settings(self, core_config: dict) -> TabComponent: + def _gen_backup_settings(self, core_config: dict) -> Optional[TabComponent]: if timeshift.is_available(): - default_width = 350 - enabled_opt = self._gen_bool_component(label=self.i18n['core.config.backup'], tooltip=None, value=bool(core_config['backup']['enabled']), - id_='enabled', - max_width=default_width) + id_='enabled') ops_opts = [(self.i18n['yes'].capitalize(), True, None), (self.i18n['no'].capitalize(), False, None), @@ -588,28 +557,24 @@ class GenericSettingsManager(SettingsController): tip=None, value=core_config['backup']['install'], opts=ops_opts, - max_width=default_width, id_='install') uninstall_mode = new_select(label=self.i18n['core.config.backup.uninstall'], tip=None, value=core_config['backup']['uninstall'], opts=ops_opts, - max_width=default_width, id_='uninstall') upgrade_mode = new_select(label=self.i18n['core.config.backup.upgrade'], tip=None, value=core_config['backup']['upgrade'], opts=ops_opts, - max_width=default_width, id_='upgrade') downgrade_mode = new_select(label=self.i18n['core.config.backup.downgrade'], tip=None, value=core_config['backup']['downgrade'], opts=ops_opts, - max_width=default_width, id_='downgrade') mode = new_select(label=self.i18n['core.config.backup.mode'], @@ -621,13 +586,11 @@ class GenericSettingsManager(SettingsController): (self.i18n['core.config.backup.mode.only_one'], 'only_one', self.i18n['core.config.backup.mode.only_one.tip']) ], - max_width=default_width, id_='mode') type_ = new_select(label=self.i18n['type'].capitalize(), tip=None, value=core_config['backup']['type'], opts=[('rsync', 'rsync', None), ('btrfs', 'btrfs', None)], - max_width=default_width, id_='type') remove_method = core_config['backup']['remove_method'] @@ -646,10 +609,9 @@ class GenericSettingsManager(SettingsController): tip=None, value=remove_method, opts=remove_opts, - max_width=default_width, capitalize_label=False, id_='remove_method') inputs = [enabled_opt, type_, mode, sel_remove, install_mode, uninstall_mode, upgrade_mode, downgrade_mode] - panel = PanelComponent([FormComponent(inputs, spaces=False)]) + panel = PanelComponent([FormComponent(inputs, spaces=False)], id_='backup') return TabComponent(self.i18n['core.config.tab.backup'].capitalize(), panel, None, 'core.bkp') diff --git a/bauh/view/qt/components.py b/bauh/view/qt/components.py index 7b9da50f..7c04a9cc 100644 --- a/bauh/view/qt/components.py +++ b/bauh/view/qt/components.py @@ -11,7 +11,8 @@ from PyQt5.QtWidgets import QRadioButton, QGroupBox, QCheckBox, QComboBox, QGrid from bauh.api.abstract.view import SingleSelectComponent, InputOption, MultipleSelectComponent, SelectViewType, \ TextInputComponent, FormComponent, FileChooserComponent, ViewComponent, TabGroupComponent, PanelComponent, \ - TwoStateButtonComponent, TextComponent, SpacerComponent, RangeInputComponent, ViewObserver, TextInputType + TwoStateButtonComponent, TextComponent, SpacerComponent, RangeInputComponent, ViewObserver, TextInputType, \ + ViewComponentAlignment from bauh.view.util.translation import I18n @@ -256,6 +257,25 @@ class QtComponentsManager: del self._saved_states[state_id] +def map_alignment(alignment: ViewComponentAlignment) -> Optional[int]: + if alignment == ViewComponentAlignment.CENTER: + return Qt.AlignCenter + elif alignment == ViewComponentAlignment.LEFT: + return Qt.AlignLeft + elif alignment == ViewComponentAlignment.RIGHT: + return Qt.AlignRight + elif alignment == ViewComponentAlignment.HORIZONTAL_CENTER: + return Qt.AlignHCenter + elif alignment == ViewComponentAlignment.VERTICAL_CENTER: + return Qt.AlignVCenter + elif alignment == ViewComponentAlignment.BOTTOM: + return Qt.AlignBottom + elif alignment == ViewComponentAlignment.TOP: + return Qt.AlignTop + else: + return + + class RadioButtonQt(QRadioButton): def __init__(self, model: InputOption, model_parent: SingleSelectComponent): @@ -265,6 +285,9 @@ class RadioButtonQt(QRadioButton): self.toggled.connect(self._set_checked) self.setCursor(QCursor(Qt.PointingHandCursor)) + if model_parent.id: + self.setProperty('parent', model_parent.id) + if model.icon_path: if model.icon_path.startswith('/'): self.setIcon(QIcon(model.icon_path)) @@ -344,8 +367,17 @@ class FormComboBoxQt(QComboBox): def __init__(self, model: SingleSelectComponent): super(FormComboBoxQt, self).__init__() self.model = model + self.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Fixed) self.setCursor(QCursor(Qt.PointingHandCursor)) self.view().setCursor(QCursor(Qt.PointingHandCursor)) + self.setEditable(True) + self.lineEdit().setReadOnly(True) + + if model.alignment: + comp_alignment = map_alignment(model.alignment) + + if comp_alignment is not None: + self.lineEdit().setAlignment(comp_alignment) if model.max_width > 0: self.setMaximumWidth(int(model.max_width)) @@ -363,6 +395,9 @@ class FormComboBoxQt(QComboBox): self.currentIndexChanged.connect(self._set_selected) + if model.id: + self.setObjectName(model.id) + def _set_selected(self, idx: int): self.model.value = self.model.options[idx] self.setToolTip(self.model.value.tooltip) @@ -373,9 +408,13 @@ class FormRadioSelectQt(QWidget): def __init__(self, model: SingleSelectComponent, parent: QWidget = None): super(FormRadioSelectQt, self).__init__(parent=parent) self.model = model - self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) + self.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Minimum) + self.setProperty('opts', str(len(self.model.options) if self.model.options else 0)) - if model.max_width > 0: + if model.id: + self.setObjectName(model.id) + + if model.max_width and model.max_width > 0: self.setMaximumWidth(int(model.max_width)) grid = QGridLayout() @@ -400,7 +439,7 @@ class FormRadioSelectQt(QWidget): else: col += 1 - if model.max_width <= 0: + if model.max_width is not None and model.max_width <= 0: self.setMaximumWidth(int(self.sizeHint().width())) @@ -408,14 +447,20 @@ class RadioSelectQt(QGroupBox): def __init__(self, model: SingleSelectComponent): super(RadioSelectQt, self).__init__(model.label + ' :' if model.label else None) + + if model.id: + self.setObjectName(model.id) + if not model.label: - self.setObjectName('radio_select_notitle') + self.setProperty('no_label', 'true') self.model = model grid = QGridLayout() self.setLayout(grid) + self.setProperty('opts', str(len(model.options)) if model.options else '0') + line, col = 0, 0 for op in model.options: comp = RadioButtonQt(op, model) @@ -445,6 +490,9 @@ class ComboSelectQt(QGroupBox): self._layout.addWidget(QLabel(model.label + ' :' if model.label else ''), 0, 0) self._layout.addWidget(FormComboBoxQt(model), 0, 1) + if model.id: + self.setObjectName(model.id) + class QLineEditObserver(QLineEdit, ViewObserver): @@ -479,7 +527,10 @@ class TextInputQt(QGroupBox): self.model = model self.setLayout(QGridLayout()) - if self.model.max_width > 0: + if model.id: + self.setObjectName(model.id) + + if self.model.max_width and self.model.max_width > 0: self.setMaximumWidth(int(self.model.max_width)) self.text_input = QLineEditObserver() if model.type == TextInputType.SINGLE_LINE else QPlainTextEditObserver() @@ -578,6 +629,9 @@ class MultipleSelectQt(QGroupBox): pos_label = QLabel() self.layout().addWidget(pos_label, line + 1, 1) + if model.id: + self.setObjectName(model.id) + class FormMultipleSelectQt(QWidget): @@ -644,6 +698,9 @@ class FormMultipleSelectQt(QWidget): if model.label: self.layout().addWidget(QLabel(), line + 1, 1) + if model.id: + self.setObjectName(model.id) + class InputFilter(QLineEdit): @@ -705,6 +762,10 @@ class PanelQt(QWidget): super(PanelQt, self).__init__(parent=parent) self.model = model self.i18n = i18n + + if model.id: + self.setObjectName(model.id) + self.setLayout(QVBoxLayout()) self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) @@ -721,6 +782,9 @@ class FormQt(QGroupBox): self.i18n = i18n self.setLayout(QFormLayout()) + if model.id: + self.setObjectName(model.id) + if model.min_width and model.min_width > 0: self.setMinimumWidth(model.min_width) @@ -809,6 +873,10 @@ class FormQt(QGroupBox): def _new_text_input(self, c: TextInputComponent) -> Tuple[QLabel, QLineEdit]: view = QLineEditObserver() if c.type == TextInputType.SINGLE_LINE else QPlainTextEditObserver() + view.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Fixed) + + if c.id: + view.setObjectName(c.id) if c.min_width >= 0: view.setMinimumWidth(int(c.min_width)) @@ -855,12 +923,16 @@ class FormQt(QGroupBox): def _new_range_input(self, model: RangeInputComponent) -> QSpinBox: spinner = QSpinBox() + spinner.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Fixed) spinner.setCursor(QCursor(Qt.PointingHandCursor)) spinner.setMinimum(model.min) spinner.setMaximum(model.max) spinner.setSingleStep(model.step) spinner.setValue(model.value if model.value is not None else model.min) + if model.id: + spinner.setObjectName(model.id) + if model.tooltip: spinner.setToolTip(model.tooltip) @@ -872,10 +944,18 @@ class FormQt(QGroupBox): def _wrap(self, comp: QWidget, model: ViewComponent) -> QWidget: field_container = QWidget() + field_container.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) field_container.setLayout(QHBoxLayout()) field_container.layout().setContentsMargins(0, 0, 0, 0) + field_container.layout().setSpacing(0) + field_container.layout().setAlignment(Qt.AlignLeft) + field_container.setProperty('wrapper', 'true') + field_container.setProperty('wrapped_type', comp.__class__.__name__) - if model.max_width > 0: + if model.id: + field_container.setProperty('wrapped', model.id) + + if model.max_width and model.max_width > 0: field_container.setMaximumWidth(int(model.max_width)) field_container.layout().addWidget(comp) @@ -883,9 +963,14 @@ class FormQt(QGroupBox): def _new_file_chooser(self, c: FileChooserComponent) -> Tuple[QLabel, QLineEdit]: chooser = QLineEditObserver() + chooser.setProperty('file_chooser', 'true') + chooser.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Fixed) chooser.setReadOnly(True) - if c.max_width > 0: + if c.id: + chooser.setObjectName(c.id) + + if c.max_width and c.max_width > 0: chooser.setMaximumWidth(int(c.max_width)) if c.file_path: @@ -957,6 +1042,7 @@ class TabGroupQt(QTabWidget): icon = QIcon() scroll = QScrollArea() + scroll.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) scroll.setFrameShape(QFrame.NoFrame) scroll.setWidgetResizable(True) scroll.setWidget(to_widget(c.get_content(), i18n)) @@ -974,13 +1060,19 @@ def new_single_select(model: SingleSelectComponent) -> QWidget: raise Exception("Unsupported type {}".format(model.type)) -def new_spacer(min_width: int = None) -> QWidget: +def new_spacer(min_width: Optional[int] = None, min_height: Optional[int] = None, max_width: Optional[int] = None) -> QWidget: spacer = QWidget() spacer.setProperty('spacer', 'true') - if min_width: + if min_width is not None and min_width >= 0: spacer.setMinimumWidth(int(min_width)) + if max_width is not None and max_width >= 0: + spacer.setMaximumWidth(max_width) + + if min_height is not None and min_height >= 0: + spacer.setMaximumHeight(int(min_height)) + spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) return spacer diff --git a/bauh/view/qt/settings.py b/bauh/view/qt/settings.py index bb7bf0df..ae732c7c 100644 --- a/bauh/view/qt/settings.py +++ b/bauh/view/qt/settings.py @@ -83,6 +83,7 @@ class SettingsWindow(QWidget): def show(self): super(SettingsWindow, self).show() centralize(self) + self.setMinimumWidth(int(self.sizeHint().width())) def closeEvent(self, event): if self.window and self.window.settings_window == self: diff --git a/bauh/view/resources/style/default/default.qss b/bauh/view/resources/style/default/default.qss index 48dd1fc9..ea2219dc 100644 --- a/bauh/view/resources/style/default/default.qss +++ b/bauh/view/resources/style/default/default.qss @@ -161,7 +161,7 @@ RadioSelectQt { font-weight: bold; } -RadioSelectQt#radio_select_notitle { +RadioSelectQt[no_label = "true"] { padding-top: 5px; } @@ -396,10 +396,59 @@ ScreenshotsDialog QPushButton#close { min-width: 25px; } +SettingsWindow * { + font-size: 14px; +} + +SettingsWindow QLabel[tip_icon = "true"] { + qproperty-scaledContents: True; + min-height: 14px; + max-height: 14px; + min-width: 14px; + max-width: 14px; +} + SettingsWindow TabGroupQt#settings { min-width: 400px; } +SettingsWindow PanelQt FormComboBoxQt { + max-width: 190px; +} + +SettingsWindow PanelQt QLineEdit, SettingsWindow PanelQt QSpinBox { + max-width: 60px; +} + +SettingsWindow PanelQt QLineEdit[file_chooser = 'true'] { + max-width: 300px; +} + +SettingsWindow PanelQt FormRadioSelectQt[opts = '2'] { + min-width: 280px; + max-width: 280px; +} + +SettingsWindow PanelQt FormRadioSelectQt[opts = '3'] { + min-width: 280px; + max-width: 400px; +} + +SettingsWindow FormMultipleSelectQt#gems { + max-width: 220px; +} + +SettingsWindow FormMultipleSelectQt#gems QCheckBox { + qproperty-iconSize: 18px 18px; +} + +SettingsWindow FormMultipleSelectQt#gems QLabel[help_icon = "true"], SettingsWindow FormMultipleSelectQt#gems QLabel[warning_icon = "true"] { + min-height: 18px; + max-height: 18px; + min-width: 18px; + max-width: 18px; +} + QMenu QPushButton[current = "true"] { font-weight: bold; }