diff --git a/bauh/api/abstract/controller.py b/bauh/api/abstract/controller.py index f1c32b60..0bea45ac 100644 --- a/bauh/api/abstract/controller.py +++ b/bauh/api/abstract/controller.py @@ -274,8 +274,10 @@ class SoftwareManager(ABC): """ pass - def get_settings(self) -> ViewComponent: + def get_settings(self, screen_width: int, screen_height: int) -> ViewComponent: """ + :param screen_width + :param screen_height :return: a form abstraction with all available settings """ pass diff --git a/bauh/api/abstract/view.py b/bauh/api/abstract/view.py index 62afece0..865563fa 100644 --- a/bauh/api/abstract/view.py +++ b/bauh/api/abstract/view.py @@ -17,6 +17,12 @@ class ViewComponent(ABC): self.id = id_ +class SpacerComponent(ViewComponent): + + def __init__(self): + super(SpacerComponent, self).__init__(id_=None) + + class PanelComponent(ViewComponent): def __init__(self, components: List[ViewComponent], id_: str = None): @@ -87,7 +93,8 @@ class SingleSelectComponent(InputViewComponent): class MultipleSelectComponent(InputViewComponent): def __init__(self, label: str, options: List[InputOption], default_options: Set[InputOption] = None, - max_per_line: int = 1, tooltip: str = None, spaces: bool = True, id_: str = None): + max_per_line: int = 1, tooltip: str = None, spaces: bool = True, max_width: int = -1, + max_height: int = -1, id_: str = None): super(MultipleSelectComponent, self).__init__(id_=id_) if not options: @@ -99,6 +106,8 @@ class MultipleSelectComponent(InputViewComponent): self.tooltip = tooltip self.values = default_options if default_options else set() self.max_per_line = max_per_line + self.max_width = max_width + self.max_height = max_height def get_selected_values(self) -> list: selected = [] @@ -110,10 +119,11 @@ class MultipleSelectComponent(InputViewComponent): class TextComponent(ViewComponent): - def __init__(self, html: str, max_width: int = -1, id_: str = None): + def __init__(self, html: str, max_width: int = -1, tooltip: str = None, id_: str = None): super(TextComponent, self).__init__(id_=id_) self.value = html self.max_width = max_width + self.tooltip = tooltip class TwoStateButtonComponent(ViewComponent): diff --git a/bauh/app.py b/bauh/app.py index 70e25aca..a1f95bca 100755 --- a/bauh/app.py +++ b/bauh/app.py @@ -74,7 +74,8 @@ def main(): if local_config['ui']['hdpi']: logger.info("HDPI settings activated") - app.setAttribute(Qt.AA_UseHighDpiPixmaps) # This fix images on HDPI resolution, not tested on non HDPI + app.setAttribute(Qt.AA_UseHighDpiPixmaps) + app.setAttribute(Qt.AA_EnableHighDpiScaling) if local_config['ui']['style']: app.setStyle(str(local_config['ui']['style'])) diff --git a/bauh/gems/appimage/controller.py b/bauh/gems/appimage/controller.py index 8c6ec3eb..b1129541 100644 --- a/bauh/gems/appimage/controller.py +++ b/bauh/gems/appimage/controller.py @@ -7,6 +7,7 @@ import sqlite3 import subprocess import traceback from datetime import datetime +from math import floor from pathlib import Path from threading import Lock, Thread from typing import Set, Type, List, Tuple @@ -499,9 +500,9 @@ class AppImageManager(SoftwareManager): print('{}[bauh][appimage] An exception has happened when deleting {}{}'.format(Fore.RED, f, Fore.RESET)) traceback.print_exc() - def get_settings(self) -> ViewComponent: - + def get_settings(self, screen_width: int, screen_height: int) -> ViewComponent: config = read_config() + max_width = floor(screen_width * 0.15) enabled_opts = [InputOption(label=self.i18n['yes'].capitalize(), value=True), InputOption(label=self.i18n['no'].capitalize(), value=False)] @@ -513,13 +514,13 @@ class AppImageManager(SoftwareManager): max_per_line=len(enabled_opts), type_=SelectViewType.RADIO, tooltip=self.i18n['appimage.config.db_updates.activated.tip'], - max_width=200, + max_width=max_width, id_='up_enabled'), TextInputComponent(label=self.i18n['interval'], value=str(config['db_updater']['interval']), tooltip=self.i18n['appimage.config.db_updates.interval.tip'], only_int=True, - max_width=150, + max_width=max_width, id_='up_int') ] diff --git a/bauh/gems/arch/controller.py b/bauh/gems/arch/controller.py index ba6a7d3a..a7f954eb 100644 --- a/bauh/gems/arch/controller.py +++ b/bauh/gems/arch/controller.py @@ -5,6 +5,7 @@ import shutil import subprocess import time import traceback +from math import floor from pathlib import Path from threading import Thread from typing import List, Set, Type, Tuple, Dict @@ -888,8 +889,9 @@ class ArchManager(SoftwareManager): def get_screenshots(self, pkg: SoftwarePackage) -> List[str]: pass - def get_settings(self) -> ViewComponent: + def get_settings(self, screen_width: int, screen_height: int) -> ViewComponent: config = read_config() + max_width = floor(screen_width * 0.15) optz_opts = [InputOption(label=self.i18n['yes'].capitalize(), value=True), InputOption(label=self.i18n['no'].capitalize(), value=False)] @@ -904,14 +906,14 @@ class ArchManager(SoftwareManager): max_per_line=len(optz_opts), type_=SelectViewType.RADIO, tooltip=self.i18n['arch.config.optimize.tip'], - max_width=200, + max_width=max_width, id_='opts'), SingleSelectComponent(label=self.i18n['arch.config.trans_dep_check'].capitalize(), options=trans_check_opts, default_option=[o for o in trans_check_opts if o.value == config['transitive_checking']][0], max_per_line=len(trans_check_opts), type_=SelectViewType.RADIO, - max_width=200, + max_width=max_width, tooltip=self.i18n['arch.config.trans_dep_check.tip'], id_='dep_check')] diff --git a/bauh/gems/flatpak/controller.py b/bauh/gems/flatpak/controller.py index fc361d11..99e5521c 100644 --- a/bauh/gems/flatpak/controller.py +++ b/bauh/gems/flatpak/controller.py @@ -1,5 +1,6 @@ import traceback from datetime import datetime +from math import floor from threading import Thread from typing import List, Set, Type, Tuple @@ -418,7 +419,7 @@ class FlatpakManager(SoftwareManager): return urls - def get_settings(self) -> ViewComponent: + def get_settings(self, screen_width: int, screen_height: int) -> ViewComponent: fields = [] config = read_config() @@ -436,7 +437,7 @@ class FlatpakManager(SoftwareManager): options=install_opts, default_option=[o for o in install_opts if o.value == config['installation_level']][0], max_per_line=len(install_opts), - max_width=300, + max_width=floor(screen_width * 0.22), type_=SelectViewType.RADIO)) return PanelComponent([FormComponent(fields, self.i18n['installation'].capitalize())]) diff --git a/bauh/gems/web/controller.py b/bauh/gems/web/controller.py index af29f076..a8eaef11 100644 --- a/bauh/gems/web/controller.py +++ b/bauh/gems/web/controller.py @@ -5,6 +5,7 @@ import re import shutil import subprocess import traceback +from math import floor from pathlib import Path from threading import Thread from typing import List, Type, Set, Tuple @@ -891,14 +892,15 @@ class WebApplicationManager(SoftwareManager): print('{}[bauh][web] An exception has happened when deleting {}{}'.format(Fore.RED, ENV_PATH, Fore.RESET)) traceback.print_exc() - def get_settings(self) -> ViewComponent: + def get_settings(self, screen_width: int, screen_height: int) -> ViewComponent: config = read_config() + max_width = floor(screen_width * 0.15) input_electron = TextInputComponent(label=self.i18n['web.settings.electron.version.label'], value=config['environment']['electron']['version'], tooltip=self.i18n['web.settings.electron.version.tooltip'], placeholder='{}: 7.1.0'.format(self.i18n['example.short']), - max_width=200, + max_width=max_width, id_='electron_version') native_opts = [ @@ -911,7 +913,7 @@ class WebApplicationManager(SoftwareManager): default_option=[o for o in native_opts if o.value == config['environment']['system']][0], type_=SelectViewType.COMBO, tooltip=self.i18n['web.settings.nativefier.tip'], - max_width=200, + max_width=max_width, id_='nativefier') form_env = FormComponent(label=self.i18n['web.settings.nativefier.env'].capitalize(), components=[input_electron, select_nativefier]) diff --git a/bauh/view/core/controller.py b/bauh/view/core/controller.py index 355df19d..8ad6f76e 100755 --- a/bauh/view/core/controller.py +++ b/bauh/view/core/controller.py @@ -1,6 +1,7 @@ import re import time import traceback +from math import floor from threading import Thread from typing import List, Set, Type, Tuple @@ -13,7 +14,7 @@ from bauh.api.abstract.handler import ProcessWatcher from bauh.api.abstract.model import SoftwarePackage, PackageUpdate, PackageHistory, PackageSuggestion, PackageAction from bauh.api.abstract.view import FormComponent, ViewComponent, TabGroupComponent, TabComponent, SingleSelectComponent, \ InputOption, PanelComponent, SelectViewType, TextInputComponent, FileChooserComponent, MultipleSelectComponent, \ - TextComponent + TextComponent, SpacerComponent from bauh.api.exception import NoInternetException from bauh.commons import internet from bauh.view.core import config @@ -421,7 +422,9 @@ class GenericSoftwareManager(SoftwareManager): max_width=max_width, id_=id_) - def _gen_general_settings(self, core_config: dict) -> TabComponent: + def _gen_general_settings(self, core_config: dict, screen_width: int, screen_height: int) -> TabComponent: + default_width = floor(0.11 * screen_width) + locale_opts = [InputOption(label=self.i18n['locale.{}'.format(k)].capitalize(), value=k) for k in translation.get_available_keys()] current_locale = None @@ -438,30 +441,33 @@ class GenericSoftwareManager(SoftwareManager): options=locale_opts, default_option=current_locale, type_=SelectViewType.COMBO, - max_width=150, + max_width=default_width, id_='locale') select_sysnotify = self._gen_bool_component(label=self.i18n['core.config.system.notifications'].capitalize(), tooltip=self.i18n['core.config.system.notifications.tip'].capitalize(), value=bool(core_config['system']['notifications']), + max_width=default_width, id_="sys_notify") select_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=150, + max_width=default_width, id_="sugs_by_type") sub_comps = [FormComponent([select_locale, select_sysnotify, select_sugs, inp_sugs], spaces=False)] return TabComponent(self.i18n['core.config.tab.general'].capitalize(), PanelComponent(sub_comps), None, 'core.gen') - def _gen_adv_settings(self, core_config: dict) -> TabComponent: + def _gen_adv_settings(self, core_config: dict, screen_width: int, screen_height: int) -> TabComponent: + default_width = floor(0.11 * screen_width) select_dcache = self._gen_bool_component(label=self.i18n['core.config.disk_cache'], tooltip=self.i18n['core.config.disk_cache.tip'], @@ -472,35 +478,39 @@ class GenericSoftwareManager(SoftwareManager): tooltip=self.i18n['core.config.mem_cache.data_exp.tip'], value=str(core_config['memory_cache']['data_expiration']), only_int=True, - max_width=150, + max_width=default_width, 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=150, + max_width=default_width, id_="icon_exp") 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']) sub_comps = [FormComponent([select_dcache, select_dmthread, select_dep_check, input_data_exp, input_icon_exp], spaces=False)] return TabComponent(self.i18n['core.config.tab.advanced'].capitalize(), PanelComponent(sub_comps), None, 'core.adv') - def _gen_tray_settings(self, core_config: dict) -> TabComponent: + def _gen_tray_settings(self, core_config: dict, screen_width: int, screen_height: int) -> TabComponent: + default_width = floor(0.22 * screen_width) + 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=250, + max_width=default_width, id_="updates_interval") allowed_exts = {'png', 'svg', 'jpg', 'jpeg', 'ico', 'xpm'} @@ -508,24 +518,27 @@ class GenericSoftwareManager(SoftwareManager): label=self.i18n["core.config.ui.tray.default_icon"].capitalize(), tooltip=self.i18n["core.config.ui.tray.default_icon.tip"].capitalize(), file_path=str(core_config['ui']['tray']['default_icon']) if core_config['ui']['tray']['default_icon'] else None, - max_width=250, + max_width=default_width, allowed_extensions=allowed_exts) select_up_icon = FileChooserComponent(id_='up_icon', label=self.i18n["core.config.ui.tray.updates_icon"].capitalize(), tooltip=self.i18n["core.config.ui.tray.updates_icon.tip"].capitalize(), file_path=str(core_config['ui']['tray']['updates_icon']) if core_config['ui']['tray']['updates_icon'] else None, - max_width=250, + max_width=default_width, allowed_extensions=allowed_exts) sub_comps = [FormComponent([input_update_interval, select_def_icon, select_up_icon], 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: + def _gen_ui_settings(self, core_config: dict, screen_width: int, screen_height: int) -> TabComponent: + default_width = floor(0.11 * screen_width) + select_hdpi = self._gen_bool_component(label=self.i18n['core.config.ui.hdpi'], tooltip=self.i18n['core.config.ui.hdpi.tip'].format( app=self.context.app_name), value=bool(core_config['ui']['hdpi']), + max_width=default_width, id_='hdpi') cur_style = QApplication.instance().style().objectName().lower() if not core_config['ui']['style'] else core_config['ui']['style'] @@ -534,19 +547,20 @@ class GenericSoftwareManager(SoftwareManager): options=style_opts, default_option=[o for o in style_opts if o.value == cur_style][0], type_=SelectViewType.COMBO, - max_width=150, + max_width=default_width, id_="style") input_maxd = TextInputComponent(label=self.i18n['core.config.ui.max_displayed'].capitalize(), tooltip=self.i18n['core.config.ui.max_displayed.tip'].capitalize(), only_int=True, id_="table_max", - max_width=150, + max_width=default_width, 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_dicons, select_style, input_maxd], spaces=False)] @@ -613,7 +627,7 @@ class GenericSoftwareManager(SoftwareManager): core_config['ui']['style'] = style # gems - checked_gems = gems_panel.get_component('gems').get_selected_values() + checked_gems = gems_panel.components[1].get_component('gems').get_selected_values() for man in self.managers: modname = man.__module__.split('.')[-2] @@ -628,14 +642,14 @@ class GenericSoftwareManager(SoftwareManager): except: return False, [traceback.format_exc()] - def get_settings(self) -> ViewComponent: + def get_settings(self, screen_width: int, screen_height: int) -> ViewComponent: tabs = list() gem_opts, def_gem_opts, gem_tabs = [], set(), [] for man in self.managers: if man.can_work(): - man_comp = man.get_settings() + man_comp = man.get_settings(screen_width, screen_height) modname = man.__module__.split('.')[-2] icon_path = "{r}/gems/{n}/resources/img/{n}.svg".format(r=ROOT_DIR, n=modname) @@ -652,7 +666,7 @@ class GenericSoftwareManager(SoftwareManager): def_gem_opts.add(opt) core_config = read_config() - tabs.append(self._gen_general_settings(core_config)) + tabs.append(self._gen_general_settings(core_config, screen_width, screen_height)) if gem_opts: type_help = TextComponent(html=self.i18n['core.config.types.tip']) @@ -660,15 +674,16 @@ class GenericSoftwareManager(SoftwareManager): gem_selector = MultipleSelectComponent(label=None, tooltip=None, options=gem_opts, + max_width=floor(screen_width * 0.30), default_options=def_gem_opts, id_="gems") tabs.append(TabComponent(label=self.i18n['core.config.tab.types'], - content=PanelComponent([type_help, gem_selector]), + content=PanelComponent([type_help, FormComponent([gem_selector], spaces=False)]), id_='core.types')) - tabs.append(self._gen_ui_settings(core_config)) - tabs.append(self._gen_tray_settings(core_config)) - tabs.append(self._gen_adv_settings(core_config)) + tabs.append(self._gen_ui_settings(core_config, screen_width, screen_height)) + tabs.append(self._gen_tray_settings(core_config, screen_width, screen_height)) + tabs.append(self._gen_adv_settings(core_config, screen_width, screen_height)) for tab in gem_tabs: tabs.append(tab) diff --git a/bauh/view/qt/components.py b/bauh/view/qt/components.py index 1004d1e0..76bf2fec 100644 --- a/bauh/view/qt/components.py +++ b/bauh/view/qt/components.py @@ -10,7 +10,7 @@ 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 + TwoStateButtonComponent, TextComponent, SpacerComponent from bauh.view.qt import css from bauh.view.util import resource from bauh.view.util.translation import I18n @@ -225,6 +225,12 @@ class MultipleSelectQt(QGroupBox): self._layout = QGridLayout() self.setLayout(self._layout) + if model.max_width > 0: + self.setMaximumWidth(model.max_width) + + if model.max_height > 0: + self.setMaximumHeight(model.max_height) + if model.label: line = 1 pre_label = QLabel() @@ -280,6 +286,12 @@ class FormMultipleSelectQt(QWidget): self.model = model self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred) + if model.max_width > 0: + self.setMaximumWidth(model.max_width) + + if model.max_height > 0: + self.setMaximumHeight(model.max_height) + self._layout = QGridLayout() self.setLayout(self._layout) @@ -432,6 +444,8 @@ class FormQt(QGroupBox): elif isinstance(c, MultipleSelectComponent): label = self._new_label(c) self.layout().addRow(label, FormMultipleSelectQt(c)) + elif isinstance(c, TextComponent): + self.layout().addRow(self._new_label(c), QWidget()) else: raise Exception('Unsupported component type {}'.format(c.__class__.__name__)) @@ -445,8 +459,11 @@ class FormQt(QGroupBox): label_comp = QLabel() label.layout().addWidget(label_comp) - if comp.label: - label_comp.setText(comp.label.capitalize()) + attr = 'label' if hasattr(comp,'label') else 'value' + text = getattr(comp, attr) + + if text: + label_comp.setText(text.capitalize()) if comp.tooltip: label.layout().addWidget(self.gen_tip_icon(comp.tooltip)) @@ -601,5 +618,7 @@ def to_widget(comp: ViewComponent, i18n: I18n, parent: QWidget = None) -> QWidge label = QLabel(comp.value) label.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) return label + elif isinstance(comp, SpacerComponent): + return new_spacer() else: raise Exception("Cannot render instances of " + comp.__class__.__name__) diff --git a/bauh/view/qt/settings.py b/bauh/view/qt/settings.py index b1a12688..2ce98fa2 100644 --- a/bauh/view/qt/settings.py +++ b/bauh/view/qt/settings.py @@ -24,7 +24,7 @@ class SettingsWindow(QWidget): self.tray = tray self.window = window - self.settings_model = self.manager.get_settings() + self.settings_model = self.manager.get_settings(screen_size.width(), screen_size.height()) tab_group = to_widget(self.settings_model, i18n) tab_group.setMinimumWidth(int(screen_size.width() / 3))