diff --git a/CHANGELOG.md b/CHANGELOG.md
index 22a656fd..6a02776d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,11 +5,51 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [0.9.9]
+### Features
+- Themes (stylesheets)
+ - new settings property "theme": it points to a file defining a set of customizations over the current style (QT). In other words, a stylesheet file. At the moment 3 will come bundled with bauh:
+ - [Light](https://raw.githubusercontent.com/vinifmor/bauh-files/master/pictures/releases/0.9.9/light.png): default light theme
+ - [Darcula](https://raw.githubusercontent.com/vinifmor/bauh-files/master/pictures/releases/0.9.9/darcula.png): dark based on JetBrain's Darcula theme
+ - [Sublime](https://raw.githubusercontent.com/vinifmor/bauh-files/master/pictures/releases/0.9.9/sublime.png): dark based on Sublime Text's editor theme
+ - the theme can be changed through the new lower bar button:
+
+
+
+
+ - you can provide custom themes by putting the required files at **~/.local/share/bauh/themes**. There are 2 required and 1 optional file for each theme (all files related to a theme must share the same name):
+ - **my_theme.qss**: file with the qss rules. Example: [light.qss](https://raw.githubusercontent.com/vinifmor/bauh/qss/bauh/view/resources/style/light/light.qss)
+ - **my_theme.meta**: file defining the data about the theme (display name, description, version, ...). Example: [light.meta](https://raw.githubusercontent.com/vinifmor/bauh/qss/bauh/view/resources/style/light/light.meta)
+ - **my_theme.vars**: optional file defining `key=value` variables that will be available for the .qss file through the symbol '@' (e.g: @my_var). Example: [light.vars](https://raw.githubusercontent.com/vinifmor/bauh/qss/bauh/view/resources/style/light/light.vars)
+ - common theme variables available:
+ - **style_dir**: path to the .qss file directory. Example: @style_dir/my_icon.svg
+ - **images**: path to bauh's icons (gem icons are not available through this variable). Example: @images/logo.svg
+
+### Improvements
+- UI
+ - root dialog design and behavior
+ - tooltip for the label displaying the number of applications on the table/available [#138](https://github.com/vinifmor/bauh/issues/138)
+ - screenshots dialog resizing behavior
+ - "name filter" now requires ENTER or click to be triggered
+ - some app actions icons are now displayed with a different picture when disabled to prevent confusion (e.g: launch, screenshots) [#138](https://github.com/vinifmor/bauh/issues/138)
+ - suggestions button moved to the lower bar (label removed)
+
+
+
+
+- Settings
+ - new property **system_theme** (UI -> System theme): merges the system's theme/stylesheet with bauh's (default: false)
+ - property **style** renamed to **qt_style** and its default value now is **fusion**. If this property is set to **null**, **fusion** will be considered as well. Fusion is the default style that all default themes (stylesheets) are based on, so if you change this property the final style may not look like as expected.
+ - **Applications displayed** property (Interface) tooltip now informs that 0 (zero) can be used for no limit [#138](https://github.com/vinifmor/bauh/issues/138)
+- Parameters
+ - new parameter **--offline**: it assumes the internet connection is off. Useful if the connection is bad/unstable and you just want to check your installed packages.
+
### Fixes
- Arch:
- search: not able to find installed packages that were renamed on the repositories (e.g: xapps -> xapp)
- not able to replace an installed package for a new one that replaces it during conflict resolutions (e.g: xapp replaces xapps)
- AUR: not able to find some repository dependencies when their names are not an exact match (e.g: sc-controller [0.4.7-1] relies on "pylibacl". This dependency now is called "python-pylibacl")
+- UI
+ - wrong tooltips
### i18n
- French translations by [KINFOO](https://github.com/KINFOO): [#143](https://github.com/vinifmor/bauh/pull/143)
diff --git a/README.md b/README.md
index 6fb25aa0..d05c6d05 100644
--- a/README.md
+++ b/README.md
@@ -275,6 +275,7 @@ You can change some application settings via environment variables or arguments
- `--settings`: it displays only the settings window.
- `--reset`: it cleans all configurations and cached data stored in the HOME directory.
- `--logs`: it enables logs (for debugging purposes).
+- `--offline`: it assumes the internet connection is off.
#### General configuration file (**~/.config/bauh/config.yml**)
```
@@ -295,15 +296,17 @@ system:
notifications: true # if system popup should be displayed for some events. e.g: when there are updates, bauh will display a system popup
single_dependency_checking: false # if bauh should check only once if for the available technologies on the system.
ui:
- style: null # the current QT style set. A null value will map to 'Fusion', 'Breeze' or 'Oxygen' (depending on what is installed)
+ qt_style: fusion # defines the Qt style. A null value will map to 'fusion' as well.
table:
- max_displayed: 50 # defines the maximum number of displayed applications on the table.
+ max_displayed: 50 # defines the maximum number of displayed applications on the table. Use 0 for no limit.
tray: # system tray settings
default_icon: null # defines a path to a custom icon
updates_icon: null # defines a path to a custom icon indicating updates
hdpi: true # enables HDPI rendering improvements. Use 'false' to disable them if you think the interface looks strange
auto_scale: false # activates Qt auto screen scale factor (QT_AUTO_SCREEN_SCALE_FACTOR). It fixes scaling issues for some desktop environments (like Gnome)
scale_factor: 1.0 # defines the interface display scaling factor (Qt). Raise the value to raise the interface size. The settings window display this value as a percentage (e.g: 1.0 -> 100%).
+ theme: defines the path to the theme/stylesheet file with a .qss extension (e.g: /path/to/my/theme.qss). For themes provided by bauh, only a string key is needed (e.g: light). Default: light
+ system_theme: merges the system's theme/stylesheet with bauh's. Default: false.
updates:
check_interval: 30 # the updates checking interval in SECONDS
ask_for_reboot: true # if a dialog asking for a system reboot should be displayed after a successful upgrade
diff --git a/bauh/__init__.py b/bauh/__init__.py
index 52b2fc44..ce8d0f7e 100644
--- a/bauh/__init__.py
+++ b/bauh/__init__.py
@@ -3,4 +3,4 @@ __app_name__ = 'bauh'
import os
ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
-LOGS_PATH = '/tmp/{}/logs'.format(__app_name__)
\ No newline at end of file
+LOGS_PATH = '/tmp/{}/logs'.format(__app_name__)
diff --git a/bauh/api/abstract/context.py b/bauh/api/abstract/context.py
index d85a0e9e..fdb91ed4 100644
--- a/bauh/api/abstract/context.py
+++ b/bauh/api/abstract/context.py
@@ -5,6 +5,7 @@ from bauh.api.abstract.cache import MemoryCacheFactory
from bauh.api.abstract.disk import DiskCacheLoaderFactory
from bauh.api.abstract.download import FileDownloader
from bauh.api.http import HttpClient
+from bauh.commons.internet import InternetChecker
from bauh.view.util.translation import I18n
@@ -12,7 +13,8 @@ class ApplicationContext:
def __init__(self, download_icons: bool, http_client: HttpClient, app_root_dir: str, i18n: I18n,
cache_factory: MemoryCacheFactory, disk_loader_factory: DiskCacheLoaderFactory,
- logger: logging.Logger, file_downloader: FileDownloader, distro: str, app_name: str):
+ logger: logging.Logger, file_downloader: FileDownloader, distro: str, app_name: str,
+ internet_checker: InternetChecker):
"""
:param download_icons: if packages icons should be downloaded
:param http_client: a shared instance of http client
@@ -24,7 +26,7 @@ class ApplicationContext:
:param file_downloader
:param distro
:param app_name
- :param root_password
+ :param internet_checker
"""
self.download_icons = download_icons
self.http_client = http_client
@@ -40,9 +42,13 @@ class ApplicationContext:
'Graphics', 'Network', 'Office', 'Science', 'Settings', 'System', 'Utility')
self.app_name = app_name
self.root_password = None
+ self.internet_checker = internet_checker
def is_system_x86_64(self):
return self.arch_x86_64
def get_view_path(self):
return self.app_root_dir + '/view'
+
+ def is_internet_available(self) -> bool:
+ return self.internet_checker.is_available()
diff --git a/bauh/api/abstract/controller.py b/bauh/api/abstract/controller.py
index 933f4ef7..3a59c925 100644
--- a/bauh/api/abstract/controller.py
+++ b/bauh/api/abstract/controller.py
@@ -52,7 +52,7 @@ class UpgradeRequirement:
class UpgradeRequirements:
- def __init__(self, to_install: List[UpgradeRequirement], to_remove: List[UpgradeRequirement],
+ def __init__(self, to_install: Optional[List[UpgradeRequirement]], to_remove: Optional[List[UpgradeRequirement]],
to_upgrade: List[UpgradeRequirement], cannot_upgrade: List[UpgradeRequirement]):
"""
:param to_install: additional packages that must be installed with the upgrade
@@ -264,7 +264,7 @@ class SoftwareManager(ABC):
f.write(icon_bytes)
@abstractmethod
- def requires_root(self, action: str, pkg: SoftwarePackage):
+ def requires_root(self, action: str, pkg: Optional[SoftwarePackage]):
"""
if a given action requires root privileges to be executed. Current actions are: 'install', 'uninstall', 'downgrade', 'search', 'refresh', 'prepare'
:param action:
@@ -274,11 +274,12 @@ class SoftwareManager(ABC):
pass
@abstractmethod
- def prepare(self, task_manager: TaskManager, root_password: str, internet_available: bool):
+ def prepare(self, task_manager: Optional[TaskManager], root_password: Optional[str], internet_available: Optional[bool]):
"""
It prepares the manager to start working. It will be called by GUI. Do not call it within.
:param task_manager: a task manager instance used to register ongoing tasks during prepare
:param root_password
+ :param internet_available: if there is internet connection available
:return:
"""
pass
diff --git a/bauh/api/abstract/handler.py b/bauh/api/abstract/handler.py
index e87ed0da..310f458b 100644
--- a/bauh/api/abstract/handler.py
+++ b/bauh/api/abstract/handler.py
@@ -77,10 +77,10 @@ class ProcessWatcher:
:return: if the use requested to stop the process.
"""
- def request_root_password(self) -> Tuple[str, bool]:
+ def request_root_password(self) -> Tuple[bool, str]:
"""
asks the root password for the user
- :return: a tuple with the typed password and if it is valid
+ :return: a tuple informing if the password is valid and its text
"""
diff --git a/bauh/api/abstract/view.py b/bauh/api/abstract/view.py
index 0918e5e8..22b38dcc 100644
--- a/bauh/api/abstract/view.py
+++ b/bauh/api/abstract/view.py
@@ -3,7 +3,7 @@ from enum import Enum
from typing import List, Set, Optional
-class MessageType:
+class MessageType(Enum):
INFO = 0
WARNING = 1
ERROR = 2
@@ -19,7 +19,7 @@ class ViewComponent(ABC):
"""
Represents a GUI component
"""
- def __init__(self, id_: str, observers: List[ViewObserver] = None):
+ def __init__(self, id_: Optional[str], observers: Optional[List[ViewObserver]] = None):
self.id = id_
self.observers = observers if observers else []
@@ -56,12 +56,15 @@ class InputOption:
Represents a select component option.
"""
- def __init__(self, label: str, value: object, tooltip: str = None, icon_path: str = None, read_only: bool = False, id_: str = None):
+ def __init__(self, label: str, value: object, tooltip: Optional[str] = None,
+ icon_path: Optional[str] = None, read_only: bool = False, id_: Optional[str] = None,
+ invalid: bool = False):
"""
:param label: the string that will be shown to the user
:param value: the option value (not shown)
:param tooltip: an optional tooltip
:param icon_path: an optional icon path
+ :param invalid: if this option is considered invalid
"""
if not label:
raise Exception("'label' must be a not blank string")
@@ -72,6 +75,7 @@ class InputOption:
self.tooltip = tooltip
self.icon_path = icon_path
self.read_only = read_only
+ self.invalid = invalid
def __hash__(self):
return hash(self.label) + hash(self.value)
@@ -155,9 +159,10 @@ class TextInputType(Enum):
class TextInputComponent(ViewComponent):
- def __init__(self, label: str, value: str = '', placeholder: str = None, tooltip: str = None, read_only: bool =False,
- id_: str = None, only_int: bool = False, max_width: int = -1, type_: TextInputType = TextInputType.SINGLE_LINE,
- capitalize_label: bool = True, min_width: int = -1, min_height: int = -1):
+ def __init__(self, label: str, value: str = '', placeholder: Optional[str] = None, tooltip: Optional[str] = None,
+ read_only: bool = False, id_: Optional[str] = None, only_int: bool = False, max_width: int = -1,
+ type_: TextInputType = TextInputType.SINGLE_LINE, capitalize_label: bool = True, min_width: int = -1,
+ min_height: int = -1):
super(TextInputComponent, self).__init__(id_=id_)
self.label = label
self.value = value
@@ -216,9 +221,9 @@ class FormComponent(ViewComponent):
class FileChooserComponent(ViewComponent):
- def __init__(self, allowed_extensions: Set[str] = None, label: str = None, tooltip: str = None,
- file_path: str = None, max_width: int = -1, id_: str = None, search_path: str = None, capitalize_label: bool = True,
- directory: bool = False):
+ def __init__(self, allowed_extensions: Optional[Set[str]] = None, label: Optional[str] = None, tooltip: Optional[str] = None,
+ file_path: Optional[str] = None, max_width: int = -1, id_: Optional[str] = None,
+ search_path: Optional[str] = None, capitalize_label: bool = True, directory: bool = False):
super(FileChooserComponent, self).__init__(id_=id_)
self.label = label
self.allowed_extensions = allowed_extensions
@@ -229,7 +234,7 @@ class FileChooserComponent(ViewComponent):
self.capitalize_label = capitalize_label
self.directory = directory
- def set_file_path(self, fpath: str):
+ def set_file_path(self, fpath: Optional[str]):
self.file_path = fpath
if self.observers:
diff --git a/bauh/api/constants.py b/bauh/api/constants.py
index 3aff0922..be89411d 100644
--- a/bauh/api/constants.py
+++ b/bauh/api/constants.py
@@ -3,5 +3,6 @@ from pathlib import Path
CACHE_PATH = '{}/.cache/bauh'.format(str(Path.home()))
CONFIG_PATH = '{}/.config/bauh'.format(str(Path.home()))
+USER_THEMES_PATH = '{}/.local/share/bauh/themes'.format(str(Path.home()))
DESKTOP_ENTRIES_DIR = '{}/.local/share/applications'.format(str(Path.home()))
TEMP_DIR = '/tmp/bauh{}'.format('_root' if os.getuid() == 0 else '')
diff --git a/bauh/app.py b/bauh/app.py
index e7a16c71..abc33929 100755
--- a/bauh/app.py
+++ b/bauh/app.py
@@ -22,6 +22,9 @@ def main(tray: bool = False):
logger = logs.new_logger(__app_name__, bool(args.logs))
+ if args.offline:
+ logger.warning("offline mode activated")
+
app_config = config.read_config(update_file=True)
if bool(app_config['ui']['auto_scale']):
diff --git a/bauh/app_args.py b/bauh/app_args.py
index ca35e626..b6c1c386 100644
--- a/bauh/app_args.py
+++ b/bauh/app_args.py
@@ -8,6 +8,7 @@ def read() -> Namespace:
parser = argparse.ArgumentParser(prog=__app_name__, description="GUI for Linux software management")
parser.add_argument('-v', '--version', action='version', version='%(prog)s {}'.format(__version__))
parser.add_argument('--logs', action="store_true", help='It activates {} logs.'.format(__app_name__))
+ parser.add_argument('--offline', action="store_true", help='It assumes the internet connection is off')
exclusive_args = parser.add_mutually_exclusive_group()
exclusive_args.add_argument('--tray', action="store_true", help='If {} should be attached to the system tray.'.format(__app_name__))
diff --git a/bauh/cli/app.py b/bauh/cli/app.py
index 3f40ebf9..af4a4c4e 100644
--- a/bauh/cli/app.py
+++ b/bauh/cli/app.py
@@ -7,6 +7,7 @@ from bauh.api.abstract.context import ApplicationContext
from bauh.api.http import HttpClient
from bauh.cli import __app_name__, cli_args
from bauh.cli.controller import CLIManager
+from bauh.commons.internet import InternetChecker
from bauh.context import generate_i18n, DEFAULT_I18N_KEY
from bauh.view.core import config, gems
from bauh.view.core.controller import GenericSoftwareManager
@@ -42,7 +43,8 @@ def main():
distro=util.get_distro(),
file_downloader=AdaptableFileDownloader(logger, bool(app_config['download']['multithreaded']),
i18n, http_client, app_config['download']['multithreaded_client']),
- app_name=__app_name__)
+ app_name=__app_name__,
+ internet_checker=InternetChecker(offline=False))
managers = gems.load_managers(context=context, locale=i18n.current_key, config=app_config, default_locale=DEFAULT_I18N_KEY)
diff --git a/bauh/commons/internet.py b/bauh/commons/internet.py
index e860d831..1587c90e 100644
--- a/bauh/commons/internet.py
+++ b/bauh/commons/internet.py
@@ -1,12 +1,20 @@
import http.client as http_client
-def is_available() -> bool:
- conn = http_client.HTTPConnection("www.google.com", timeout=5)
- try:
- conn.request("HEAD", "/")
- conn.close()
- return True
- except:
- conn.close()
- return False
+class InternetChecker:
+
+ def __init__(self, offline: bool):
+ self.offline = offline
+
+ def is_available(self) -> bool:
+ if self.offline:
+ return False
+
+ conn = http_client.HTTPConnection("www.google.com", timeout=5)
+ try:
+ conn.request("HEAD", "/")
+ conn.close()
+ return True
+ except:
+ conn.close()
+ return False
diff --git a/bauh/context.py b/bauh/context.py
index d5c5c19b..5443c9a3 100644
--- a/bauh/context.py
+++ b/bauh/context.py
@@ -1,27 +1,39 @@
+import os
import sys
+from logging import Logger
from typing import Tuple
+from PyQt5.QtCore import QCoreApplication
from PyQt5.QtWidgets import QApplication
from bauh import __app_name__, __version__
+from bauh.stylesheet import process_theme, read_default_themes, read_user_themes, read_theme_metada
from bauh.view.util import util, translation
from bauh.view.util.translation import I18n
DEFAULT_I18N_KEY = 'en'
+PROPERTY_HARDCODED_STYLESHEET = 'hcqss'
-def new_qt_application(app_config: dict, quit_on_last_closed: bool = False, name: str = None) -> QApplication:
+def new_qt_application(app_config: dict, logger: Logger, quit_on_last_closed: bool = False, name: str = None) -> QApplication:
app = QApplication(sys.argv)
app.setQuitOnLastWindowClosed(quit_on_last_closed) # otherwise windows opened through the tray icon kill the application when closed
app.setApplicationName(name if name else __app_name__)
app.setApplicationVersion(__version__)
app.setWindowIcon(util.get_default_icon()[1])
- if app_config['ui']['style']:
- app.setStyle(str(app_config['ui']['style']))
+ if app_config['ui']['qt_style']:
+ app.setStyle(str(app_config['ui']['qt_style']))
else:
- if app.style().objectName().lower() not in {'fusion', 'breeze', 'oxygen'}:
- app.setStyle('Fusion')
+ app.setStyle('fusion')
+
+ app.setProperty('qt_style', app.style().objectName().lower())
+
+ theme_key = app_config['ui']['theme'].strip() if app_config['ui']['theme'] else None
+ set_theme(theme_key=theme_key, app=app, logger=logger)
+
+ if not app_config['ui']['system_theme']:
+ app.setPalette(app.style().standardPalette())
return app
@@ -44,3 +56,49 @@ def update_i18n(app_config, locale_dir: str, i18n: I18n) -> I18n:
i18n.default.update(def_dict)
return i18n
+
+
+def set_theme(theme_key: str, app: QCoreApplication, logger: Logger):
+ if not theme_key:
+ logger.warning("config: no theme defined")
+ else:
+ available_themes = {}
+ default_themes = read_default_themes()
+ available_themes.update(default_themes)
+
+ theme_file = None
+
+ if '/' in theme_key:
+ if os.path.isfile(theme_key):
+ user_sheets = read_user_themes()
+
+ if user_sheets:
+ available_themes.update(user_sheets)
+
+ if theme_key in user_sheets:
+ theme_file = theme_key
+ else:
+ theme_file = default_themes.get(theme_key)
+
+ if theme_file:
+ with open(theme_file) as f:
+ theme_str = f.read()
+
+ if not theme_str:
+ logger.warning("theme file '{}' has no content".format(theme_file))
+ else:
+ base_metadata = read_theme_metada(key=theme_key, file_path=theme_file)
+
+ if base_metadata.abstract:
+ logger.warning("theme file '{}' is abstract (abstract = true) and cannot be loaded".format(theme_file))
+ else:
+ processed = process_theme(file_path=theme_file,
+ metadata=base_metadata,
+ theme_str=theme_str,
+ available_themes=available_themes)
+
+ if processed:
+ app.setStyleSheet(processed[0])
+ logger.info("theme file '{}' loaded".format(theme_file))
+ else:
+ logger.warning("theme file '{}' could not be interpreted and processed".format(theme_file))
diff --git a/bauh/gems/appimage/controller.py b/bauh/gems/appimage/controller.py
index e08882c9..f05adf91 100644
--- a/bauh/gems/appimage/controller.py
+++ b/bauh/gems/appimage/controller.py
@@ -590,7 +590,8 @@ class AppImageManager(SoftwareManager):
updater = DatabaseUpdater(task_man=task_manager,
i18n=self.context.i18n,
http_client=self.context.http_client, logger=self.context.logger,
- db_locks=self.db_locks, interval=interval)
+ db_locks=self.db_locks, interval=interval,
+ internet_checker=self.context.internet_checker)
if local_config['db_updater']['enabled']:
updater.start()
elif internet_available:
diff --git a/bauh/gems/appimage/resources/img/upgrade.svg b/bauh/gems/appimage/resources/img/upgrade.svg
old mode 100644
new mode 100755
index b4f5a482..8915f8dd
--- a/bauh/gems/appimage/resources/img/upgrade.svg
+++ b/bauh/gems/appimage/resources/img/upgrade.svg
@@ -8,13 +8,13 @@
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
- viewBox="0 0 47.810421 48"
+ viewBox="0 0 512 512"
enable-background="new 0 0 26 26"
id="svg8"
sodipodi:docname="upgrade.svg"
- inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)"
- width="47.810421"
- height="48">
+ inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)"
+ width="512"
+ height="512">
@@ -42,10 +42,10 @@
inkscape:window-height="739"
id="namedview10"
showgrid="false"
- showborder="false"
- inkscape:zoom="3.2091769"
- inkscape:cx="-13.285588"
- inkscape:cy="-0.030460959"
+ showborder="true"
+ inkscape:zoom="0.80229424"
+ inkscape:cx="65.887412"
+ inkscape:cy="257.44157"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
@@ -53,24 +53,21 @@
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
- fit-margin-bottom="0" />
+ fit-margin-bottom="0"
+ inkscape:document-rotation="0" />
+ transform="matrix(19.334486,0,0,20.945696,4.651682,-16.294048)"
+ style="fill:#59a869;fill-opacity:1;stroke:none;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none">
+ style="fill:#59a869;fill-opacity:1;stroke:none;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none" />
+ style="fill:#59a869;fill-opacity:1;stroke:none;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none" />
-
diff --git a/bauh/gems/appimage/worker.py b/bauh/gems/appimage/worker.py
index 5edbd9dd..16e640b0 100644
--- a/bauh/gems/appimage/worker.py
+++ b/bauh/gems/appimage/worker.py
@@ -12,7 +12,7 @@ import requests
from bauh.api.abstract.handler import TaskManager, ProcessWatcher
from bauh.api.http import HttpClient
-from bauh.commons import internet
+from bauh.commons.internet import InternetChecker
from bauh.gems.appimage import LOCAL_PATH, get_icon_path, INSTALLATION_PATH, SYMLINKS_DIR, util
from bauh.gems.appimage.model import AppImage
from bauh.view.util.translation import I18n
@@ -22,7 +22,9 @@ class DatabaseUpdater(Thread):
URL_DB = 'https://raw.githubusercontent.com/vinifmor/bauh-files/master/appimage/dbs.tar.gz'
COMPRESS_FILE_PATH = LOCAL_PATH + '/db.tar.gz'
- def __init__(self, task_man: TaskManager, i18n: I18n, http_client: HttpClient, logger: logging.Logger, db_locks: dict, interval: int):
+ def __init__(self, task_man: TaskManager, i18n: I18n, http_client: HttpClient,
+ logger: logging.Logger, db_locks: dict, interval: int,
+ internet_checker: InternetChecker):
super(DatabaseUpdater, self).__init__(daemon=True)
self.http_client = http_client
self.logger = logger
@@ -31,6 +33,7 @@ class DatabaseUpdater(Thread):
self.i18n = i18n
self.task_man = task_man
self.task_id = 'appim_db'
+ self.internet_checker = internet_checker
def _finish_task(self):
if self.task_man:
@@ -44,7 +47,7 @@ class DatabaseUpdater(Thread):
self.task_man.update_progress(self.task_id, 10, None)
try:
- if not internet.is_available():
+ if not self.internet_checker.is_available():
self._finish_task()
return
except requests.exceptions.ConnectionError:
diff --git a/bauh/gems/arch/controller.py b/bauh/gems/arch/controller.py
index 56f9d042..2a06cd94 100644
--- a/bauh/gems/arch/controller.py
+++ b/bauh/gems/arch/controller.py
@@ -25,7 +25,7 @@ from bauh.api.abstract.view import MessageType, FormComponent, InputOption, Sing
ViewComponent, PanelComponent, MultipleSelectComponent, TextInputComponent, TextInputType, \
FileChooserComponent, TextComponent
from bauh.api.constants import TEMP_DIR
-from bauh.commons import user, internet, system
+from bauh.commons import user, system
from bauh.commons.category import CategoriesDownloader
from bauh.commons.config import save_config
from bauh.commons.html import bold
@@ -1175,7 +1175,7 @@ class ArchManager(SoftwareManager):
def _uninstall(self, context: TransactionContext, names: Set[str], remove_unneeded: bool = False, disk_loader: Optional[DiskCacheLoader] = None, skip_requirements: bool = False):
self._update_progress(context, 10)
- net_available = internet.is_available() if disk_loader else True
+ net_available = self.context.internet_checker.is_available() if disk_loader else True
hard_requirements = set()
@@ -2830,7 +2830,8 @@ class ArchManager(SoftwareManager):
def upgrade_system(self, root_password: str, watcher: ProcessWatcher) -> bool:
# repo_map = pacman.map_repositories()
- installed = self.read_installed(limit=-1, only_apps=False, pkg_types=None, internet_available=internet.is_available(), disk_loader=None).installed
+ net_available = self.context.internet_checker.is_available()
+ installed = self.read_installed(limit=-1, only_apps=False, pkg_types=None, internet_available=net_available, disk_loader=None).installed
if not installed:
watcher.show_message(title=self.i18n['arch.custom_action.upgrade_system'],
@@ -3077,7 +3078,7 @@ class ArchManager(SoftwareManager):
confirmation_label=self.i18n['proceed'].capitalize(),
deny_label=self.i18n['cancel'].capitalize()):
- pwd, valid_pwd = watcher.request_root_password()
+ valid_pwd, pwd = watcher.request_root_password()
if valid_pwd:
handler = ProcessHandler(watcher)
diff --git a/bauh/gems/arch/resources/img/mark_pkgbuild.svg b/bauh/gems/arch/resources/img/mark_pkgbuild.svg
index d9ab0b45..cbb28b43 100644
--- a/bauh/gems/arch/resources/img/mark_pkgbuild.svg
+++ b/bauh/gems/arch/resources/img/mark_pkgbuild.svg
@@ -7,12 +7,12 @@
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
- inkscape:version="1.0 (4035a4fb49, 2020-05-01)"
- height="23.999998"
- width="24.000002"
- sodipodi:docname="edit_pkgbuild.svg"
+ inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)"
+ height="512"
+ width="512"
+ sodipodi:docname="mark_pkgbuild.svg"
xml:space="preserve"
- viewBox="0 0 24.000003 23.999999"
+ viewBox="0 0 512.00002 512.00002"
y="0px"
x="0px"
id="Capa_1"
@@ -25,9 +25,9 @@
inkscape:window-maximized="1"
inkscape:window-y="0"
inkscape:window-x="0"
- inkscape:cy="-9.5853392"
- inkscape:cx="75.093443"
- inkscape:zoom="3.2847783"
+ inkscape:cy="176.38591"
+ inkscape:cx="96.230087"
+ inkscape:zoom="0.58067225"
fit-margin-bottom="0"
fit-margin-right="0"
fit-margin-left="0"
@@ -43,17 +43,18 @@
objecttolerance="10"
borderopacity="1"
bordercolor="#666666"
- pagecolor="#ffffff" />
+ pagecolor="#ffffff"
+ inkscape:document-rotation="0" />
diff --git a/bauh/gems/arch/resources/img/unmark_pkgbuild.svg b/bauh/gems/arch/resources/img/unmark_pkgbuild.svg
index 43ced941..33ff9464 100644
--- a/bauh/gems/arch/resources/img/unmark_pkgbuild.svg
+++ b/bauh/gems/arch/resources/img/unmark_pkgbuild.svg
@@ -11,16 +11,19 @@
id="Capa_1"
x="0px"
y="0px"
- viewBox="0 0 24.000003 23.999999"
+ viewBox="0 0 512.00002 512.00002"
xml:space="preserve"
sodipodi:docname="unmark_pkgbuild.svg"
- width="24.000002"
- height="23.999998"
- inkscape:version="1.0 (4035a4fb49, 2020-05-01)">image/svg+xml
+
+
+
-
-
-
-
+
@@ -119,11 +110,13 @@
transform="translate(-0.757)">
+ id="g13014">
diff --git a/bauh/gems/arch/resources/locale/en b/bauh/gems/arch/resources/locale/en
index e8a940b6..dbe4c5da 100644
--- a/bauh/gems/arch/resources/locale/en
+++ b/bauh/gems/arch/resources/locale/en
@@ -258,4 +258,4 @@ gem.arch.info=Software packages available for distributions based on Arch Linux
gem.arch.label=Arch
gem.arch.type.arch_repo.label=Arch - Repository
gem.arch.type.aur.label=Arch - AUR
-gem.aur.install.warning=AUR packages are maintained by an independent user community. There is no warranty that they will work or not harm your system.
\ No newline at end of file
+gem.aur.install.warning=AUR packages are maintained by an independent user community. There is no warranty they will work or not harm your system.
\ No newline at end of file
diff --git a/bauh/gems/flatpak/controller.py b/bauh/gems/flatpak/controller.py
index fc03d487..ff9ce5c4 100644
--- a/bauh/gems/flatpak/controller.py
+++ b/bauh/gems/flatpak/controller.py
@@ -15,7 +15,7 @@ from bauh.api.abstract.model import PackageHistory, PackageUpdate, SoftwarePacka
SuggestionPriority, PackageStatus
from bauh.api.abstract.view import MessageType, FormComponent, SingleSelectComponent, InputOption, SelectViewType, \
ViewComponent, PanelComponent
-from bauh.commons import user, internet
+from bauh.commons import user
from bauh.commons.config import save_config
from bauh.commons.html import strip_html, bold
from bauh.commons.system import ProcessHandler
@@ -364,7 +364,7 @@ class FlatpakManager(SoftwareManager):
if user.is_root():
handler.handle_simple(flatpak.set_default_remotes('system'))
else:
- user_password, valid = watcher.request_root_password()
+ valid, user_password = watcher.request_root_password()
if not valid:
watcher.print('Operation aborted')
return TransactionResult(success=False, installed=[], removed=[])
@@ -424,7 +424,7 @@ class FlatpakManager(SoftwareManager):
if current_installed_by_level and (not installed_by_level or len(current_installed_by_level) > len(installed_by_level) + 1):
pkg_key = '{}:{}:{}'.format(pkg.id, pkg.name, pkg.branch)
- net_available = internet.is_available()
+ net_available = self.context.is_internet_available()
for p in current_installed_by_level:
current_key = '{}:{}:{}'.format(p['id'], p['name'], p['branch'])
if current_key != pkg_key and (not installed_by_level or current_key not in installed_by_level):
diff --git a/bauh/gems/snap/controller.py b/bauh/gems/snap/controller.py
index 397f709c..b641d417 100644
--- a/bauh/gems/snap/controller.py
+++ b/bauh/gems/snap/controller.py
@@ -13,14 +13,14 @@ from bauh.api.abstract.model import SoftwarePackage, PackageHistory, PackageUpda
from bauh.api.abstract.view import SingleSelectComponent, SelectViewType, InputOption, ViewComponent, PanelComponent, \
FormComponent
from bauh.api.exception import NoInternetException
-from bauh.commons import resource, internet
+from bauh.commons import resource
from bauh.commons.category import CategoriesDownloader
from bauh.commons.config import save_config
from bauh.commons.html import bold
from bauh.commons.system import SystemProcess, ProcessHandler, new_root_subprocess, get_human_size_str
from bauh.commons.view_utils import new_select
from bauh.gems.snap import snap, URL_CATEGORIES_FILE, SNAP_CACHE_PATH, CATEGORIES_FILE_PATH, SUGGESTIONS_FILE, \
- get_icon_path, snapd, CONFIG_FILE
+ get_icon_path, snapd, CONFIG_FILE, ROOT_DIR
from bauh.gems.snap.config import read_config
from bauh.gems.snap.model import SnapApplication
from bauh.gems.snap.snapd import SnapdClient
@@ -45,14 +45,14 @@ class SnapManager(SoftwareManager):
self.custom_actions = [
CustomSoftwareAction(i18n_status_key='snap.action.refresh.status',
i18n_label_key='snap.action.refresh.label',
- icon_path=resource.get_path('img/refresh.svg', context.get_view_path()),
+ icon_path=resource.get_path('img/refresh.svg', ROOT_DIR),
manager_method='refresh',
requires_root=True,
i18n_confirm_key='snap.action.refresh.confirm'),
CustomSoftwareAction(i18n_status_key='snap.action.channel.status',
i18n_label_key='snap.action.channel.label',
i18n_confirm_key='snap.action.channel.confirm',
- icon_path=resource.get_path('img/refresh.svg', context.get_view_path()),
+ icon_path=resource.get_path('img/refresh.svg', ROOT_DIR),
manager_method='change_channel',
requires_root=True)
]
@@ -230,7 +230,8 @@ class SnapManager(SoftwareManager):
if success:
new_installed = []
try:
- current_installed = self.read_installed(disk_loader=disk_loader, internet_available=internet.is_available()).installed
+ net_available = self.context.internet_checker.is_available()
+ current_installed = self.read_installed(disk_loader=disk_loader, internet_available=net_available).installed
except:
new_installed = [pkg]
traceback.print_exc()
@@ -261,7 +262,7 @@ class SnapManager(SoftwareManager):
return ProcessHandler(watcher).handle_simple(snap.refresh_and_stream(pkg.name, root_password))[0]
def change_channel(self, pkg: SnapApplication, root_password: str, watcher: ProcessWatcher) -> bool:
- if not internet.is_available():
+ if not self.context.internet_checker.is_available():
raise NoInternetException()
try:
diff --git a/bauh/gems/snap/resources/img/refresh.svg b/bauh/gems/snap/resources/img/refresh.svg
new file mode 100755
index 00000000..60e9d79d
--- /dev/null
+++ b/bauh/gems/snap/resources/img/refresh.svg
@@ -0,0 +1,128 @@
+
+image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/manage.py b/bauh/manage.py
index c1cb902f..2e6a8f37 100644
--- a/bauh/manage.py
+++ b/bauh/manage.py
@@ -7,6 +7,7 @@ from PyQt5.QtWidgets import QApplication, QWidget
from bauh import ROOT_DIR, __app_name__
from bauh.api.abstract.context import ApplicationContext
from bauh.api.http import HttpClient
+from bauh.commons.internet import InternetChecker
from bauh.context import generate_i18n, DEFAULT_I18N_KEY, new_qt_application
from bauh.view.core import gems
from bauh.view.core.controller import GenericSoftwareManager
@@ -39,7 +40,8 @@ def new_manage_panel(app_args: Namespace, app_config: dict, logger: logging.Logg
distro=util.get_distro(),
file_downloader=AdaptableFileDownloader(logger, bool(app_config['download']['multithreaded']),
i18n, http_client, app_config['download']['multithreaded_client']),
- app_name=__app_name__)
+ app_name=__app_name__,
+ internet_checker=InternetChecker(offline=app_args.offline))
managers = gems.load_managers(context=context, locale=i18n.current_key, config=app_config, default_locale=DEFAULT_I18N_KEY)
@@ -49,7 +51,7 @@ def new_manage_panel(app_args: Namespace, app_config: dict, logger: logging.Logg
manager = GenericSoftwareManager(managers, context=context, config=app_config)
- app = new_qt_application(app_config, quit_on_last_closed=True)
+ app = new_qt_application(app_config=app_config, logger=logger, quit_on_last_closed=True)
if app_args.settings: # only settings window
manager.cache_available_managers()
diff --git a/bauh/stylesheet.py b/bauh/stylesheet.py
new file mode 100644
index 00000000..681a99d6
--- /dev/null
+++ b/bauh/stylesheet.py
@@ -0,0 +1,259 @@
+import glob
+import os
+import re
+import traceback
+from typing import Optional, Dict, Tuple, Set
+
+from PyQt5.QtWidgets import QApplication
+
+from bauh.api.constants import USER_THEMES_PATH
+from bauh.view.util import resource
+from bauh.view.util.translation import I18n
+
+RE_WIDTH_PERCENT = re.compile(r'[\d\\.]+%w')
+RE_HEIGHT_PERCENT = re.compile(r'[\d\\.]+%h')
+RE_META_I18N_FIELDS = re.compile(r'((name|description)(\[\w+])?)')
+RE_VAR_PATTERN = re.compile(r'^@[\w.\-_]+')
+RE_QSS_EXT = re.compile(r'\.qss$')
+
+
+class ThemeMetadata:
+
+ def __init__(self, file_path: str, default: bool, default_name: Optional[str] = None,
+ default_description: Optional[str] = None, version: Optional[str] = None,
+ root_sheet: Optional[str] = None, abstract: bool = False):
+ self.names = {}
+ self.default_name = default_name
+ self.descriptions = {}
+ self.default_description = default_description
+ self.root_sheet = root_sheet
+ self.version = version
+ self.file_path = file_path
+ self.file_dir = '/'.join(file_path.split('/')[0:-1])
+ self.default = default
+ self.key = self.file_path.split('/')[-1].split('.')[0] if self.default else self.file_path
+ self.abstract = abstract
+
+ def __eq__(self, other) -> bool:
+ if isinstance(other, ThemeMetadata):
+ return self.file_path == other.file_path
+
+ return False
+
+ def __hash__(self):
+ return self.file_path.__hash__()
+
+ def __repr__(self):
+ return self.file_path if self.file_path else ''
+
+ def get_i18n_name(self, i18n: I18n) -> str:
+ if self.names:
+ name = self.names.get(i18n.current_key, self.names.get(i18n.default_key))
+
+ if name:
+ return name
+
+ if self.default_name:
+ return self.default_name
+ else:
+ return self.file_path.split('/')[-1]
+
+ def get_i18n_description(self, i18n: I18n) -> Optional[str]:
+ if self.descriptions:
+ des = self.descriptions.get(i18n.current_key, self.descriptions.get(i18n.default_key))
+
+ if des:
+ return des
+
+ return self.default_description
+
+
+def read_theme_metada(key: str, file_path: str) -> ThemeMetadata:
+ meta_file = RE_QSS_EXT.sub('.meta', file_path)
+ meta_obj = ThemeMetadata(file_path=file_path, default_name=key, default=not key.startswith('/'))
+
+ if os.path.exists(meta_file):
+ meta_dict = {}
+ with open(meta_file) as f:
+ for line in f.readlines():
+ if line:
+ field_split = line.split('=')
+
+ if len(field_split) > 1:
+ meta_dict[field_split[0].strip()] = field_split[1].strip()
+
+ if meta_dict:
+ for field, val in meta_dict.items():
+ if field == 'version':
+ meta_obj.version = val
+ elif field == 'root_sheet':
+ meta_obj.root_sheet = val
+ elif field == 'name':
+ meta_obj.default_name = val
+ elif field == 'description':
+ meta_obj.default_description = val
+ elif field == 'abstract':
+ boolean = val.lower()
+
+ if boolean == 'true':
+ meta_obj.abstract = True
+ elif boolean == 'false':
+ meta_obj.abstract = False
+
+ else:
+ i18n_field = RE_META_I18N_FIELDS.findall(field)
+
+ if i18n_field:
+ if i18n_field[0][1] == 'name':
+ meta_obj.names[i18n_field[0][2][1:-1]] = val
+ else:
+ meta_obj.descriptions[i18n_field[0][2][1:-1]] = val
+
+ return meta_obj
+
+
+def read_default_themes() -> Dict[str, str]:
+ return {f.split('/')[-1].split('.')[0].lower(): f for f in glob.glob(resource.get_path('style/**/*.qss'))}
+
+
+def read_user_themes() -> Dict[str, str]:
+ return {f: f for f in glob.glob('{}/**/*.qss'.format(USER_THEMES_PATH))}
+
+
+def read_all_themes_metadata() -> Set[ThemeMetadata]:
+ themes = set()
+
+ for key, file_path in read_default_themes().items():
+ themes.add(read_theme_metada(key=key, file_path=file_path))
+
+ for key, file_path in read_user_themes().items():
+ themes.add(read_theme_metada(key=key, file_path=file_path))
+
+ return themes
+
+
+def process_theme(file_path: str, theme_str: str, metadata: ThemeMetadata,
+ available_themes: Optional[Dict[str, str]]) -> Optional[Tuple[str, ThemeMetadata]]:
+ if theme_str and metadata:
+ root_theme = None
+ if metadata.root_sheet and metadata.root_sheet in available_themes:
+ root_file = available_themes[metadata.root_sheet]
+
+ if os.path.isfile(root_file):
+ with open(root_file) as f:
+ root_theme_str = f.read()
+
+ if root_theme_str:
+ root_metadata = read_theme_metada(key=metadata.root_sheet, file_path=root_file)
+ root_theme = process_theme(file_path=root_file,
+ theme_str=root_theme_str,
+ metadata=root_metadata,
+ available_themes=available_themes)
+
+ var_map = _read_var_file(file_path)
+ var_map['images'] = resource.get_path('img')
+ var_map['style_dir'] = metadata.file_dir
+
+ if var_map:
+ var_list = [*var_map.keys()]
+ var_list.sort(key=_by_str_len, reverse=True)
+
+ for var in var_list:
+ theme_str = theme_str.replace('@' + var, var_map[var])
+
+ screen_size = QApplication.primaryScreen().size()
+ theme_str = process_width_percent_measures(theme_str, screen_size.width())
+ theme_str = process_height_percent_measures(theme_str, screen_size.height())
+
+ return theme_str if not root_theme else '{}\n{}'.format(root_theme[0], theme_str), metadata
+
+
+def _by_str_len(string: str) -> int:
+ return len(string)
+
+
+def process_width_percent_measures(theme: str, screen_width: int) -> str:
+ width_measures = RE_WIDTH_PERCENT.findall(theme)
+
+ final_theme = theme
+ if width_measures:
+ for m in width_measures:
+ try:
+ percent = float(m.split('%')[0])
+ final_theme = final_theme.replace(m, '{}px'.format(round(screen_width * percent)))
+ except ValueError:
+ traceback.print_exc()
+
+ return final_theme
+
+
+def process_height_percent_measures(theme: str, screen_height: int) -> str:
+ width_measures = RE_HEIGHT_PERCENT.findall(theme)
+
+ final_sheet = theme
+ if width_measures:
+ for m in width_measures:
+ try:
+ percent = float(m.split('%')[0])
+ final_sheet = final_sheet.replace(m, '{}px'.format(round(screen_height * percent)))
+ except ValueError:
+ traceback.print_exc()
+
+ return final_sheet
+
+
+def _read_var_file(theme_file: str) -> dict:
+ vars_file = theme_file.replace('.qss', '.vars')
+ var_map = {}
+
+ if os.path.isfile(vars_file):
+ with open(vars_file) as f:
+ for line in f.readlines():
+ if line:
+ line_strip = line.strip()
+ if line_strip:
+ var_value = line_strip.split('=')
+
+ if var_value and len(var_value) == 2:
+ var, value = var_value[0].strip(), var_value[1].strip()
+
+ if var and value:
+ var_map[var] = value
+
+ if var_map:
+ process_var_of_vars(var_map) # mapping keys that point to others
+
+ return var_map
+
+
+def process_var_of_vars(var_map: dict):
+ while True:
+ pending_vars, invalid = {}, set()
+
+ for k, v in var_map.items():
+ var_match = RE_VAR_PATTERN.match(v)
+
+ if var_match:
+ var_name = var_match.group()[1:]
+ if var_name not in var_map or var_name == k:
+ invalid.add(k)
+ else:
+ pending_vars[k] = var_name
+
+ for key in invalid:
+ del var_map[key]
+
+ if not pending_vars:
+ break
+
+ resolved = 0
+
+ for key, val in pending_vars.items():
+ real_val = var_map[val]
+
+ if not RE_VAR_PATTERN.match(real_val):
+ var_map[key] = real_val
+ resolved += 1
+
+ if resolved == len(pending_vars):
+ break
diff --git a/bauh/tray.py b/bauh/tray.py
index c1910b41..dc579652 100644
--- a/bauh/tray.py
+++ b/bauh/tray.py
@@ -1,14 +1,15 @@
-import logging
+from logging import Logger
from typing import Tuple
-from PyQt5.QtWidgets import QApplication, QWidget
+from PyQt5.QtCore import QObject
+from PyQt5.QtWidgets import QApplication
from bauh.context import new_qt_application
from bauh.view.qt.systray import TrayIcon
-def new_tray_icon(app_config: dict, logger: logging.Logger) -> Tuple[QApplication, QWidget]:
- app = new_qt_application(app_config, quit_on_last_closed=True)
+def new_tray_icon(app_config: dict, logger: Logger) -> Tuple[QApplication, QObject]:
+ app = new_qt_application(app_config=app_config, logger=logger, quit_on_last_closed=True)
tray_icon = TrayIcon(screen_size=app.primaryScreen().size(), config=app_config, logger=logger)
tray_icon.show()
diff --git a/bauh/view/core/config.py b/bauh/view/core/config.py
index 5ffa28b9..a93a8fce 100644
--- a/bauh/view/core/config.py
+++ b/bauh/view/core/config.py
@@ -37,10 +37,12 @@ def read_config(update_file: bool = False) -> dict:
'default_icon': None,
'updates_icon': None
},
- 'style': None,
+ 'qt_style': 'fusion',
'hdpi': True,
"auto_scale": False,
- "scale_factor": 1.0
+ "scale_factor": 1.0,
+ 'theme': 'light',
+ 'system_theme': False
},
'download': {
diff --git a/bauh/view/core/controller.py b/bauh/view/core/controller.py
index 5affb4db..5bb0a70d 100755
--- a/bauh/view/core/controller.py
+++ b/bauh/view/core/controller.py
@@ -1,4 +1,4 @@
-import operator
+import re
import re
import time
import traceback
@@ -14,7 +14,6 @@ from bauh.api.abstract.model import SoftwarePackage, PackageUpdate, PackageHisto
CustomSoftwareAction
from bauh.api.abstract.view import ViewComponent, TabGroupComponent, MessageType
from bauh.api.exception import NoInternetException
-from bauh.commons import internet
from bauh.commons.html import bold
from bauh.commons.system import run_cmd
from bauh.view.core.config import read_config
@@ -158,7 +157,7 @@ class GenericSoftwareManager(SoftwareManager):
res = SearchResult([], [], 0)
- if internet.is_available():
+ if self.context.is_internet_available():
norm_word = word.strip().lower()
url_words = RE_IS_URL.match(norm_word)
@@ -211,7 +210,7 @@ class GenericSoftwareManager(SoftwareManager):
disk_loader = None
- net_available = internet.is_available()
+ net_available = self.context.is_internet_available()
if not pkg_types: # any type
for man in self.managers:
if self._can_work(man):
@@ -402,7 +401,7 @@ class GenericSoftwareManager(SoftwareManager):
def prepare(self, task_manager: TaskManager, root_password: str, internet_available: bool):
if self.managers:
- internet_on = internet.is_available()
+ internet_on = self.context.is_internet_available()
taskman = task_manager if task_manager else TaskManager() # empty task manager to prevent null pointers
for man in self.managers:
if man not in self._already_prepared and self._can_work(man):
@@ -420,7 +419,7 @@ class GenericSoftwareManager(SoftwareManager):
updates = []
if self.managers:
- net_available = internet.is_available()
+ net_available = self.context.is_internet_available()
for man in self.managers:
if self._can_work(man):
@@ -433,7 +432,7 @@ class GenericSoftwareManager(SoftwareManager):
def list_warnings(self, internet_available: bool = None) -> List[str]:
warnings = []
- int_available = internet.is_available()
+ int_available = self.context.is_internet_available()
if int_available:
updates_msg = check_for_update(self.logger, self.http_client, self.i18n)
@@ -469,7 +468,7 @@ class GenericSoftwareManager(SoftwareManager):
def list_suggestions(self, limit: int, filter_installed: bool) -> List[PackageSuggestion]:
if bool(self.config['suggestions']['enabled']):
- if self.managers and internet.is_available():
+ if self.managers and self.context.is_internet_available():
suggestions, threads = [], []
for man in self.managers:
t = Thread(target=self._fill_suggestions, args=(suggestions, man, int(self.config['suggestions']['by_type']), filter_installed))
diff --git a/bauh/view/core/settings.py b/bauh/view/core/settings.py
index ef920d6a..24558c45 100644
--- a/bauh/view/core/settings.py
+++ b/bauh/view/core/settings.py
@@ -2,11 +2,11 @@ import logging
import os
import traceback
from math import floor
-from typing import List, Tuple
+from typing import List, Tuple, Optional
from PyQt5.QtWidgets import QApplication, QStyleFactory
-from bauh import ROOT_DIR
+from bauh import ROOT_DIR, __app_name__
from bauh.api.abstract.controller import SoftwareManager
from bauh.api.abstract.download import FileDownloader
from bauh.api.abstract.view import ViewComponent, TabComponent, InputOption, TextComponent, MultipleSelectComponent, \
@@ -102,13 +102,13 @@ class GenericSettingsManager:
id_="icon_exp")
select_trim_up = 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)],
- id_='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)],
+ id_='trim_after_upgrade')
select_dep_check = self._gen_bool_component(label=self.i18n['core.config.system.dep_checking'],
tooltip=self.i18n['core.config.system.dep_checking.tip'],
@@ -178,7 +178,7 @@ class GenericSettingsManager:
return TabComponent(self.i18n['core.config.tab.tray'].capitalize(), PanelComponent(sub_comps), None, 'core.tray')
def _gen_ui_settings(self, core_config: dict, screen_width: int, screen_height: int) -> TabComponent:
- default_width = floor(0.11 * screen_width)
+ default_width = floor(0.15 * screen_width)
select_hdpi = self._gen_bool_component(label=self.i18n['core.config.ui.hdpi'],
tooltip=self.i18n['core.config.ui.hdpi.tip'],
@@ -205,9 +205,9 @@ class GenericSettingsManager:
min_value=100, max_value=400, step_value=5, value=scale * 100,
max_width=default_width)
- cur_style = QApplication.instance().style().objectName().lower() if not core_config['ui']['style'] else core_config['ui']['style']
- style_opts = [InputOption(label=self.i18n['core.config.ui.style.default'].capitalize(), value=None)]
- style_opts.extend([InputOption(label=s.capitalize(), value=s.lower()) for s in QStyleFactory.keys()])
+ cur_style = QApplication.instance().property('qt_style') if not core_config['ui']['qt_style'] else core_config['ui']['qt_style']
+
+ style_opts = [InputOption(label=s.capitalize(), value=s.lower()) for s in QStyleFactory.keys()]
default_style = [o for o in style_opts if o.value == cur_style]
@@ -221,14 +221,21 @@ class GenericSettingsManager:
default_style = default_style[0]
select_style = SingleSelectComponent(label=self.i18n['style'].capitalize(),
+ tooltip=self.i18n['core.config.ui.qt_style.tooltip'],
options=style_opts,
default_option=default_style,
type_=SelectViewType.COMBO,
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(),
+ select_system_theme = self._gen_bool_component(label=self.i18n['core.config.ui.system_theme'],
+ tooltip=self.i18n['core.config.ui.system_theme.tip'].format(app=__app_name__),
+ 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=default_width,
@@ -240,11 +247,14 @@ class GenericSettingsManager:
max_width=default_width,
value=core_config['download']['icons'])
- sub_comps = [FormComponent([select_hdpi, select_ascale, select_scale, select_dicons, select_style, input_maxd], spaces=False)]
+ 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')
def _gen_general_settings(self, core_config: dict, screen_width: int, screen_height: int) -> TabComponent:
- default_width = floor(0.11 * screen_width)
+ default_width = floor(0.15 * screen_width)
locale_opts = [InputOption(label=self.i18n['locale.{}'.format(k)].capitalize(), value=k) for k in translation.get_available_keys()]
@@ -295,11 +305,12 @@ class GenericSettingsManager:
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,
- value=bool(core_config['updates']['ask_for_reboot']),
- opts=[(self.i18n['ask'].capitalize(), True, None), (self.i18n['no'].capitalize(), False, None)])
+ tip=self.i18n['core.config.updates.reboot.tip'],
+ id_='ask_for_reboot',
+ max_width=default_width,
+ value=bool(core_config['updates']['ask_for_reboot']),
+ opts=[(self.i18n['ask'].capitalize(), True, None),
+ (self.i18n['no'].capitalize(), False, None)])
sub_comps = [FormComponent([select_locale, select_store_pwd, select_sysnotify, select_sugs, inp_sugs, inp_reboot], spaces=False)]
return TabComponent(self.i18n['core.config.tab.general'].capitalize(), PanelComponent(sub_comps), None, 'core.gen')
@@ -322,7 +333,7 @@ class GenericSettingsManager:
backup: PanelComponent,
ui: PanelComponent,
tray: PanelComponent,
- gems_panel: PanelComponent) -> Tuple[bool, List[str]]:
+ gems_panel: PanelComponent) -> Tuple[bool, Optional[List[str]]]:
core_config = config.read_config()
# general
@@ -407,9 +418,12 @@ class GenericSettingsManager:
style = ui_form.get_component('style').get_selected()
- cur_style = core_config['ui']['style'] if core_config['ui']['style'] else QApplication.instance().style().objectName().lower()
+ cur_style = core_config['ui']['qt_style'] if core_config['ui']['qt_style'] else QApplication.instance().property('qt_style')
if style != cur_style:
- core_config['ui']['style'] = style
+ core_config['ui']['qt_style'] = style
+ QApplication.instance().setProperty('qt_style', style)
+
+ core_config['ui']['system_theme'] = ui_form.get_component('system_theme').get_selected()
# gems
checked_gems = gems_panel.components[1].get_component('gems').get_selected_values()
diff --git a/bauh/view/qt/about.py b/bauh/view/qt/about.py
index 3e7d8cfb..c75f4297 100644
--- a/bauh/view/qt/about.py
+++ b/bauh/view/qt/about.py
@@ -1,8 +1,7 @@
from glob import glob
-from PyQt5.QtCore import Qt, QSize
from PyQt5.QtGui import QIcon
-from PyQt5.QtWidgets import QVBoxLayout, QDialog, QLabel, QWidget, QHBoxLayout
+from PyQt5.QtWidgets import QVBoxLayout, QDialog, QLabel, QWidget, QHBoxLayout, QSizePolicy, QApplication
from bauh import __version__, __app_name__, ROOT_DIR
from bauh.context import generate_i18n
@@ -21,28 +20,29 @@ class AboutDialog(QDialog):
layout = QVBoxLayout()
self.setLayout(layout)
+ logo_container = QWidget()
+ logo_container.setObjectName('logo_container')
+ logo_container.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
+ logo_container.setLayout(QHBoxLayout())
+
label_logo = QLabel()
- icon = QIcon(resource.get_path('img/logo.svg')).pixmap(64, 64)
- label_logo.setPixmap(icon)
- label_logo.setAlignment(Qt.AlignCenter)
- layout.addWidget(label_logo)
+ label_logo.setObjectName('logo')
+
+ logo_container.layout().addWidget(label_logo)
+ layout.addWidget(logo_container)
label_name = QLabel(__app_name__)
- label_name.setStyleSheet('font-weight: bold; font-size: 14px')
- label_name.setAlignment(Qt.AlignCenter)
+ label_name.setObjectName('app_name')
layout.addWidget(label_name)
label_version = QLabel(i18n['about.version'].lower() + ' ' + __version__)
- label_version.setStyleSheet('QLabel { font-size: 11px; font-weight: bold }')
- label_version.setAlignment(Qt.AlignCenter)
+ label_version.setObjectName('app_version')
layout.addWidget(label_version)
layout.addWidget(QLabel(''))
line_desc = QLabel(i18n['about.info.desc'])
- line_desc.setStyleSheet('font-size: 12px; font-weight: bold;')
- line_desc.setAlignment(Qt.AlignCenter)
- line_desc.setMinimumWidth(400)
+ line_desc.setObjectName('app_description')
layout.addWidget(line_desc)
layout.addWidget(QLabel(''))
@@ -54,54 +54,52 @@ class AboutDialog(QDialog):
gems_widget.setLayout(QHBoxLayout())
gems_widget.layout().addWidget(QLabel())
+ gem_logo_size = int(0.032552083 * QApplication.primaryScreen().size().height())
+
for gem_path in available_gems:
icon = QLabel()
+ icon.setObjectName('gem_logo')
icon_path = gem_path + '/resources/img/{}.svg'.format(gem_path.split('/')[-1])
- icon.setPixmap(QIcon(icon_path).pixmap(QSize(25, 25)))
+ icon.setPixmap(QIcon(icon_path).pixmap(gem_logo_size, gem_logo_size))
gems_widget.layout().addWidget(icon)
+
gems_widget.layout().addWidget(QLabel())
layout.addWidget(gems_widget)
layout.addWidget(QLabel(''))
label_more_info = QLabel()
- label_more_info.setStyleSheet('font-size: 11px;')
+ label_more_info.setObjectName('app_more_information')
label_more_info.setText(i18n['about.info.link'] + " {url} ".format(url=PROJECT_URL))
label_more_info.setOpenExternalLinks(True)
- label_more_info.setAlignment(Qt.AlignCenter)
layout.addWidget(label_more_info)
label_license = QLabel()
- label_license.setStyleSheet('font-size: 11px;')
+ label_license.setObjectName('app_license')
label_license.setText("{} ".format(LICENSE_URL, i18n['about.info.license']))
label_license.setOpenExternalLinks(True)
- label_license.setAlignment(Qt.AlignCenter)
layout.addWidget(label_license)
layout.addWidget(QLabel(''))
label_trouble_question = QLabel(i18n['about.info.trouble.question'])
- label_trouble_question.setStyleSheet('font-size: 10px; font-weight: bold')
- label_trouble_question.setAlignment(Qt.AlignCenter)
+ label_trouble_question.setObjectName('app_trouble_question')
layout.addWidget(label_trouble_question)
label_trouble_answer = QLabel(i18n['about.info.trouble.answer'])
- label_trouble_answer.setStyleSheet('font-size: 10px;')
- label_trouble_answer.setAlignment(Qt.AlignCenter)
+ label_trouble_answer.setObjectName('app_trouble_answer')
layout.addWidget(label_trouble_answer)
layout.addWidget(QLabel(''))
label_rate_question = QLabel(i18n['about.info.rate.question'])
- label_rate_question.setStyleSheet('font-size: 10px; font-weight: bold;')
- label_rate_question.setAlignment(Qt.AlignCenter)
+ label_rate_question.setObjectName('app_rate_question')
layout.addWidget(label_rate_question)
label_rate_answer = QLabel(i18n['about.info.rate.answer'])
- label_rate_answer.setStyleSheet('font-size: 10px;')
- label_rate_answer.setAlignment(Qt.AlignCenter)
+ label_rate_answer.setObjectName('app_rate_answer')
layout.addWidget(label_rate_answer)
layout.addWidget(QLabel(''))
diff --git a/bauh/view/qt/apps_table.py b/bauh/view/qt/apps_table.py
index 4d70c958..cf46ce4c 100644
--- a/bauh/view/qt/apps_table.py
+++ b/bauh/view/qt/apps_table.py
@@ -2,22 +2,21 @@ import operator
import os
from functools import reduce
from threading import Lock
-from typing import List
+from typing import List, Optional
from PyQt5.QtCore import Qt, QUrl, QSize
from PyQt5.QtGui import QPixmap, QIcon, QCursor
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply
-from PyQt5.QtWidgets import QTableWidget, QTableView, QMenu, QAction, QTableWidgetItem, QToolButton, QWidget, \
+from PyQt5.QtWidgets import QTableWidget, QTableView, QMenu, QToolButton, QWidget, \
QHeaderView, QLabel, QHBoxLayout, QToolBar, QSizePolicy
from bauh.api.abstract.cache import MemoryCache
from bauh.api.abstract.model import PackageStatus, CustomSoftwareAction
from bauh.commons.html import strip_html, bold
-from bauh.view.qt import dialog
-from bauh.view.qt.colors import GREEN, BROWN
-from bauh.view.qt.components import IconButton
+from bauh.view.qt.components import IconButton, QCustomMenuAction, QCustomToolbar
+from bauh.view.qt.dialog import ConfirmationDialog
+from bauh.view.qt.qt_utils import measure_based_on_height
from bauh.view.qt.view_model import PackageView
-from bauh.view.util import resource
from bauh.view.util.translation import I18n
NAME_MAX_SIZE = 30
@@ -25,45 +24,32 @@ DESC_MAX_SIZE = 40
PUBLISHER_MAX_SIZE = 25
-class UpdateToggleButton(QWidget):
+class UpgradeToggleButton(QToolButton):
- STYLE_DEFAULT = 'QToolButton { background: ' + GREEN + ' } QToolButton:checked { background: gray } '
- STYLE_UNCHECKED = 'QToolButton:disabled { background: #d69003 }'
-
- def __init__(self, pkg: PackageView, root: QWidget, i18n: I18n, checked: bool = True, clickable: bool = True):
- super(UpdateToggleButton, self).__init__()
- self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
+ def __init__(self, pkg: Optional[PackageView], root: QWidget, i18n: I18n, checked: bool = True,
+ clickable: bool = True):
+ super(UpgradeToggleButton, self).__init__()
self.app_view = pkg
self.root = root
- layout = QHBoxLayout()
- layout.setContentsMargins(0, 0, 0, 0)
- layout.setAlignment(Qt.AlignCenter)
- self.setLayout(layout)
-
- self.bt = QToolButton()
- self.bt.setCursor(QCursor(Qt.PointingHandCursor))
- self.bt.setCheckable(True)
- self.bt.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
+ self.setCursor(QCursor(Qt.PointingHandCursor))
+ self.setCheckable(True)
if clickable:
- self.bt.clicked.connect(self.change_state)
+ self.clicked.connect(self.change_state)
- self.bt.setStyleSheet(self.STYLE_DEFAULT + (self.STYLE_UNCHECKED if not clickable and not checked else ''))
-
- layout.addWidget(self.bt)
+ if not clickable and not checked:
+ self.setProperty('enabled', 'false')
if not checked:
- self.bt.click()
+ self.click()
if clickable:
- self.bt.setIcon(QIcon(resource.get_path('img/app_update.svg')))
self.setToolTip('{} {}'.format(i18n['manage_window.apps_table.upgrade_toggle.tooltip'],
i18n['manage_window.apps_table.upgrade_toggle.enabled.tooltip']))
else:
if not checked:
- self.bt.setIcon(QIcon(resource.get_path('img/exclamation.svg')))
- self.bt.setEnabled(False)
+ self.setEnabled(False)
tooltip = i18n['{}.update.disabled.tooltip'.format(pkg.model.gem_name)]
@@ -73,22 +59,22 @@ class UpdateToggleButton(QWidget):
self.setToolTip('{} {}'.format(i18n['manage_window.apps_table.upgrade_toggle.tooltip'],
i18n['manage_window.apps_table.upgrade_toggle.disabled.tooltip']))
else:
- self.bt.setIcon(QIcon(resource.get_path('img/app_update.svg')))
- self.bt.setCheckable(False)
+ self.setCheckable(False)
def change_state(self, not_checked: bool):
self.app_view.update_checked = not not_checked
+ self.setProperty('toggled', str(self.app_view.update_checked).lower())
self.root.update_bt_upgrade()
+ self.style().unpolish(self)
+ self.style().polish(self)
-class AppsTable(QTableWidget):
-
- COL_NUMBER = 8
- STYLE_BT_INSTALL = 'background: {b}; color: white; font-size: 10px; font-weight: bold'.format(b=GREEN)
- STYLE_BT_UNINSTALL = 'color: {c}; font-size: 10px; font-weight: bold;'.format(c=BROWN)
+class PackagesTable(QTableWidget):
+ COL_NUMBER = 9
def __init__(self, parent: QWidget, icon_cache: MemoryCache, download_icons: bool):
- super(AppsTable, self).__init__()
+ super(PackagesTable, self).__init__()
+ self.setObjectName('table_packages')
self.setParent(parent)
self.window = parent
self.download_icons = download_icons
@@ -102,8 +88,8 @@ class AppsTable(QTableWidget):
self.setHorizontalHeaderLabels(['' for _ in range(self.columnCount())])
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
- self.icon_logo = QIcon(resource.get_path('img/logo.svg'))
- self.pixmap_verified = QIcon(resource.get_path('img/verified.svg')).pixmap(QSize(10, 10))
+ self.horizontalScrollBar().setCursor(QCursor(Qt.PointingHandCursor))
+ self.verticalScrollBar().setCursor(QCursor(Qt.PointingHandCursor))
self.network_man = QNetworkAccessManager()
self.network_man.finished.connect(self._load_icon_and_cache)
@@ -114,6 +100,10 @@ class AppsTable(QTableWidget):
self.cache_type_icon = {}
self.i18n = self.window.i18n
+ def icon_size(self) -> QSize:
+ pixels = measure_based_on_height(0.02083)
+ return QSize(pixels, pixels)
+
def has_any_settings(self, pkg: PackageView):
return pkg.model.has_history() or \
pkg.model.can_be_downgraded() or \
@@ -122,77 +112,77 @@ class AppsTable(QTableWidget):
def show_pkg_actions(self, pkg: PackageView):
menu_row = QMenu()
+ menu_row.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Preferred)
+ menu_row.setObjectName('app_actions')
menu_row.setCursor(QCursor(Qt.PointingHandCursor))
if pkg.model.installed:
if pkg.model.has_history():
- action_history = QAction(self.i18n["manage_window.apps_table.row.actions.history"])
- action_history.setIcon(QIcon(resource.get_path('img/history.svg')))
-
def show_history():
self.window.begin_show_history(pkg)
- action_history.triggered.connect(show_history)
- menu_row.addAction(action_history)
+ menu_row.addAction(QCustomMenuAction(parent=menu_row,
+ label=self.i18n["manage_window.apps_table.row.actions.history"],
+ action=show_history,
+ button_name='app_history'))
if pkg.model.can_be_downgraded():
- action_downgrade = QAction(self.i18n["manage_window.apps_table.row.actions.downgrade"])
def downgrade():
- if dialog.ask_confirmation(
- title=self.i18n['manage_window.apps_table.row.actions.downgrade'],
- body=self._parag(self.i18n['manage_window.apps_table.row.actions.downgrade.popup.body'].format(self._bold(str(pkg)))),
- i18n=self.i18n):
+ if ConfirmationDialog(title=self.i18n['manage_window.apps_table.row.actions.downgrade'],
+ body=self._parag(self.i18n[
+ 'manage_window.apps_table.row.actions.downgrade.popup.body'].format(
+ self._bold(str(pkg)))),
+ i18n=self.i18n).ask():
self.window.begin_downgrade(pkg)
- action_downgrade.triggered.connect(downgrade)
- action_downgrade.setIcon(QIcon(resource.get_path('img/downgrade.svg')))
- menu_row.addAction(action_downgrade)
+ menu_row.addAction(QCustomMenuAction(parent=menu_row,
+ label=self.i18n["manage_window.apps_table.row.actions.downgrade"],
+ action=downgrade,
+ button_name='app_downgrade'))
if pkg.model.supports_ignored_updates():
if pkg.model.is_update_ignored():
- action_ignore_updates = QAction(
- self.i18n["manage_window.apps_table.row.actions.ignore_updates_reverse"])
- action_ignore_updates.setIcon(QIcon(resource.get_path('img/revert_update_ignored.svg')))
+ action_label = self.i18n["manage_window.apps_table.row.actions.ignore_updates_reverse"]
+ button_name = 'revert_ignore_updates'
else:
- action_ignore_updates = QAction(self.i18n["manage_window.apps_table.row.actions.ignore_updates"])
- action_ignore_updates.setIcon(QIcon(resource.get_path('img/ignore_update.svg')))
+ action_label = self.i18n["manage_window.apps_table.row.actions.ignore_updates"]
+ button_name = 'ignore_updates'
def ignore_updates():
self.window.begin_ignore_updates(pkg)
- action_ignore_updates.triggered.connect(ignore_updates)
- menu_row.addAction(action_ignore_updates)
+ menu_row.addAction(QCustomMenuAction(parent=menu_row,
+ label=action_label,
+ button_name=button_name,
+ action=ignore_updates))
if bool(pkg.model.get_custom_supported_actions()):
- actions = [self._map_custom_action(pkg, a) for a in pkg.model.get_custom_supported_actions()]
+ actions = [self._map_custom_action(pkg, a, menu_row) for a in pkg.model.get_custom_supported_actions()]
menu_row.addActions(actions)
menu_row.adjustSize()
menu_row.popup(QCursor.pos())
menu_row.exec_()
- def _map_custom_action(self, pkg: PackageView, action: CustomSoftwareAction) -> QAction:
- item = QAction(self.i18n[action.i18n_label_key])
-
- if action.icon_path:
- item.setIcon(QIcon(action.icon_path))
-
+ def _map_custom_action(self, pkg: PackageView, action: CustomSoftwareAction, parent: QWidget) -> QCustomMenuAction:
def custom_action():
if action.i18n_confirm_key:
body = self.i18n[action.i18n_confirm_key].format(bold(pkg.model.name))
else:
body = '{} ?'.format(self.i18n[action.i18n_label_key])
- if dialog.ask_confirmation(
- title=self.i18n[action.i18n_label_key],
- body=self._parag(body),
- i18n=self.i18n):
+ if ConfirmationDialog(icon=QIcon(pkg.model.get_type_icon_path()),
+ title=self.i18n[action.i18n_label_key],
+ body=self._parag(body),
+ i18n=self.i18n).ask():
self.window.begin_execute_custom_action(pkg, action)
- item.triggered.connect(custom_action)
- return item
+ return QCustomMenuAction(parent=parent,
+ label=self.i18n[action.i18n_label_key],
+ icon=QIcon(action.icon_path) if action.icon_path else None,
+ action=custom_action)
def refresh(self, pkg: PackageView):
self._update_row(pkg, update_check_enabled=False, change_update_col=False)
@@ -206,9 +196,11 @@ class AppsTable(QTableWidget):
self._update_row(pkg, change_update_col=change_update_col)
def _uninstall(self, pkg: PackageView):
- if dialog.ask_confirmation(title=self.i18n['manage_window.apps_table.row.actions.uninstall.popup.title'],
- body=self._parag(self.i18n['manage_window.apps_table.row.actions.uninstall.popup.body'].format(self._bold(str(pkg)))),
- i18n=self.i18n):
+ if ConfirmationDialog(title=self.i18n['manage_window.apps_table.row.actions.uninstall.popup.title'],
+ body=self._parag(
+ self.i18n['manage_window.apps_table.row.actions.uninstall.popup.body'].format(
+ self._bold(str(pkg)))),
+ i18n=self.i18n).ask():
self.window.begin_uninstall(pkg)
def _bold(self, text: str) -> str:
@@ -224,13 +216,12 @@ class AppsTable(QTableWidget):
warning = self.i18n.get('gem.{}.install.warning'.format(pkgv.model.get_type().lower()))
if warning:
- body += ' {}'.format(' '.join(('{}.'.format(phrase) for phrase in warning.split('.') if phrase)))
-
- if dialog.ask_confirmation(
- title=self.i18n['manage_window.apps_table.row.actions.install.popup.title'],
- body=self._parag(body),
- i18n=self.i18n):
+ body += ' {}'.format(
+ ' '.join(('{}.'.format(phrase) for phrase in warning.split('.') if phrase)))
+ if ConfirmationDialog(title=self.i18n['manage_window.apps_table.row.actions.install.popup.title'],
+ body=self._parag(body),
+ i18n=self.i18n).ask():
self.window.install(pkgv)
def _load_icon_and_cache(self, http_response: QNetworkReply):
@@ -257,18 +248,19 @@ class AppsTable(QTableWidget):
if icon_data:
for idx, app in enumerate(self.window.pkgs):
if app.model.icon_url == icon_url:
- col_name = self.item(idx, 0)
- col_name.setIcon(icon_data['icon'])
+ self._update_icon(self.cellWidget(idx, 0), icon_data['icon'])
if app.model.supports_disk_cache() and app.model.get_disk_icon_path() and icon_data['bytes']:
if not icon_was_cached or not os.path.exists(app.model.get_disk_icon_path()):
- self.window.manager.cache_to_disk(pkg=app.model, icon_bytes=icon_data['bytes'], only_icon=True)
+ self.window.manager.cache_to_disk(pkg=app.model, icon_bytes=icon_data['bytes'],
+ only_icon=True)
def update_packages(self, pkgs: List[PackageView], update_check_enabled: bool = True):
self.setRowCount(0) # removes the overwrite effect when updates the table
self.setEnabled(True)
if pkgs:
+ self.setColumnCount(self.COL_NUMBER if update_check_enabled else self.COL_NUMBER - 1)
self.setRowCount(len(pkgs))
for idx, pkg in enumerate(pkgs):
@@ -284,59 +276,49 @@ class AppsTable(QTableWidget):
self.scrollToTop()
def _update_row(self, pkg: PackageView, update_check_enabled: bool = True, change_update_col: bool = True):
- self._set_col_name(0, pkg)
- self._set_col_version(1, pkg)
- self._set_col_description(2, pkg)
- self._set_col_publisher(3, pkg)
- self._set_col_type(4, pkg)
- self._set_col_installed(5, pkg)
- self._set_col_actions(6, pkg)
+ self._set_col_icon(0, pkg)
+ self._set_col_name(1, pkg)
+ self._set_col_version(2, pkg)
+ self._set_col_description(3, pkg)
+ self._set_col_publisher(4, pkg)
+ self._set_col_type(5, pkg)
+ self._set_col_installed(6, pkg)
+ self._set_col_actions(7, pkg)
- if change_update_col:
- col_update = None
+ if change_update_col and update_check_enabled:
+ if pkg.model.installed and not pkg.model.is_update_ignored() and pkg.model.update:
+ col_update = QCustomToolbar()
+ col_update.add_space()
+ col_update.add_widget(UpgradeToggleButton(pkg=pkg,
+ root=self.window,
+ i18n=self.i18n,
+ checked=pkg.update_checked if pkg.model.can_be_updated() else False,
+ clickable=pkg.model.can_be_updated()))
+ col_update.add_space()
+ else:
+ col_update = QLabel()
- if update_check_enabled and pkg.model.installed and not pkg.model.is_update_ignored() and pkg.model.update:
- col_update = QToolBar()
- col_update.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
- col_update.addWidget(UpdateToggleButton(pkg=pkg,
- root=self.window,
- i18n=self.i18n,
- checked=pkg.update_checked if pkg.model.can_be_updated() else False,
- clickable=pkg.model.can_be_updated()))
-
- self.setCellWidget(pkg.table_index, 7, col_update)
-
- def _gen_row_button(self, text: str, style: str, callback) -> QWidget:
- col = QWidget()
- col.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
+ self.setCellWidget(pkg.table_index, 8, col_update)
+ def _gen_row_button(self, text: str, name: str, callback) -> QToolButton:
col_bt = QToolButton()
+ col_bt.setProperty('text_only', 'true')
+ col_bt.setObjectName(name)
col_bt.setCursor(QCursor(Qt.PointingHandCursor))
- col_bt.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
col_bt.setText(text)
- col_bt.setStyleSheet('QToolButton { ' + style + '}')
- col_bt.setMinimumWidth(80)
col_bt.clicked.connect(callback)
-
- layout = QHBoxLayout()
- layout.setContentsMargins(0, 0, 0, 0)
- layout.setAlignment(Qt.AlignCenter)
-
- layout.addWidget(col_bt)
-
- col.setLayout(layout)
- return col
+ return col_bt
def _set_col_installed(self, col: int, pkg: PackageView):
- toolbar = QToolBar()
- toolbar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
+ toolbar = QCustomToolbar()
+ toolbar.add_space()
if pkg.model.installed:
if pkg.model.can_be_uninstalled():
def uninstall():
self._uninstall(pkg)
- item = self._gen_row_button(self.i18n['uninstall'].capitalize(), self.STYLE_BT_UNINSTALL, uninstall)
+ item = self._gen_row_button(self.i18n['uninstall'].capitalize(), 'bt_uninstall', uninstall)
else:
item = None
@@ -344,34 +326,39 @@ class AppsTable(QTableWidget):
def install():
self._install_app(pkg)
- item = self._gen_row_button(self.i18n['install'].capitalize(), self.STYLE_BT_INSTALL, install)
+ item = self._gen_row_button(self.i18n['install'].capitalize(), 'bt_install', install)
else:
item = None
- toolbar.addWidget(item)
+ toolbar.add_widget(item)
+ toolbar.add_space()
self.setCellWidget(pkg.table_index, col, toolbar)
def _set_col_type(self, col: int, pkg: PackageView):
icon_data = self.cache_type_icon.get(pkg.model.get_type())
if icon_data is None:
- pixmap = QIcon(pkg.model.get_type_icon_path()).pixmap(QSize(16, 16))
+ pixmap = QIcon(pkg.model.get_type_icon_path()).pixmap(self.icon_size())
icon_data = {'px': pixmap, 'tip': '{}: {}'.format(self.i18n['type'], pkg.get_type_label())}
self.cache_type_icon[pkg.model.get_type()] = icon_data
- item = QLabel()
- item.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Preferred)
- item.setPixmap(icon_data['px'])
- item.setAlignment(Qt.AlignCenter)
-
- item.setToolTip(icon_data['tip'])
- self.setCellWidget(pkg.table_index, col, item)
+ col_type_icon = QLabel()
+ col_type_icon.setCursor(QCursor(Qt.WhatsThisCursor))
+ col_type_icon.setObjectName('app_type')
+ col_type_icon.setProperty('icon', 'true')
+ col_type_icon.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Preferred)
+ col_type_icon.setPixmap(icon_data['px'])
+ col_type_icon.setToolTip(icon_data['tip'])
+ self.setCellWidget(pkg.table_index, col, col_type_icon)
def _set_col_version(self, col: int, pkg: PackageView):
label_version = QLabel(str(pkg.model.version if pkg.model.version else '?'))
+ label_version.setObjectName('app_version')
label_version.setAlignment(Qt.AlignCenter)
item = QWidget()
+ item.setProperty('container', 'true')
+ item.setCursor(QCursor(Qt.WhatsThisCursor))
item.setLayout(QHBoxLayout())
item.layout().addWidget(label_version)
@@ -381,11 +368,11 @@ class AppsTable(QTableWidget):
tooltip = self.i18n['version.unknown']
if pkg.model.update and not pkg.model.is_update_ignored():
- label_version.setStyleSheet("color: {}; font-weight: bold".format(GREEN))
+ label_version.setProperty('update', 'true')
tooltip = self.i18n['version.installed_outdated']
if pkg.model.is_update_ignored():
- label_version.setStyleSheet("color: {}; font-weight: bold".format(BROWN))
+ label_version.setProperty('ignored', 'true')
tooltip = self.i18n['version.updates_ignored']
if pkg.model.installed and pkg.model.update and not pkg.model.is_update_ignored() and pkg.model.version and pkg.model.latest_version and pkg.model.version != pkg.model.latest_version:
@@ -395,25 +382,7 @@ class AppsTable(QTableWidget):
item.setToolTip(tooltip)
self.setCellWidget(pkg.table_index, col, item)
- def _set_col_name(self, col: int, pkg: PackageView):
- item = QTableWidgetItem()
- item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
-
- name = pkg.model.get_display_name()
- if name:
- item.setToolTip('{}: {}'.format(self.i18n['app.name'].lower(), pkg.model.get_name_tooltip()))
- else:
- name = '...'
- item.setToolTip(self.i18n['app.name'].lower())
-
- if len(name) > NAME_MAX_SIZE:
- name = name[0:NAME_MAX_SIZE - 3] + '...'
-
- if len(name) < NAME_MAX_SIZE:
- name = name + ' ' * (NAME_MAX_SIZE-len(name))
-
- item.setText(name)
-
+ def _set_col_icon(self, col: int, pkg: PackageView):
icon_path = pkg.model.get_disk_icon_path()
if pkg.model.installed and pkg.model.supports_disk_cache() and icon_path:
if icon_path.startswith('/'):
@@ -444,11 +413,42 @@ class AppsTable(QTableWidget):
icon_data = self.icon_cache.get(pkg.model.icon_url)
icon = icon_data['icon'] if icon_data else QIcon(pkg.model.get_default_icon_path())
- item.setIcon(icon)
- self.setItem(pkg.table_index, col, item)
+ col_icon = QLabel()
+ col_icon.setObjectName('app_icon')
+ col_icon.setProperty('icon', 'true')
+ col_icon.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Preferred)
+ self._update_icon(col_icon, icon)
+ self.setCellWidget(pkg.table_index, col, col_icon)
+
+ def _set_col_name(self, col: int, pkg: PackageView):
+ col_name = QLabel()
+ col_name.setObjectName('app_name')
+ col_name.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Preferred)
+ col_name.setCursor(QCursor(Qt.WhatsThisCursor))
+
+ name = pkg.model.get_display_name()
+ if name:
+ col_name.setToolTip('{}: {}'.format(self.i18n['app.name'].lower(), pkg.model.get_name_tooltip()))
+ else:
+ name = '...'
+ col_name.setToolTip(self.i18n['app.name'].lower())
+
+ if len(name) > NAME_MAX_SIZE:
+ name = name[0:NAME_MAX_SIZE - 3] + '...'
+
+ if len(name) < NAME_MAX_SIZE:
+ name = name + ' ' * (NAME_MAX_SIZE - len(name))
+
+ col_name.setText(name)
+ self.setCellWidget(pkg.table_index, col, col_name)
+
+ def _update_icon(self, label: QLabel, icon: QIcon):
+ label.setPixmap(icon.pixmap(QSize(self.icon_size())))
def _set_col_description(self, col: int, pkg: PackageView):
item = QLabel()
+ item.setObjectName('app_description')
+ item.setCursor(QCursor(Qt.WhatsThisCursor))
if pkg.model.description is not None or not pkg.model.is_application() or pkg.model.status == PackageStatus.READY:
desc = pkg.model.description.split('\n')[0] if pkg.model.description else pkg.model.description
@@ -478,21 +478,27 @@ class AppsTable(QTableWidget):
if len(publisher) > PUBLISHER_MAX_SIZE:
publisher = full_publisher[0: PUBLISHER_MAX_SIZE - 3] + '...'
+ lb_name = QLabel()
+ lb_name.setObjectName('app_publisher')
+ lb_name.setCursor(QCursor(Qt.WhatsThisCursor))
+
if not publisher:
if not pkg.model.installed:
- item.setStyleSheet('QLabel { color: red; }')
+ lb_name.setProperty('publisher_known', 'false')
publisher = self.i18n['unknown']
- lb_name = QLabel(' {}'.format(publisher))
+ lb_name.setText(' {}'.format(publisher))
item.addWidget(lb_name)
if publisher and full_publisher:
- lb_name.setToolTip(self.i18n['publisher'].capitalize() + ((': ' + full_publisher) if full_publisher else ''))
+ lb_name.setToolTip(
+ self.i18n['publisher'].capitalize() + ((': ' + full_publisher) if full_publisher else ''))
if pkg.model.is_trustable():
lb_verified = QLabel()
- lb_verified.setPixmap(self.pixmap_verified)
+ lb_verified.setObjectName('icon_publisher_verified')
+ lb_verified.setCursor(QCursor(Qt.WhatsThisCursor))
lb_verified.setToolTip(self.i18n['publisher.verified'].capitalize())
item.addWidget(lb_verified)
else:
@@ -501,49 +507,63 @@ class AppsTable(QTableWidget):
self.setCellWidget(pkg.table_index, col, item)
def _set_col_actions(self, col: int, pkg: PackageView):
- item = QToolBar()
- item.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
+ toolbar = QCustomToolbar()
+ toolbar.setObjectName('app_actions')
+ toolbar.add_space()
if pkg.model.installed:
def run():
self.window.begin_launch_package(pkg)
- bt = IconButton(QIcon(resource.get_path('img/app_play.svg')), i18n=self.i18n, action=run, tooltip=self.i18n['action.run.tooltip'])
- bt.setEnabled(pkg.model.can_be_run())
- item.addWidget(bt)
+ bt = IconButton(i18n=self.i18n, action=run, tooltip=self.i18n['action.run.tooltip'])
+ bt.setObjectName('app_run')
- def handle_click():
- self.show_pkg_actions(pkg)
+ if not pkg.model.can_be_run():
+ bt.setEnabled(False)
+ bt.setProperty('_enabled', 'false')
+
+ toolbar.layout().addWidget(bt)
settings = self.has_any_settings(pkg)
+
if pkg.model.installed:
- bt = IconButton(QIcon(resource.get_path('img/app_actions.svg')), i18n=self.i18n, action=handle_click, tooltip=self.i18n['action.settings.tooltip'])
+ def handle_custom_actions():
+ self.show_pkg_actions(pkg)
+
+ bt = IconButton(i18n=self.i18n, action=handle_custom_actions, tooltip=self.i18n['action.settings.tooltip'])
+ bt.setObjectName('app_actions')
bt.setEnabled(bool(settings))
- item.addWidget(bt)
+ toolbar.layout().addWidget(bt)
if not pkg.model.installed:
def show_screenshots():
self.window.begin_show_screenshots(pkg)
- bt = IconButton(QIcon(resource.get_path('img/camera.svg')), i18n=self.i18n, action=show_screenshots,
+ bt = IconButton(i18n=self.i18n, action=show_screenshots,
tooltip=self.i18n['action.screenshots.tooltip'])
- bt.setEnabled(bool(pkg.model.has_screenshots()))
- item.addWidget(bt)
+ bt.setObjectName('app_screenshots')
+
+ if not pkg.model.has_screenshots():
+ bt.setEnabled(False)
+ bt.setProperty('_enabled', 'false')
+
+ toolbar.layout().addWidget(bt)
def show_info():
self.window.begin_show_info(pkg)
- bt = IconButton(QIcon(resource.get_path('img/app_info.svg')), i18n=self.i18n, action=show_info, tooltip=self.i18n['action.info.tooltip'])
+ bt = IconButton(i18n=self.i18n, action=show_info, tooltip=self.i18n['action.info.tooltip'])
+ bt.setObjectName('app_info')
bt.setEnabled(bool(pkg.model.has_info()))
- item.addWidget(bt)
+ toolbar.layout().addWidget(bt)
- self.setCellWidget(pkg.table_index, col, item)
+ self.setCellWidget(pkg.table_index, col, toolbar)
def change_headers_policy(self, policy: QHeaderView = QHeaderView.ResizeToContents, maximized: bool = False):
header_horizontal = self.horizontalHeader()
for i in range(self.columnCount()):
if maximized:
- if i not in (3, 4, 7):
+ if i not in (4, 5, 8):
header_horizontal.setSectionResizeMode(i, QHeaderView.ResizeToContents)
else:
header_horizontal.setSectionResizeMode(i, QHeaderView.Stretch)
diff --git a/bauh/view/qt/colors.py b/bauh/view/qt/colors.py
index da643e5d..ecd1ba57 100644
--- a/bauh/view/qt/colors.py
+++ b/bauh/view/qt/colors.py
@@ -1,4 +1,3 @@
GREEN = '#91a069'
ORANGE = '#ECB03E'
-BROWN = '#B47C3E'
RED = '#FF614E'
diff --git a/bauh/view/qt/commons.py b/bauh/view/qt/commons.py
index b8e0639b..08135568 100644
--- a/bauh/view/qt/commons.py
+++ b/bauh/view/qt/commons.py
@@ -47,6 +47,16 @@ def apply_filters(pkg: PackageView, filters: dict, info: dict, limit: bool = Tru
info['pkgs_displayed'].append(pkg)
+def sum_updates_displayed(info: dict) -> int:
+ updates = 0
+ if info['pkgs_displayed']:
+ for p in info['pkgs_displayed']:
+ if p.model.update and not p.model.is_update_ignored():
+ updates += 1
+
+ return updates
+
+
def is_package_hidden(pkg: PackageView, filters: dict) -> bool:
hidden = filters['only_apps'] and pkg.model.installed and not pkg.model.is_application()
diff --git a/bauh/view/qt/components.py b/bauh/view/qt/components.py
index 75b6106e..0cadfb7b 100644
--- a/bauh/view/qt/components.py
+++ b/bauh/view/qt/components.py
@@ -3,18 +3,15 @@ import traceback
from pathlib import Path
from typing import Tuple, Dict, Optional, Set
-from PyQt5.QtCore import Qt, QSize, QTimer
-from PyQt5.QtGui import QIcon, QPixmap, QIntValidator, QCursor
+from PyQt5.QtCore import Qt, QTimer
+from PyQt5.QtGui import QIcon, QIntValidator, QCursor, QFocusEvent
from PyQt5.QtWidgets import QRadioButton, QGroupBox, QCheckBox, QComboBox, QGridLayout, QWidget, \
QLabel, QSizePolicy, QLineEdit, QToolButton, QHBoxLayout, QFormLayout, QFileDialog, QTabWidget, QVBoxLayout, \
- QSlider, QScrollArea, QFrame, QAction, QSpinBox, QPlainTextEdit
+ QSlider, QScrollArea, QFrame, QAction, QSpinBox, QPlainTextEdit, QWidgetAction, QPushButton, QMenu
from bauh.api.abstract.view import SingleSelectComponent, InputOption, MultipleSelectComponent, SelectViewType, \
TextInputComponent, FormComponent, FileChooserComponent, ViewComponent, TabGroupComponent, PanelComponent, \
TwoStateButtonComponent, TextComponent, SpacerComponent, RangeInputComponent, ViewObserver, TextInputType
-from bauh.view.qt import css
-from bauh.view.qt.colors import RED
-from bauh.view.util import resource
from bauh.view.util.translation import I18n
@@ -26,7 +23,7 @@ class QtComponentsManager:
self.group_of_groups = {}
self._saved_states = {}
- def register_component(self, component_id: int, instance: QWidget, action: QAction = None):
+ def register_component(self, component_id: int, instance: QWidget, action: Optional[QAction] = None):
comp = (instance, action, {'v': True, 'e': True, 'r': False})
self.components[component_id] = comp
self._save_state(comp)
@@ -339,6 +336,8 @@ class FormComboBoxQt(QComboBox):
def __init__(self, model: SingleSelectComponent):
super(FormComboBoxQt, self).__init__()
self.model = model
+ self.setCursor(QCursor(Qt.PointingHandCursor))
+ self.view().setCursor(QCursor(Qt.PointingHandCursor))
if model.max_width > 0:
self.setMaximumWidth(model.max_width)
@@ -401,8 +400,10 @@ class RadioSelectQt(QGroupBox):
def __init__(self, model: SingleSelectComponent):
super(RadioSelectQt, self).__init__(model.label + ' :' if model.label else None)
+ if not model.label:
+ self.setObjectName('radio_select_notitle')
+
self.model = model
- self.setStyleSheet("QGroupBox { font-weight: bold }")
grid = QGridLayout()
self.setLayout(grid)
@@ -431,10 +432,10 @@ class ComboSelectQt(QGroupBox):
def __init__(self, model: SingleSelectComponent):
super(ComboSelectQt, self).__init__()
self.model = model
- self.setLayout(QGridLayout())
- self.setStyleSheet('QGridLayout {margin-left: 0} QLabel { font-weight: bold}')
- self.layout().addWidget(QLabel(model.label + ' :' if model.label else ''), 0, 0)
- self.layout().addWidget(FormComboBoxQt(model), 0, 1)
+ self._layout = QGridLayout()
+ self.setLayout(self._layout)
+ self._layout.addWidget(QLabel(model.label + ' :' if model.label else ''), 0, 0)
+ self._layout.addWidget(FormComboBoxQt(model), 0, 1)
class QLineEditObserver(QLineEdit, ViewObserver):
@@ -469,9 +470,6 @@ class TextInputQt(QGroupBox):
super(TextInputQt, self).__init__()
self.model = model
self.setLayout(QGridLayout())
- self.setStyleSheet('QGridLayout {margin-left: 0} QLabel { font-weight: bold}')
-
- self.layout().addWidget(QLabel(model.get_label()), 0, 0)
if self.model.max_width > 0:
self.setMaximumWidth(self.model.max_width)
@@ -511,7 +509,6 @@ class MultipleSelectQt(QGroupBox):
def __init__(self, model: MultipleSelectComponent, callback):
super(MultipleSelectQt, self).__init__(model.label if model.label else None)
- self.setStyleSheet(css.GROUP_BOX)
self.model = model
self._layout = QGridLayout()
self.setLayout(self._layout)
@@ -531,17 +528,6 @@ class MultipleSelectQt(QGroupBox):
col = 0
- pixmap_help = QPixmap()
-
- for op in model.options: # loads the help icon if at least one option has a tooltip
- if op.tooltip:
- try:
- pixmap_help = QIcon(resource.get_path('img/about.svg')).pixmap(QSize(16, 16))
- except:
- traceback.print_exc()
-
- break
-
for op in model.options:
comp = CheckboxQt(op, model, callback)
@@ -555,7 +541,8 @@ class MultipleSelectQt(QGroupBox):
if op.tooltip:
help_icon = QLabel()
- help_icon.setPixmap(pixmap_help)
+ help_icon.setProperty('help_icon', 'true')
+ help_icon.setCursor(QCursor(Qt.WhatsThisCursor))
help_icon.setToolTip(op.tooltip)
widget.layout().addWidget(help_icon)
@@ -590,23 +577,12 @@ class FormMultipleSelectQt(QWidget):
if model.label:
line = 1
- self.layout().addWidget(QLabel(), 0, 1)
+ self._layout.addWidget(QLabel(), 0, 1)
else:
line = 0
col = 0
- pixmap_help = QPixmap()
-
- for op in model.options: # loads the help icon if at least one option has a tooltip
- if op.tooltip:
- try:
- pixmap_help = QIcon(resource.get_path('img/about.svg')).pixmap(QSize(16, 16))
- except:
- traceback.print_exc()
-
- break
-
for op in model.options:
comp = CheckboxQt(op, model, None)
@@ -620,9 +596,9 @@ class FormMultipleSelectQt(QWidget):
if op.tooltip:
help_icon = QLabel()
- help_icon.setPixmap(pixmap_help)
+ help_icon.setProperty('help_icon', 'true')
help_icon.setToolTip(op.tooltip)
- help_icon.setCursor(QCursor(Qt.PointingHandCursor))
+ help_icon.setCursor(QCursor(Qt.WhatsThisCursor))
widget.layout().addWidget(help_icon)
self._layout.addWidget(widget, line, col)
@@ -669,40 +645,26 @@ class InputFilter(QLineEdit):
self.last_text = p_str
-class IconButton(QWidget):
+class IconButton(QToolButton):
- def __init__(self, icon: QIcon, action, i18n: I18n, background: str = None, align: int = Qt.AlignCenter, tooltip: str = None, expanding: bool = False):
+ def __init__(self, action, i18n: I18n, align: int = Qt.AlignCenter, tooltip: str = None, expanding: bool = False):
super(IconButton, self).__init__()
- self.bt = QToolButton()
- self.bt.setCursor(QCursor(Qt.PointingHandCursor))
- self.bt.setIcon(icon)
- self.bt.clicked.connect(action)
+ self.setCursor(QCursor(Qt.PointingHandCursor))
+ self.clicked.connect(action)
self.i18n = i18n
self.default_tootip = tooltip
- self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
- self.bt.setSizePolicy(QSizePolicy.Expanding if expanding else QSizePolicy.Minimum, QSizePolicy.Minimum)
-
- if background:
- style = 'QToolButton { color: white; background: ' + background + '} '
- style += 'QToolButton:disabled { color: white; background: blue }'
- self.bt.setStyleSheet(style)
+ self.setSizePolicy(QSizePolicy.Expanding if expanding else QSizePolicy.Minimum, QSizePolicy.Minimum)
if tooltip:
- self.bt.setToolTip(tooltip)
-
- layout = QHBoxLayout()
- layout.setContentsMargins(0, 0, 0, 0)
- layout.setAlignment(align)
- layout.addWidget(self.bt)
- self.setLayout(layout)
+ self.setToolTip(tooltip)
def setEnabled(self, enabled):
super(IconButton, self).setEnabled(enabled)
if not enabled:
- self.bt.setToolTip(self.i18n['icon_button.tooltip.disabled'])
+ self.setToolTip(self.i18n['icon_button.tooltip.disabled'])
else:
- self.bt.setToolTip(self.default_tootip)
+ self.setToolTip(self.default_tootip)
class PanelQt(QWidget):
@@ -726,7 +688,6 @@ class FormQt(QGroupBox):
self.model = model
self.i18n = i18n
self.setLayout(QFormLayout())
- self.setStyleSheet(css.GROUP_BOX)
if model.spaces:
self.layout().addRow(QLabel(), QLabel())
@@ -787,7 +748,7 @@ class FormQt(QGroupBox):
if hasattr(comp, 'get_label'):
text = comp.get_label()
else:
- attr = 'label' if hasattr(comp,'label') else 'value'
+ attr = 'label' if hasattr(comp, 'label') else 'value'
text = getattr(comp, attr)
if text:
@@ -803,13 +764,8 @@ class FormQt(QGroupBox):
def gen_tip_icon(self, tip: str) -> QLabel:
tip_icon = QLabel()
+ tip_icon.setProperty('tip_icon', 'true')
tip_icon.setToolTip(tip.strip())
-
- try:
- tip_icon.setPixmap(QIcon(resource.get_path('img/about.svg')).pixmap(QSize(12, 12)))
- except:
- traceback.print_exc()
-
return tip_icon
def _new_text_input(self, c: TextInputComponent) -> Tuple[QLabel, QLineEdit]:
@@ -860,6 +816,7 @@ class FormQt(QGroupBox):
def _new_range_input(self, model: RangeInputComponent) -> QSpinBox:
spinner = QSpinBox()
+ spinner.setCursor(QCursor(Qt.PointingHandCursor))
spinner.setMinimum(model.min)
spinner.setMaximum(model.max)
spinner.setSingleStep(model.step)
@@ -929,13 +886,8 @@ class FormQt(QGroupBox):
label = self._new_label(c)
wrapped = self._wrap(chooser, c)
- try:
- icon = QIcon(resource.get_path('img/clean.svg'))
- except:
- traceback.print_exc()
- icon = QIcon()
-
- bt = IconButton(icon, i18n=self.i18n['clean'].capitalize(), action=clean_path, background=RED, tooltip=self.i18n['action.run.tooltip'])
+ bt = IconButton(i18n=self.i18n['clean'].capitalize(), action=clean_path, tooltip=self.i18n['clean'].capitalize())
+ bt.setObjectName('clean_field')
wrapped.layout().addWidget(bt)
return label, wrapped
@@ -962,6 +914,8 @@ class TabGroupQt(QTabWidget):
scroll.setWidget(to_widget(c.content, i18n))
self.addTab(scroll, icon, c.label)
+ self.tabBar().setCursor(QCursor(Qt.PointingHandCursor))
+
def new_single_select(model: SingleSelectComponent) -> QWidget:
if model.type == SelectViewType.RADIO:
@@ -974,6 +928,7 @@ def new_single_select(model: SingleSelectComponent) -> QWidget:
def new_spacer(min_width: int = None) -> QWidget:
spacer = QWidget()
+ spacer.setProperty('spacer', 'true')
if min_width:
spacer.setMinimumWidth(min_width)
@@ -1019,13 +974,13 @@ class RangeInputQt(QGroupBox):
super(RangeInputQt, self).__init__()
self.model = model
self.setLayout(QGridLayout())
- self.setStyleSheet('QGridLayout {margin-left: 0} QLabel { font-weight: bold}')
self.layout().addWidget(QLabel(model.label.capitalize() + ' :' if model.label else ''), 0, 0)
if self.model.max_width > 0:
self.setMaximumWidth(self.model.max_width)
self.spinner = QSpinBox()
+ self.spinner.setCursor(QCursor(Qt.PointingHandCursor))
self.spinner.setMinimum(model.min)
self.spinner.setMaximum(model.max)
self.spinner.setSingleStep(model.step)
@@ -1040,3 +995,155 @@ class RangeInputQt(QGroupBox):
def _update_value(self):
self.model.value = self.spinner.value()
+
+
+class QCustomLineEdit(QLineEdit):
+
+ def __init__(self, focus_in_callback, focus_out_callback, **kwargs):
+ super(QCustomLineEdit, self).__init__(**kwargs)
+ self.focus_in_callback = focus_in_callback
+ self.focus_out_callback = focus_out_callback
+
+ def focusInEvent(self, ev: QFocusEvent):
+ super(QCustomLineEdit, self).focusInEvent(ev)
+ if self.focus_in_callback:
+ self.focus_in_callback()
+
+ def focusOutEvent(self, ev: QFocusEvent):
+ super(QCustomLineEdit, self).focusOutEvent(ev)
+ if self.focus_out_callback:
+ self.focus_out_callback()
+
+ self.clearFocus()
+
+
+class QSearchBar(QWidget):
+
+ def __init__(self, search_callback, parent: Optional[QWidget] = None):
+ super(QSearchBar, self).__init__(parent=parent)
+ self.setLayout(QHBoxLayout())
+ self.setContentsMargins(0, 0, 0, 0)
+ self.layout().setSpacing(0)
+ self.callback = search_callback
+
+ self.inp_search = QCustomLineEdit(focus_in_callback=self._set_focus_in,
+ focus_out_callback=self._set_focus_out)
+ self.inp_search.setObjectName('inp_search')
+ self.inp_search.setFrame(False)
+ self.inp_search.returnPressed.connect(search_callback)
+ search_background_color = self.inp_search.palette().color(self.inp_search.backgroundRole()).name()
+
+ self.search_left_corner = QLabel()
+ self.search_left_corner.setObjectName('lb_left_corner')
+
+ self.layout().addWidget(self.search_left_corner)
+
+ self.layout().addWidget(self.inp_search)
+
+ self.search_button = QPushButton()
+ self.search_button.setObjectName('search_button')
+ self.search_button.setCursor(QCursor(Qt.PointingHandCursor))
+ self.search_button.clicked.connect(search_callback)
+
+ self.layout().addWidget(self.search_button)
+
+ def clear(self):
+ self.inp_search.clear()
+
+ def text(self) -> str:
+ return self.inp_search.text()
+
+ def set_text(self, text: str):
+ self.inp_search.setText(text)
+
+ def setFocus(self):
+ self.inp_search.setFocus()
+
+ def set_tooltip(self, tip: str):
+ self.inp_search.setToolTip(tip)
+
+ def set_button_tooltip(self, tip: str):
+ self.search_button.setToolTip(tip)
+
+ def set_placeholder(self, placeholder: str):
+ self.inp_search.setPlaceholderText(placeholder)
+
+ def _set_focus_in(self):
+ self.search_button.setProperty('focused', 'true')
+ self.search_left_corner.setProperty('focused', 'true')
+
+ for c in (self.search_button, self.search_left_corner):
+ c.style().unpolish(c)
+ c.style().polish(c)
+
+ def _set_focus_out(self):
+ self.search_button.setProperty('focused', 'false')
+ self.search_left_corner.setProperty('focused', 'false')
+
+ for c in (self.search_button, self.search_left_corner):
+ c.style().unpolish(c)
+ c.style().polish(c)
+
+
+class QCustomMenuAction(QWidgetAction):
+
+ def __init__(self, parent: QWidget, label: Optional[str] = None, action=None, button_name: Optional[str] = None,
+ icon: Optional[QIcon] = None, tooltip: Optional[str] = None):
+ super(QCustomMenuAction, self).__init__(parent)
+ self.button = QPushButton()
+ self.set_label(label)
+ self._action = None
+ self.set_action(action)
+ self.set_button_name(button_name)
+ self.set_icon(icon)
+ self.setDefaultWidget(self.button)
+
+ if tooltip:
+ self.button.setToolTip(tooltip)
+
+ def set_label(self, label: str):
+ self.button.setText(label)
+
+ def set_action(self, action):
+ self._action = action
+ self.button.clicked.connect(self._handle_action)
+
+ def _handle_action(self):
+ if self._action:
+ self._action()
+
+ if self.parent() and isinstance(self.parent(), QMenu):
+ self.parent().close()
+
+ def set_button_name(self, name: str):
+ if name:
+ self.button.setObjectName(name)
+
+ def set_icon(self, icon: QIcon):
+ if icon:
+ self.button.setIcon(icon)
+
+ def get_label(self) -> str:
+ return self.button.text()
+
+
+class QCustomToolbar(QWidget):
+
+ def __init__(self, spacing: int = 2, parent: Optional[QWidget] = None, alignment: int = Qt.AlignRight):
+ super(QCustomToolbar, self).__init__(parent=parent)
+ self.setProperty('container', 'true')
+ self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
+ self.setLayout(QHBoxLayout())
+ self.layout().setContentsMargins(0, 0, 0, 0)
+ self.layout().setSpacing(spacing)
+ self.layout().setAlignment(alignment)
+
+ def add_widget(self, widget: QWidget):
+ if widget:
+ self.layout().addWidget(widget)
+
+ def add_stretch(self, value: int = 0):
+ self.layout().addStretch(value)
+
+ def add_space(self, min_width: int = 0):
+ self.layout().addWidget(new_spacer(min_width))
diff --git a/bauh/view/qt/confirmation.py b/bauh/view/qt/confirmation.py
deleted file mode 100644
index d7ccc4d4..00000000
--- a/bauh/view/qt/confirmation.py
+++ /dev/null
@@ -1,83 +0,0 @@
-from typing import List
-
-from PyQt5.QtCore import QSize, Qt
-from PyQt5.QtGui import QCursor
-from PyQt5.QtWidgets import QMessageBox, QVBoxLayout, QLabel, QWidget, QScrollArea, QFrame
-
-from bauh.api.abstract.view import ViewComponent
-from bauh.view.qt import css
-from bauh.view.qt.components import to_widget
-from bauh.view.util.translation import I18n
-
-
-class ConfirmationDialog(QMessageBox):
-
- def __init__(self, title: str, body: str, i18n: I18n, screen_size: QSize, components: List[ViewComponent] = None,
- confirmation_label: str = None, deny_label: str = None, deny_button: bool = True, window_cancel: bool = True,
- confirmation_button: bool = True):
- super(ConfirmationDialog, self).__init__()
-
- if not window_cancel:
- self.setWindowFlags(Qt.CustomizeWindowHint | Qt.WindowTitleHint)
-
- self.setWindowTitle(title)
- self.setStyleSheet('QLabel { margin-right: 25px; }')
-
- self.bt_yes = None
- if confirmation_button:
- self.bt_yes = self.addButton(i18n['popup.button.yes'] if not confirmation_label else confirmation_label.capitalize(), QMessageBox.YesRole)
- self.bt_yes.setCursor(QCursor(Qt.PointingHandCursor))
- self.bt_yes.setStyleSheet(css.OK_BUTTON)
- self.setDefaultButton(self.bt_yes)
-
- if deny_button:
- self.bt_no = self.addButton(i18n['popup.button.no'] if not deny_label else deny_label.capitalize(), QMessageBox.NoRole)
- self.bt_no.setCursor(QCursor(Qt.PointingHandCursor))
-
- if not confirmation_button:
- self.bt_no.setStyleSheet(css.OK_BUTTON)
- self.setDefaultButton(self.bt_no)
-
- label = None
- if body:
- if not components:
- self.setIcon(QMessageBox.Question)
- label = QLabel(body)
- self.layout().addWidget(label, 0, 1)
-
- width = 0
- if components:
- scroll = QScrollArea(self)
- scroll.setFrameShape(QFrame.NoFrame)
- scroll.setWidgetResizable(True)
-
- comps_container = QWidget()
- comps_container.setLayout(QVBoxLayout())
- scroll.setWidget(comps_container)
-
- height = 0
-
- for idx, comp in enumerate(components):
- inst = to_widget(comp, i18n)
- height += inst.sizeHint().height()
-
- if inst.sizeHint().width() > width:
- width = inst.sizeHint().width()
-
- comps_container.layout().addWidget(inst)
-
- height = height if height < int(screen_size.height() / 2.5) else int(screen_size.height() / 2.5)
-
- scroll.setFixedHeight(height)
-
- self.layout().addWidget(scroll, 1 if body else 0, 1)
-
- if label and comps_container.sizeHint().width() > label.sizeHint().width():
- label.setText(label.text() + (' ' * int(comps_container.sizeHint().width() - label.sizeHint().width())))
- if not body and width > 0:
- self.layout().addWidget(QLabel(' ' * int(width / 2)), 1, 1)
-
- self.exec_()
-
- def is_confirmed(self) -> bool:
- return bool(self.bt_yes and self.clickedButton() == self.bt_yes)
diff --git a/bauh/view/qt/css.py b/bauh/view/qt/css.py
deleted file mode 100644
index b1008f43..00000000
--- a/bauh/view/qt/css.py
+++ /dev/null
@@ -1,19 +0,0 @@
-from bauh.view.qt.colors import GREEN
-
-OK_BUTTON = """QPushButton { background: %s; color: white; font-weight: bold}
- QPushButton:disabled { background-color: gray; }""" % GREEN
-
-GROUP_BOX = """
-QGroupBox {
- font-weight: bold;
- font-size: 12px;
- border: 1px solid silver;
- border-radius: 6px;
- margin-top: 6px;
-}
-QGroupBox::title {
- subcontrol-origin: margin;
- left: 7px;
- padding: 0px 5px 0px 5px;
-}
-"""
\ No newline at end of file
diff --git a/bauh/view/qt/dialog.py b/bauh/view/qt/dialog.py
index b6ae292f..0ada9b8a 100644
--- a/bauh/view/qt/dialog.py
+++ b/bauh/view/qt/dialog.py
@@ -1,11 +1,12 @@
-from typing import List
+from typing import List, Optional
-from PyQt5.QtCore import Qt
+from PyQt5.QtCore import Qt, QSize, QMargins
from PyQt5.QtGui import QIcon, QCursor
-from PyQt5.QtWidgets import QMessageBox, QLabel, QWidget, QHBoxLayout
+from PyQt5.QtWidgets import QMessageBox, QLabel, QWidget, QHBoxLayout, QDialog, QVBoxLayout, QSizePolicy, QApplication, \
+ QStyle, QPushButton, QScrollArea, QFrame
from bauh.api.abstract.view import MessageType
-from bauh.view.qt import css
+from bauh.view.qt.components import new_spacer
from bauh.view.util import resource
from bauh.view.util.translation import I18n
@@ -28,34 +29,96 @@ def show_message(title: str, body: str, type_: MessageType, icon: QIcon = QIcon(
popup.exec_()
-def ask_confirmation(title: str, body: str, i18n: I18n, icon: QIcon = QIcon(resource.get_path('img/logo.svg')), widgets: List[QWidget] = None):
- diag = QMessageBox()
- diag.setIcon(QMessageBox.Question)
- diag.setWindowTitle(title)
- diag.setStyleSheet('QLabel { margin-right: 25px; }')
- diag.setWindowFlags(Qt.CustomizeWindowHint | Qt.WindowTitleHint)
+class ConfirmationDialog(QDialog):
- wbody = QWidget()
- wbody.setLayout(QHBoxLayout())
- wbody.layout().addWidget(QLabel(body))
+ def __init__(self, title: str, body: Optional[str], i18n: I18n, icon: QIcon = QIcon(resource.get_path('img/logo.svg')),
+ widgets: Optional[List[QWidget]] = None, confirmation_button: bool = True, deny_button: bool = True,
+ window_cancel: bool = False, confirmation_label: Optional[str] = None, deny_label: Optional[str] = None):
+ super(ConfirmationDialog, self).__init__()
- if widgets:
- for w in widgets:
- wbody.layout().addWidget(w)
+ if not window_cancel:
+ self.setWindowFlags(Qt.CustomizeWindowHint | Qt.WindowTitleHint)
- diag.layout().addWidget(wbody, 0, 1)
+ self.setLayout(QVBoxLayout())
+ self.setWindowTitle(title)
+ self.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Preferred)
+ self.setMinimumWidth(250)
+ self.confirmed = False
- bt_yes = diag.addButton(i18n['popup.button.yes'], QMessageBox.YesRole)
- bt_yes.setStyleSheet(css.OK_BUTTON)
- bt_yes.setCursor(QCursor(Qt.PointingHandCursor))
- diag.setDefaultButton(bt_yes)
+ if icon:
+ self.setWindowIcon(icon)
- bt_no = diag.addButton(i18n['popup.button.no'], QMessageBox.NoRole)
- bt_no.setCursor(QCursor(Qt.PointingHandCursor))
+ container_body = QWidget()
+ container_body.setObjectName('confirm_container_body')
+ container_body.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Preferred)
- if icon:
- diag.setWindowIcon(icon)
+ if widgets:
+ container_body.setLayout(QVBoxLayout())
+ scroll = QScrollArea(self)
+ scroll.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Preferred)
+ scroll.setFrameShape(QFrame.NoFrame)
+ scroll.setWidgetResizable(True)
+ scroll.setWidget(container_body)
+ self.layout().addWidget(scroll)
+ else:
+ container_body.setLayout(QHBoxLayout())
+ self.layout().addWidget(container_body)
- diag.exec_()
+ lb_icon = QLabel()
+ lb_icon.setObjectName('confirm_icon')
+ lb_icon.setPixmap(QApplication.style().standardIcon(QStyle.SP_MessageBoxQuestion).pixmap(QSize(48, 48)))
+ container_body.layout().addWidget(lb_icon)
- return diag.clickedButton() == bt_yes
+ if body:
+ lb_msg = QLabel(body)
+ lb_msg.setObjectName('confirm_msg')
+ container_body.layout().addWidget(lb_msg)
+
+ if widgets:
+ for w in widgets:
+ container_body.layout().addWidget(w)
+ else:
+ container_body.layout().addWidget(new_spacer())
+
+ container_bottom = QWidget()
+ container_bottom.setObjectName('confirm_container_bottom')
+ container_bottom.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Preferred)
+ container_bottom.setLayout(QHBoxLayout())
+ self.layout().addWidget(container_bottom)
+
+ container_bottom.layout().addWidget(new_spacer())
+
+ if confirmation_button:
+ bt_confirm = QPushButton(confirmation_label.capitalize() if confirmation_label else i18n['popup.button.yes'])
+ bt_confirm.setObjectName('ok')
+ bt_confirm.setCursor(QCursor(Qt.PointingHandCursor))
+ bt_confirm.setDefault(True)
+ bt_confirm.setAutoDefault(True)
+ bt_confirm.clicked.connect(self.confirm)
+ container_bottom.layout().addWidget(bt_confirm)
+
+ if deny_button:
+ bt_cancel = QPushButton(deny_label.capitalize() if deny_label else i18n['popup.button.no'])
+ bt_cancel.setObjectName('bt_cancel')
+ bt_cancel.setCursor(QCursor(Qt.PointingHandCursor))
+ bt_cancel.clicked.connect(self.close)
+ container_bottom.layout().addWidget(bt_cancel)
+
+ if not confirmation_button:
+ bt_cancel.setDefault(True)
+ bt_cancel.setAutoDefault(True)
+
+ def confirm(self):
+ self.confirmed = True
+ self.close()
+
+ def ask(self) -> bool:
+ self.exec_()
+ return self.confirmed
+
+
+def ask_confirmation(title: str, body: str, i18n: I18n, icon: QIcon = QIcon(resource.get_path('img/logo.svg')),
+ widgets: List[QWidget] = None) -> bool:
+ popup = ConfirmationDialog(title=title, body=body, i18n=i18n, icon=icon, widgets=widgets)
+ popup.exec_()
+ return popup.confirmed
diff --git a/bauh/view/qt/gem_selector.py b/bauh/view/qt/gem_selector.py
deleted file mode 100644
index c4f812d5..00000000
--- a/bauh/view/qt/gem_selector.py
+++ /dev/null
@@ -1,103 +0,0 @@
-from PyQt5.QtCore import Qt
-from PyQt5.QtGui import QIcon
-from PyQt5.QtWidgets import QWidget, QLabel, QGridLayout, QPushButton
-
-from bauh import ROOT_DIR
-from bauh.api.abstract.view import MultipleSelectComponent, InputOption
-from bauh.view.core.config import save
-from bauh.view.core.controller import GenericSoftwareManager
-from bauh.view.util import resource
-from bauh.view.qt import qt_utils, css
-from bauh.view.qt.components import MultipleSelectQt, CheckboxQt, new_spacer
-from bauh.view.util.translation import I18n
-
-
-class GemSelectorPanel(QWidget):
-
- def __init__(self, window: QWidget, manager: GenericSoftwareManager, i18n: I18n, config: dict, show_panel_after_restart: bool = False):
- super(GemSelectorPanel, self).__init__()
- self.window = window
- self.manager = manager
- self.config = config
- self.setLayout(QGridLayout())
- self.setWindowIcon(QIcon(resource.get_path('img/logo.svg')))
- self.setWindowTitle(i18n['gem_selector.title'])
- self.resize(400, 400)
- self.show_panel_after_restart = show_panel_after_restart
-
- self.label_question = QLabel(i18n['gem_selector.question'])
- self.label_question.setStyleSheet('QLabel { font-weight: bold}')
- self.layout().addWidget(self.label_question, 0, 1, Qt.AlignHCenter)
-
- self.bt_proceed = QPushButton(i18n['change'].capitalize())
- self.bt_proceed.setStyleSheet(css.OK_BUTTON)
- self.bt_proceed.clicked.connect(self.save)
-
- self.bt_exit = QPushButton(i18n['close'].capitalize())
- self.bt_exit.clicked.connect(self.exit)
-
- self.gem_map = {}
- gem_options = []
- default = set()
-
- managers = [*manager.managers]
- managers.sort(key=lambda c: c.__class__.__name__)
-
- for m in managers:
- if m.can_work():
- modname = m.__module__.split('.')[-2]
- op = InputOption(label=i18n.get('gem.{}.label'.format(modname), modname.capitalize()),
- tooltip=i18n.get('gem.{}.info'.format(modname)),
- value=modname,
- icon_path='{r}/gems/{n}/resources/img/{n}.svg'.format(r=ROOT_DIR, n=modname))
-
- gem_options.append(op)
- self.gem_map[modname] = m
-
- if m.is_enabled() and m in manager.working_managers:
- default.add(op)
-
- if self.config['gems']:
- default_ops = {o for o in gem_options if o.value in self.config['gems']}
- else:
- default_ops = default
-
- self.bt_proceed.setEnabled(bool(default_ops))
-
- self.gem_select_model = MultipleSelectComponent(label='', options=gem_options, default_options=default_ops, max_per_line=1)
-
- self.gem_select = MultipleSelectQt(self.gem_select_model, self.check_state)
- self.layout().addWidget(self.gem_select, 1, 1)
-
- self.layout().addWidget(new_spacer(), 2, 1)
-
- self.layout().addWidget(self.bt_proceed, 3, 1, Qt.AlignRight)
- self.layout().addWidget(self.bt_exit, 3, 1, Qt.AlignLeft)
-
- self.adjustSize()
- self.setFixedSize(self.size())
- qt_utils.centralize(self)
-
- def check_state(self, model: CheckboxQt, checked: bool):
- if self.isVisible():
- self.bt_proceed.setEnabled(bool(self.gem_select_model.values))
-
- def save(self):
- enabled_gems = [op.value for op in self.gem_select_model.values]
-
- for module, man in self.gem_map.items():
- enabled = module in enabled_gems
- man.set_enabled(enabled)
-
- self.config['gems'] = enabled_gems
- save(self.config)
-
- self.manager.reset_cache()
- self.manager.prepare()
- self.window.verify_warnings()
- self.window.types_changed = True
- self.window.begin_refresh_packages()
- self.close()
-
- def exit(self):
- self.close()
diff --git a/bauh/view/qt/history.py b/bauh/view/qt/history.py
index 1a1855bf..3edb8625 100644
--- a/bauh/view/qt/history.py
+++ b/bauh/view/qt/history.py
@@ -2,8 +2,8 @@ import operator
from functools import reduce
from PyQt5.QtCore import Qt
-from PyQt5.QtGui import QColor, QIcon
-from PyQt5.QtWidgets import QDialog, QVBoxLayout, QTableWidget, QTableWidgetItem, QHeaderView
+from PyQt5.QtGui import QColor, QIcon, QCursor
+from PyQt5.QtWidgets import QDialog, QVBoxLayout, QTableWidget, QTableWidgetItem, QHeaderView, QLabel
from bauh.api.abstract.cache import MemoryCache
from bauh.api.abstract.model import PackageHistory
@@ -40,16 +40,19 @@ class HistoryDialog(QDialog):
current_status = history.pkg_status_idx == row
for col, key in enumerate(sorted(data.keys())):
- item = QTableWidgetItem()
+ item = QLabel()
+ item.setProperty('even', row % 2 == 0)
item.setText(str(data[key]))
if current_status:
- item.setBackground(QColor(ORANGE if row != 0 else GREEN))
+ item.setCursor(QCursor(Qt.WhatsThisCursor))
+ item.setProperty('outdated', str(row != 0).lower())
+
tip = '{}. {}.'.format(i18n['popup.history.selected.tooltip'], i18n['version.{}'.format('updated'if row == 0 else 'outdated')].capitalize())
item.setToolTip(tip)
- table_history.setItem(row, col, item)
+ table_history.setCellWidget(row, col, item)
layout.addWidget(table_history)
diff --git a/bauh/view/qt/info.py b/bauh/view/qt/info.py
index 9f6ba1c3..64c55c49 100644
--- a/bauh/view/qt/info.py
+++ b/bauh/view/qt/info.py
@@ -1,7 +1,8 @@
from PyQt5.QtCore import QSize, Qt
from PyQt5.QtGui import QIcon, QCursor
from PyQt5.QtWidgets import QDialog, QVBoxLayout, QGroupBox, \
- QLineEdit, QLabel, QGridLayout, QPushButton, QPlainTextEdit, QToolBar, QScrollArea, QFrame, QWidget
+ QLineEdit, QLabel, QGridLayout, QPushButton, QPlainTextEdit, QScrollArea, QFrame, QWidget, QSizePolicy, \
+ QHBoxLayout
from bauh.api.abstract.cache import MemoryCache
from bauh.view.qt.components import new_spacer
@@ -13,7 +14,7 @@ IGNORED_ATTRS = {'name', '__app__'}
class InfoDialog(QDialog):
def __init__(self, pkg_info: dict, icon_cache: MemoryCache, i18n: I18n, screen_size: QSize):
- super(InfoDialog, self).__init__(flags=Qt.CustomizeWindowHint | Qt.WindowTitleHint)
+ super(InfoDialog, self).__init__()
self.setWindowTitle(str(pkg_info['__app__']))
self.screen_size = screen_size
self.i18n = i18n
@@ -24,16 +25,19 @@ class InfoDialog(QDialog):
scroll.setFrameShape(QFrame.NoFrame)
scroll.setWidgetResizable(True)
comps_container = QWidget()
+ comps_container.setObjectName('root_container')
comps_container.setLayout(QVBoxLayout())
scroll.setWidget(comps_container)
# shows complete field string
self.text_field = QPlainTextEdit()
+ self.text_field.setObjectName('full_field')
self.text_field.setReadOnly(True)
comps_container.layout().addWidget(self.text_field)
self.text_field.hide()
self.gbox_info = QGroupBox()
+ self.gbox_info.setObjectName('fields')
self.gbox_info.setLayout(QGridLayout())
comps_container.layout().addWidget(self.gbox_info)
@@ -64,14 +68,14 @@ class InfoDialog(QDialog):
show_val = val
text = QLineEdit()
+ text.setObjectName('field_value')
text.setToolTip(show_val)
text.setText(val)
text.setCursorPosition(0)
- text.setStyleSheet("width: 400px")
text.setReadOnly(True)
label = QLabel(i18n.get(i18n_key, i18n.get(attr.lower(), attr)).capitalize())
- label.setStyleSheet("font-weight: bold")
+ label.setObjectName('field_name')
self.gbox_info.layout().addWidget(label, idx, 0)
self.gbox_info.layout().addWidget(text, idx, 1)
@@ -79,21 +83,27 @@ class InfoDialog(QDialog):
layout.addWidget(scroll)
- lower_bar = QToolBar()
- bt_back = QPushButton(self.i18n['back'].capitalize())
- bt_back.setVisible(False)
- bt_back.setCursor(QCursor(Qt.PointingHandCursor))
- bt_back.clicked.connect(self.back_to_info)
+ lower_container = QWidget()
+ lower_container.setObjectName('lower_container')
+ lower_container.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
+ lower_container.setLayout(QHBoxLayout())
- self.ref_bt_back = lower_bar.addWidget(bt_back)
- lower_bar.addWidget(new_spacer())
+ self.bt_back = QPushButton('< {}'.format(self.i18n['back'].capitalize()))
+ self.bt_back.setObjectName('back')
+ self.bt_back.setVisible(False)
+ self.bt_back.setCursor(QCursor(Qt.PointingHandCursor))
+ self.bt_back.clicked.connect(self.back_to_info)
- bt_close = QPushButton(self.i18n['close'].capitalize())
- bt_close.setCursor(QCursor(Qt.PointingHandCursor))
- bt_close.clicked.connect(self.close)
+ lower_container.layout().addWidget(self.bt_back)
+ lower_container.layout().addWidget(new_spacer())
- lower_bar.addWidget(bt_close)
- layout.addWidget(lower_bar)
+ self.bt_close = QPushButton(self.i18n['close'].capitalize())
+ self.bt_close.setObjectName('close')
+ self.bt_close.setCursor(QCursor(Qt.PointingHandCursor))
+ self.bt_close.clicked.connect(self.close)
+
+ lower_container.layout().addWidget(self.bt_close)
+ layout.addWidget(lower_container)
self.setMinimumWidth(self.gbox_info.sizeHint().width() * 1.2)
self.setMaximumHeight(screen_size.height() * 0.8)
self.adjustSize()
@@ -102,11 +112,12 @@ class InfoDialog(QDialog):
def show_full_field():
self.gbox_info.hide()
- self.ref_bt_back.setVisible(True)
+ self.bt_back.setVisible(True)
self.text_field.show()
self.text_field.setPlainText(val)
bt_full_field = QPushButton(self.i18n['show'].capitalize())
+ bt_full_field.setObjectName('show')
bt_full_field.setCursor(QCursor(Qt.PointingHandCursor))
bt_full_field.clicked.connect(show_full_field)
self.gbox_info.layout().addWidget(bt_full_field, idx, 2)
@@ -115,4 +126,4 @@ class InfoDialog(QDialog):
self.text_field.setPlainText("")
self.text_field.hide()
self.gbox_info.show()
- self.ref_bt_back.setVisible(False)
+ self.bt_back.setVisible(False)
diff --git a/bauh/view/qt/prepare.py b/bauh/view/qt/prepare.py
index f662f81b..f39ba865 100644
--- a/bauh/view/qt/prepare.py
+++ b/bauh/view/qt/prepare.py
@@ -1,10 +1,10 @@
import datetime
import operator
from functools import reduce
-from typing import Tuple
+from typing import Tuple, Optional
from PyQt5.QtCore import QSize, Qt, QThread, pyqtSignal, QCoreApplication
-from PyQt5.QtGui import QIcon, QCursor
+from PyQt5.QtGui import QIcon, QCursor, QCloseEvent
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel, QSizePolicy, QTableWidget, QHeaderView, QPushButton, QToolBar, \
QProgressBar, QApplication, QPlainTextEdit, QToolButton, QHBoxLayout
@@ -12,10 +12,9 @@ from bauh import __app_name__
from bauh.api.abstract.context import ApplicationContext
from bauh.api.abstract.controller import SoftwareManager
from bauh.api.abstract.handler import TaskManager
-from bauh.view.qt import root, styles
-from bauh.view.qt.colors import GREEN
-from bauh.view.qt.components import new_spacer
+from bauh.view.qt.components import new_spacer, QCustomToolbar
from bauh.view.qt.qt_utils import centralize
+from bauh.view.qt.root import RootDialog
from bauh.view.qt.thread import AnimateProgress
from bauh.view.util.translation import I18n
@@ -37,7 +36,7 @@ class Prepare(QThread, TaskManager):
self.password_response = None
self._registered = 0
- def ask_password(self) -> Tuple[str, bool]:
+ def ask_password(self) -> Tuple[bool, Optional[str]]:
self.waiting_password = True
self.signal_ask_password.emit()
@@ -46,14 +45,14 @@ class Prepare(QThread, TaskManager):
return self.password_response
- def set_password_reply(self, password: str, valid: bool):
- self.password_response = password, valid
+ def set_password_reply(self, valid: bool, password: str):
+ self.password_response = valid, password
self.waiting_password = False
def run(self):
root_pwd = None
if self.manager.requires_root('prepare', None):
- root_pwd, ok = self.ask_password()
+ ok, root_pwd = self.ask_password()
if not ok:
QCoreApplication.exit(1)
@@ -115,7 +114,7 @@ class EnableSkip(QThread):
class PreparePanel(QWidget, TaskManager):
signal_status = pyqtSignal(int)
- signal_password_response = pyqtSignal(str, bool)
+ signal_password_response = pyqtSignal(bool, str)
def __init__(self, context: ApplicationContext, manager: SoftwareManager, screen_size: QSize, i18n: I18n, manage_window: QWidget):
super(PreparePanel, self).__init__(flags=Qt.CustomizeWindowHint | Qt.WindowTitleHint)
@@ -157,14 +156,14 @@ class PreparePanel(QWidget, TaskManager):
self.label_top = QLabel()
self.label_top.setCursor(QCursor(Qt.WaitCursor))
self.label_top.setText("{}...".format(self.i18n['prepare_panel.title.start'].capitalize()))
+ self.label_top.setObjectName('prepare_status')
self.label_top.setAlignment(Qt.AlignHCenter)
- self.label_top.setStyleSheet("QLabel { font-size: 14px; font-weight: bold; }")
self.layout().addWidget(self.label_top)
self.layout().addWidget(QLabel())
self.table = QTableWidget()
+ self.table.setObjectName('tasks')
self.table.setCursor(QCursor(Qt.WaitCursor))
- self.table.setStyleSheet("QTableWidget { background-color: transparent; }")
self.table.setFocusPolicy(Qt.NoFocus)
self.table.setShowGrid(False)
self.table.verticalHeader().setVisible(False)
@@ -172,58 +171,63 @@ class PreparePanel(QWidget, TaskManager):
self.table.horizontalHeader().setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Preferred)
self.table.setColumnCount(4)
self.table.setHorizontalHeaderLabels(['' for _ in range(4)])
+ self.table.horizontalScrollBar().setCursor(QCursor(Qt.PointingHandCursor))
+ self.table.verticalScrollBar().setCursor(QCursor(Qt.PointingHandCursor))
self.layout().addWidget(self.table)
- self.textarea_output = QPlainTextEdit(self)
- self.textarea_output.resize(self.table.size())
- self.textarea_output.setStyleSheet("background: black; color: white;")
- self.layout().addWidget(self.textarea_output)
- self.textarea_output.setVisible(False)
- self.textarea_output.setReadOnly(True)
- self.textarea_output.setMaximumHeight(100)
+ self.textarea_details = QPlainTextEdit(self)
+ self.textarea_details.setObjectName('task_details')
+ self.textarea_details.setProperty('console', 'true')
+ self.textarea_details.resize(self.table.size())
+ self.layout().addWidget(self.textarea_details)
+ self.textarea_details.setVisible(False)
+ self.textarea_details.setReadOnly(True)
+ self.textarea_details.setMaximumHeight(100)
self.current_output_task = None
self.bottom_widget = QWidget()
self.bottom_widget.setLayout(QHBoxLayout())
self.bottom_widget.layout().addStretch()
- bt_hide_output = QPushButton(self.i18n['prepare.bt_hide_details'])
- bt_hide_output.setStyleSheet('QPushButton { text-decoration: underline; border: 0px; background: none } ')
- bt_hide_output.clicked.connect(self.hide_output)
- bt_hide_output.setCursor(QCursor(Qt.PointingHandCursor))
- self.bottom_widget.layout().addWidget(bt_hide_output)
+
+ bt_hide_details = QPushButton(self.i18n['prepare.bt_hide_details'])
+ bt_hide_details.setObjectName('bt_hide_details')
+ bt_hide_details.clicked.connect(self.hide_output)
+ bt_hide_details.setCursor(QCursor(Qt.PointingHandCursor))
+ self.bottom_widget.layout().addWidget(bt_hide_details)
self.bottom_widget.layout().addStretch()
self.layout().addWidget(self.bottom_widget)
self.bottom_widget.setVisible(False)
- self.bt_bar = QToolBar()
+ self.bt_bar = QCustomToolbar()
self.bt_close = QPushButton(self.i18n['close'].capitalize())
+ self.bt_close.setObjectName('bt_cancel')
self.bt_close.setCursor(QCursor(Qt.PointingHandCursor))
self.bt_close.clicked.connect(self.close)
self.bt_close.setVisible(False)
- self.ref_bt_close = self.bt_bar.addWidget(self.bt_close)
+ self.bt_bar.add_widget(self.bt_close)
+ self.bt_bar.add_widget(new_spacer())
- self.bt_bar.addWidget(new_spacer())
self.progress_bar = QProgressBar()
- self.progress_bar.setStyleSheet(styles.PROGRESS_BAR)
+ self.progress_bar.setObjectName('prepare_progress')
self.progress_bar.setMaximumHeight(10 if QApplication.instance().style().objectName().lower() == 'windows' else 4)
self.progress_bar.setTextVisible(False)
self.progress_bar.setVisible(False)
self.progress_bar.setCursor(QCursor(Qt.WaitCursor))
- self.ref_progress_bar = self.bt_bar.addWidget(self.progress_bar)
- self.bt_bar.addWidget(new_spacer())
+ self.bt_bar.add_widget(self.progress_bar)
+ self.bt_bar.add_widget(new_spacer())
self.bt_skip = QPushButton(self.i18n['prepare_panel.bt_skip.label'].capitalize())
self.bt_skip.clicked.connect(self.finish)
self.bt_skip.setEnabled(False)
self.bt_skip.setCursor(QCursor(Qt.WaitCursor))
- self.bt_bar.addWidget(self.bt_skip)
+ self.bt_bar.add_widget(self.bt_skip)
self.layout().addWidget(self.bt_bar)
def hide_output(self):
self.current_output_task = None
- self.textarea_output.setVisible(False)
- self.textarea_output.clear()
+ self.textarea_details.setVisible(False)
+ self.textarea_details.clear()
self.bottom_widget.setVisible(False)
self._resize_columns()
self.setFocus(Qt.NoFocusReason)
@@ -232,8 +236,8 @@ class PreparePanel(QWidget, TaskManager):
self.bt_bar.setVisible(True)
def ask_root_password(self):
- root_pwd, ok = root.ask_root_password(self.context, self.i18n)
- self.signal_password_response.emit(root_pwd, ok)
+ valid, root_pwd = RootDialog.ask_password(self.context, self.i18n)
+ self.signal_password_response.emit(valid, root_pwd)
def _enable_skip_button(self):
self.bt_skip.setEnabled(True)
@@ -250,7 +254,7 @@ class PreparePanel(QWidget, TaskManager):
for i in range(self.table.columnCount()):
header_horizontal.setSectionResizeMode(i, QHeaderView.ResizeToContents)
- self.resize(self.get_table_width() * 1.05, self.sizeHint().height())
+ self.resize(int(self.get_table_width() * 1.05), self.sizeHint().height())
def show(self):
super(PreparePanel, self).show()
@@ -264,10 +268,10 @@ class PreparePanel(QWidget, TaskManager):
self.progress_thread.start()
- self.ref_bt_close.setVisible(True)
- self.ref_progress_bar.setVisible(True)
+ self.bt_close.setVisible(True)
+ self.progress_bar.setVisible(True)
- def closeEvent(self, QCloseEvent):
+ def closeEvent(self, ev: QCloseEvent):
if not self.self_close:
QCoreApplication.exit()
@@ -277,14 +281,15 @@ class PreparePanel(QWidget, TaskManager):
task_row = self.added_tasks - 1
icon_widget = QWidget()
+ icon_widget.setProperty('container', 'true')
icon_widget.setLayout(QHBoxLayout())
icon_widget.layout().setContentsMargins(10, 0, 10, 0)
+
bt_icon = QToolButton()
+ bt_icon.setObjectName('bt_task')
bt_icon.setCursor(QCursor(Qt.WaitCursor))
bt_icon.setEnabled(False)
bt_icon.setToolTip(self.i18n['prepare.bt_icon.no_output'])
- bt_icon.setFixedSize(QSize(24, 24))
- bt_icon.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
if icon_path:
bt_icon.setIcon(QIcon(icon_path))
@@ -294,11 +299,11 @@ class PreparePanel(QWidget, TaskManager):
if lines:
self.current_output_task = id_
- self.textarea_output.clear()
- self.textarea_output.setVisible(True)
+ self.textarea_details.clear()
+ self.textarea_details.setVisible(True)
for l in lines:
- self.textarea_output.appendPlainText(l)
+ self.textarea_details.appendPlainText(l)
self.bottom_widget.setVisible(True)
@@ -313,22 +318,27 @@ class PreparePanel(QWidget, TaskManager):
self.table.setCellWidget(task_row, 0, icon_widget)
lb_status = QLabel(label)
+ lb_status.setObjectName('task_status')
+ lb_status.setProperty('status', 'running')
lb_status.setCursor(Qt.WaitCursor)
lb_status.setMinimumWidth(50)
lb_status.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
- lb_status.setStyleSheet("QLabel { font-weight: bold; }")
- self.table.setCellWidget(task_row, 1, lb_status)
+ lb_status_col = 1
+ self.table.setCellWidget(task_row, lb_status_col, lb_status)
lb_progress = QLabel('{0:.2f}'.format(0) + '%')
+ lb_progress.setObjectName('task_progress')
+ lb_progress.setProperty('status', 'running')
lb_progress.setCursor(Qt.WaitCursor)
lb_progress.setContentsMargins(10, 0, 10, 0)
- lb_progress.setStyleSheet("QLabel { font-weight: bold; }")
lb_progress.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
+ lb_progress_col = 2
- self.table.setCellWidget(task_row, 2, lb_progress)
+ self.table.setCellWidget(task_row, lb_progress_col, lb_progress)
lb_sub = QLabel()
- lb_status.setCursor(Qt.WaitCursor)
+ lb_sub.setObjectName('task_substatus')
+ lb_sub.setCursor(Qt.WaitCursor)
lb_sub.setContentsMargins(10, 0, 10, 0)
lb_sub.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
lb_sub.setMinimumWidth(50)
@@ -336,10 +346,13 @@ class PreparePanel(QWidget, TaskManager):
self.tasks[id_] = {'bt_icon': bt_icon,
'lb_status': lb_status,
+ 'lb_status_col': lb_status_col,
'lb_prog': lb_progress,
+ 'lb_prog_col': lb_progress_col,
'progress': 0,
'lb_sub': lb_sub,
- 'finished': False}
+ 'finished': False,
+ 'row': task_row}
def update_progress(self, task_id: str, progress: float, substatus: str):
task = self.tasks[task_id]
@@ -369,14 +382,18 @@ class PreparePanel(QWidget, TaskManager):
full_output.append(output)
if self.current_output_task == task_id:
- self.textarea_output.appendPlainText(output)
+ self.textarea_details.appendPlainText(output)
def finish_task(self, task_id: str):
task = self.tasks[task_id]
task['lb_sub'].setText('')
for key in ('lb_prog', 'lb_status'):
- task[key].setStyleSheet('QLabel { color: %s; text-decoration: line-through; }' % GREEN)
+ label = task[key]
+ label.setProperty('status', 'done')
+ label.style().unpolish(label)
+ label.style().polish(label)
+ label.update()
task['finished'] = True
self._resize_columns()
@@ -389,7 +406,7 @@ class PreparePanel(QWidget, TaskManager):
def finish(self):
if self.isVisible():
- self.manage_window.begin_refresh_packages()
self.manage_window.show()
+ self.manage_window.begin_refresh_packages()
self.self_close = True
self.close()
diff --git a/bauh/view/qt/qt_utils.py b/bauh/view/qt/qt_utils.py
index 30ba55dd..ca092d6e 100644
--- a/bauh/view/qt/qt_utils.py
+++ b/bauh/view/qt/qt_utils.py
@@ -19,3 +19,11 @@ def load_icon(path: str, width: int, height: int = None) -> QIcon:
def load_resource_icon(path: str, width: int, height: int = None) -> QIcon:
return load_icon(resource.get_path(path), width, height)
+
+
+def measure_based_on_width(percent: float) -> int:
+ return round(percent * QApplication.primaryScreen().size().width())
+
+
+def measure_based_on_height(percent: float) -> int:
+ return round(percent * QApplication.primaryScreen().size().height())
diff --git a/bauh/view/qt/root.py b/bauh/view/qt/root.py
index fc86950f..7c260da0 100644
--- a/bauh/view/qt/root.py
+++ b/bauh/view/qt/root.py
@@ -1,100 +1,164 @@
import os
-from typing import Tuple
+import traceback
+from typing import Tuple, Optional
-from PyQt5.QtCore import Qt
-from PyQt5.QtGui import QCursor, QIcon
-from PyQt5.QtWidgets import QInputDialog, QLineEdit, QDialogButtonBox, QApplication
+from PyQt5.QtCore import Qt, QThread, pyqtSignal
+from PyQt5.QtGui import QCursor
+from PyQt5.QtWidgets import QLineEdit, QApplication, QDialog, QPushButton, QVBoxLayout, \
+ QSizePolicy, QToolBar, QLabel
from bauh.api.abstract.context import ApplicationContext
-from bauh.api.abstract.view import MessageType
from bauh.commons.system import new_subprocess
from bauh.view.core.config import read_config
-from bauh.view.qt import css
-from bauh.view.qt.components import QtComponentsManager
-from bauh.view.qt.dialog import show_message
+from bauh.view.qt.components import QtComponentsManager, new_spacer
from bauh.view.util import util
from bauh.view.util.translation import I18n
ACTION_ASK_ROOT = 99
+class ValidatePassword(QThread):
+
+ signal_valid = pyqtSignal(bool)
+
+ def __init__(self, password: Optional[str] = None):
+ super(ValidatePassword, self).__init__()
+ self.password = password
+
+ def run(self):
+ if self.password is not None:
+ try:
+ valid = validate_password(self.password)
+ except:
+ traceback.print_exc()
+ valid = False
+
+ self.signal_valid.emit(valid)
+
+
+class RootDialog(QDialog):
+
+ def __init__(self, i18n: I18n, max_tries: int = 3):
+ super(RootDialog, self).__init__(flags=Qt.CustomizeWindowHint | Qt.WindowTitleHint)
+ self.i18n = i18n
+ self.max_tries = max_tries
+ self.tries = 0
+ self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
+ self.setWindowIcon(util.get_default_icon()[1])
+ self.setWindowTitle(i18n['popup.root.title'])
+ self.setLayout(QVBoxLayout())
+ self.setMinimumWidth(300)
+
+ self.label_msg = QLabel(i18n['popup.root.msg'])
+ self.label_msg.setObjectName('message')
+ self.layout().addWidget(self.label_msg)
+
+ self.input_password = QLineEdit()
+ self.input_password.setObjectName('password')
+ self.layout().addWidget(self.input_password)
+
+ self.label_error = QLabel()
+ self.label_error.setProperty('error', 'true')
+ self.layout().addWidget(self.label_error)
+ self.label_error.hide()
+
+ self.lower_bar = QToolBar()
+ self.layout().addWidget(self.lower_bar)
+
+ self.lower_bar.addWidget(new_spacer())
+ self.bt_ok = QPushButton(i18n['popup.root.continue'])
+ self.bt_ok.setDefault(True)
+ self.bt_ok.setAutoDefault(True)
+ self.bt_ok.setCursor(QCursor(Qt.PointingHandCursor))
+ self.bt_ok.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
+ self.bt_ok.setObjectName('ok')
+ self.bt_ok.clicked.connect(self._validate_password)
+ self.lower_bar.addWidget(self.bt_ok)
+
+ self.bt_cancel = QPushButton()
+ self.bt_cancel.setText(i18n['popup.button.cancel'])
+
+ self.bt_cancel.setCursor(QCursor(Qt.PointingHandCursor))
+ self.bt_cancel.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
+ self.bt_cancel.setObjectName('bt_cancel')
+ self.bt_cancel.clicked.connect(self.close)
+ self.lower_bar.addWidget(self.bt_cancel)
+ self.lower_bar.addWidget(new_spacer())
+
+ self.valid = False
+ self.password = None
+ self.validate_password = ValidatePassword()
+ self.validate_password.signal_valid.connect(self._handle_password_validated)
+
+ def _validate_password(self):
+ password = self.input_password.text().strip()
+
+ if password:
+ self.password = password
+ self.tries += 1
+ self.bt_ok.setEnabled(False)
+ self.bt_cancel.setEnabled(False)
+ self.input_password.setEnabled(False)
+ self.label_error.setText('')
+
+ QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
+ self.validate_password.password = password
+ self.validate_password.start()
+
+ def _handle_password_validated(self, valid: bool):
+ self.valid = valid
+
+ QApplication.restoreOverrideCursor()
+ tries_ended = self.tries == self.max_tries
+
+ if not self.valid:
+ self.label_error.show()
+ self.bt_cancel.setEnabled(True)
+
+ if tries_ended:
+ self.bt_cancel.setText(self.i18n['close'].capitalize())
+ self.label_error.setText(self.i18n['popup.root.bad_password.last_try'])
+ self.bt_cancel.setFocus()
+ else:
+ self.label_error.setText(self.i18n['popup.root.bad_password.body'])
+ self.bt_ok.setEnabled(True)
+ self.input_password.setEnabled(True)
+ self.input_password.setFocus()
+ else:
+ self.close()
+
+ @staticmethod
+ def ask_password(context: ApplicationContext, i18n: I18n, app_config: Optional[dict] = None,
+ comp_manager: Optional[QtComponentsManager] = None, tries: int = 3) -> Tuple[bool, Optional[str]]:
+
+ current_config = read_config() if not app_config else app_config
+
+ store_password = bool(current_config['store_root_password'])
+
+ if store_password and context.root_password and validate_password(context.root_password):
+ return True, context.root_password
+
+ if comp_manager:
+ comp_manager.save_states(state_id=ACTION_ASK_ROOT, only_visible=True)
+ comp_manager.disable_visible()
+
+ diag = RootDialog(i18n=i18n, max_tries=tries)
+ diag.exec()
+ password = diag.password
+
+ if comp_manager:
+ comp_manager.restore_state(ACTION_ASK_ROOT)
+
+ if password is not None and store_password:
+ context.root_password = password
+
+ return (True, password) if diag.valid else (False, None)
+
+
def is_root():
return os.getuid() == 0
-def ask_root_password(context: ApplicationContext, i18n: I18n,
- app_config: dict = None,
- comp_manager: QtComponentsManager = None) -> Tuple[str, bool]:
-
- cur_config = read_config() if not app_config else app_config
- store_password = bool(cur_config['store_root_password'])
-
- if store_password and context.root_password and validate_password(context.root_password):
- return context.root_password, True
-
- diag = QInputDialog(flags=Qt.CustomizeWindowHint | Qt.WindowTitleHint)
- diag.setStyleSheet("""QLineEdit { border-radius: 5px; font-size: 16px; border: 1px solid lightblue }""")
- diag.setInputMode(QInputDialog.TextInput)
- diag.setTextEchoMode(QLineEdit.Password)
- diag.setWindowIcon(util.get_default_icon()[1])
- diag.setWindowTitle(i18n['popup.root.title'])
- diag.setLabelText('')
- diag.setOkButtonText(i18n['popup.root.continue'].capitalize())
- diag.setCancelButtonText(i18n['popup.button.cancel'].capitalize())
-
- bt_box = [c for c in diag.children() if isinstance(c, QDialogButtonBox)][0]
-
- for bt in bt_box.buttons():
- if bt_box.buttonRole(bt) == QDialogButtonBox.AcceptRole:
- bt.setStyleSheet(css.OK_BUTTON)
-
- bt.setCursor(QCursor(Qt.PointingHandCursor))
- bt.setIcon(QIcon())
-
- diag.resize(400, 200)
-
- if comp_manager:
- comp_manager.save_states(state_id=ACTION_ASK_ROOT, only_visible=True)
- comp_manager.disable_visible()
-
- for attempt in range(3):
-
- ok = diag.exec_()
-
- if ok:
- QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
- valid = validate_password(diag.textValue())
- QApplication.restoreOverrideCursor()
-
- if not valid:
- body = i18n['popup.root.bad_password.body']
-
- if attempt == 2:
- body += '. ' + i18n['popup.root.bad_password.last_try']
-
- show_message(title=i18n['popup.root.bad_password.title'],
- body=body,
- type_=MessageType.ERROR)
- ok = False
- diag.setTextValue('')
-
- if ok:
- if store_password:
- context.root_password = diag.textValue()
-
- if comp_manager:
- comp_manager.restore_state(ACTION_ASK_ROOT)
-
- return diag.textValue(), ok
- else:
- break
-
- if comp_manager:
- comp_manager.restore_state(ACTION_ASK_ROOT)
-
- return '', False
-
-
def validate_password(password: str) -> bool:
clean = new_subprocess(['sudo', '-k']).stdout
echo = new_subprocess(['echo', password], stdin=clean).stdout
diff --git a/bauh/view/qt/screenshots.py b/bauh/view/qt/screenshots.py
index 9766e969..9ccba379 100644
--- a/bauh/view/qt/screenshots.py
+++ b/bauh/view/qt/screenshots.py
@@ -4,11 +4,12 @@ from typing import List
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QIcon, QPixmap, QCursor
-from PyQt5.QtWidgets import QDialog, QLabel, QPushButton, QToolBar, QVBoxLayout, QProgressBar, QApplication
+from PyQt5.QtWidgets import QDialog, QLabel, QPushButton, QVBoxLayout, QProgressBar, QApplication, QWidget, \
+ QSizePolicy, QHBoxLayout
from bauh.api.abstract.cache import MemoryCache
from bauh.api.http import HttpClient
-from bauh.view.qt import qt_utils, styles
+from bauh.view.qt import qt_utils
from bauh.view.qt.components import new_spacer
from bauh.view.qt.thread import AnimateProgress
from bauh.view.qt.view_model import PackageView
@@ -17,9 +18,6 @@ from bauh.view.util.translation import I18n
class ScreenshotsDialog(QDialog):
- MAX_HEIGHT = 600
- MAX_WIDTH = 800
-
def __init__(self, pkg: PackageView, http_client: HttpClient, icon_cache: MemoryCache, i18n: I18n, screenshots: List[QPixmap], logger: logging.Logger):
super(ScreenshotsDialog, self).__init__()
self.setWindowTitle(str(pkg))
@@ -27,12 +25,11 @@ class ScreenshotsDialog(QDialog):
self.logger = logger
self.loaded_imgs = []
self.download_threads = []
- self.resize(1280, 720)
self.i18n = i18n
self.http_client = http_client
self.progress_bar = QProgressBar()
+ self.progress_bar.setObjectName('progress_screenshots')
self.progress_bar.setCursor(QCursor(Qt.WaitCursor))
- self.progress_bar.setStyleSheet(styles.PROGRESS_BAR)
self.progress_bar.setMaximumHeight(10 if QApplication.instance().style().objectName().lower() == 'windows' else 6)
self.progress_bar.setTextVisible(False)
self.thread_progress = AnimateProgress()
@@ -50,39 +47,48 @@ class ScreenshotsDialog(QDialog):
self.setWindowIcon(QIcon(pkg.model.get_type_icon_path()))
self.setLayout(QVBoxLayout())
+ self.layout().addWidget(new_spacer())
self.img = QLabel()
+ self.img.setObjectName('image')
self.layout().addWidget(self.img)
+ self.layout().addWidget(new_spacer())
- self.bottom_bar = QToolBar()
+ self.container_buttons = QWidget()
+ self.container_buttons.setObjectName('buttons_container')
+ self.container_buttons.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
+ self.container_buttons.setContentsMargins(0, 0, 0, 0)
+ self.container_buttons.setLayout(QHBoxLayout())
self.bt_back = QPushButton(' < ' + self.i18n['screenshots.bt_back.label'].capitalize())
+ self.bt_back.setObjectName('back')
self.bt_back.setCursor(QCursor(Qt.PointingHandCursor))
self.bt_back.clicked.connect(self.back)
- self.ref_bt_back = self.bottom_bar.addWidget(self.bt_back)
- self.bottom_bar.addWidget(new_spacer(50))
+ self.container_buttons.layout().addWidget(self.bt_back)
+ self.container_buttons.layout().addWidget(new_spacer())
- self.img_label = QLabel()
- self.img_label.setCursor(QCursor(Qt.WaitCursor))
- self.img_label.setStyleSheet('QLabel { font-weight: bold; text-align: center }')
- self.ref_img_label = self.bottom_bar.addWidget(self.img_label)
- self.ref_img_label.setVisible(False)
- self.ref_progress_bar = self.bottom_bar.addWidget(self.progress_bar)
- self.bottom_bar.addWidget(new_spacer(50))
+ self.container_buttons.layout().addWidget(self.progress_bar)
+ self.container_buttons.layout().addWidget(new_spacer())
self.bt_next = QPushButton(self.i18n['screenshots.bt_next.label'].capitalize() + ' > ')
+ self.bt_next.setObjectName('next')
self.bt_next.setCursor(QCursor(Qt.PointingHandCursor))
self.bt_next.clicked.connect(self.next)
- self.ref_bt_next = self.bottom_bar.addWidget(self.bt_next)
+ self.container_buttons.layout().addWidget(self.bt_next)
- self.layout().addWidget(self.bottom_bar)
+ self.layout().addWidget(self.container_buttons)
self.img_idx = 0
+ screen_size = QApplication.primaryScreen().size()
+ self.max_img_width = int(screen_size.width() * 0.585651537)
+ self.max_img_height = int(screen_size.height() * 0.78125)
for idx, s in enumerate(self.screenshots):
t = Thread(target=self._download_img, args=(idx, s), daemon=True)
t.start()
+ self.resize(self.max_img_width, self.max_img_height)
self._load_img()
+ qt_utils.centralize(self)
def _update_progress(self, val: int):
self.progress_bar.setValue(val)
@@ -92,32 +98,28 @@ class ScreenshotsDialog(QDialog):
img = self.loaded_imgs[self.img_idx]
if isinstance(img, QPixmap):
- self.img_label.setText('')
+ self.img.setText('')
self.img.setPixmap(img)
else:
- self.img_label.setText(img)
+ self.img.setText(img)
self.img.setPixmap(QPixmap())
self.img.unsetCursor()
self.thread_progress.stop = True
- self.ref_progress_bar.setVisible(False)
- self.ref_img_label.setVisible(True)
+ self.progress_bar.setVisible(False)
else:
self.img.setPixmap(QPixmap())
self.img.setCursor(QCursor(Qt.WaitCursor))
- self.ref_img_label.setVisible(False)
- self.ref_progress_bar.setVisible(True)
+ self.img.setText('{}...'.format(self.i18n['screenshots.image.loading']))
+ self.progress_bar.setVisible(True)
self.thread_progress.start()
if len(self.screenshots) == 1:
- self.ref_bt_back.setVisible(False)
- self.ref_bt_next.setVisible(False)
+ self.bt_back.setVisible(False)
+ self.bt_next.setVisible(False)
else:
- self.ref_bt_back.setEnabled(self.img_idx != 0)
- self.ref_bt_next.setEnabled(self.img_idx != len(self.screenshots) - 1)
-
- self.adjustSize()
- qt_utils.centralize(self)
+ self.bt_back.setEnabled(self.img_idx != 0)
+ self.bt_next.setEnabled(self.img_idx != len(self.screenshots) - 1)
def _download_img(self, idx: int, url: str):
self.logger.info('Downloading image [{}] from {}'.format(idx, url))
@@ -133,8 +135,8 @@ class ScreenshotsDialog(QDialog):
pixmap = QPixmap()
pixmap.loadFromData(res.content)
- if pixmap.size().height() > self.MAX_HEIGHT or pixmap.size().width() > self.MAX_WIDTH:
- pixmap = pixmap.scaled(self.MAX_WIDTH, self.MAX_HEIGHT, Qt.KeepAspectRatio, Qt.SmoothTransformation)
+ if pixmap.size().height() > self.max_img_height or pixmap.size().width() > self.max_img_width:
+ pixmap = pixmap.scaled(self.max_img_width, self.max_img_height, Qt.KeepAspectRatio, Qt.SmoothTransformation)
self.loaded_imgs.append(pixmap)
diff --git a/bauh/view/qt/settings.py b/bauh/view/qt/settings.py
index d3192b4c..fdf1f9c7 100644
--- a/bauh/view/qt/settings.py
+++ b/bauh/view/qt/settings.py
@@ -1,16 +1,18 @@
import gc
from io import StringIO
+from typing import Optional
from PyQt5.QtCore import QSize, Qt, QCoreApplication
from PyQt5.QtGui import QCursor
-from PyQt5.QtWidgets import QWidget, QVBoxLayout, QToolBar, QSizePolicy, QPushButton
+from PyQt5.QtWidgets import QWidget, QVBoxLayout, QSizePolicy, QPushButton, QHBoxLayout
from bauh import __app_name__
from bauh.api.abstract.controller import SoftwareManager
from bauh.api.abstract.view import MessageType
from bauh.view.core.controller import GenericSoftwareManager
-from bauh.view.qt import dialog, css
+from bauh.view.qt import dialog
from bauh.view.qt.components import to_widget, new_spacer
+from bauh.view.qt.dialog import ConfirmationDialog
from bauh.view.qt.qt_utils import centralize
from bauh.view.util import util
from bauh.view.util.translation import I18n
@@ -18,7 +20,7 @@ from bauh.view.util.translation import I18n
class SettingsWindow(QWidget):
- def __init__(self, manager: SoftwareManager, i18n: I18n, screen_size: QSize, window: QWidget, parent: QWidget = None):
+ def __init__(self, manager: SoftwareManager, i18n: I18n, screen_size: QSize, window: QWidget, parent: Optional[QWidget] = None):
super(SettingsWindow, self).__init__(parent=parent, flags=Qt.CustomizeWindowHint | Qt.WindowTitleHint)
self.setWindowTitle('{} ({})'.format(i18n['settings'].capitalize(), __app_name__))
self.setLayout(QVBoxLayout())
@@ -30,28 +32,34 @@ class SettingsWindow(QWidget):
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))
+ tab_group.setObjectName('settings')
self.layout().addWidget(tab_group)
- action_bar = QToolBar()
- action_bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
+ lower_container = QWidget()
+ lower_container.setObjectName('lower_container')
+ lower_container.setProperty('container', 'true')
+ lower_container.setLayout(QHBoxLayout())
+ lower_container.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
bt_close = QPushButton()
+ bt_close.setObjectName('cancel')
+ bt_close.setAutoDefault(True)
bt_close.setCursor(QCursor(Qt.PointingHandCursor))
bt_close.setText(self.i18n['close'].capitalize())
bt_close.clicked.connect(lambda: self.close())
- action_bar.addWidget(bt_close)
+ lower_container.layout().addWidget(bt_close)
- action_bar.addWidget(new_spacer())
+ lower_container.layout().addWidget(new_spacer())
bt_change = QPushButton()
+ bt_change.setAutoDefault(True)
+ bt_change.setObjectName('ok')
bt_change.setCursor(QCursor(Qt.PointingHandCursor))
- bt_change.setStyleSheet(css.OK_BUTTON)
bt_change.setText(self.i18n['change'].capitalize())
bt_change.clicked.connect(self._save_settings)
- action_bar.addWidget(bt_change)
+ lower_container.layout().addWidget(bt_change)
- self.layout().addWidget(action_bar)
+ self.layout().addWidget(lower_container)
def show(self):
super(SettingsWindow, self).show()
@@ -83,10 +91,10 @@ class SettingsWindow(QWidget):
body=self.i18n['settings.changed.success.warning'],
type_=MessageType.INFO)
QCoreApplication.exit()
- elif dialog.ask_confirmation(title=self.i18n['warning'].capitalize(),
- body="{}
{}
".format(self.i18n['settings.changed.success.warning'],
- self.i18n['settings.changed.success.reboot']),
- i18n=self.i18n):
+ elif ConfirmationDialog(title=self.i18n['warning'].capitalize(),
+ body="{}
{}
".format(self.i18n['settings.changed.success.warning'],
+ self.i18n['settings.changed.success.reboot']),
+ i18n=self.i18n).ask():
self.close()
util.restart_app()
else:
@@ -109,4 +117,4 @@ class SettingsWindow(QWidget):
msg.write('* ' + w + '
')
msg.seek(0)
- dialog.show_message(title="Warning", body=msg.read(), type_=MessageType.WARNING)
+ dialog.show_message(title=self.i18n['warning'].capitalize(), body=msg.read(), type_=MessageType.WARNING)
diff --git a/bauh/view/qt/styles.py b/bauh/view/qt/styles.py
deleted file mode 100644
index a7b01ad8..00000000
--- a/bauh/view/qt/styles.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from bauh.view.qt.colors import GREEN
-
-PROGRESS_BAR = """QProgressBar::chunk { background-color: %s; }""" % GREEN
diff --git a/bauh/view/qt/thread.py b/bauh/view/qt/thread.py
index cec0122d..1537eebe 100644
--- a/bauh/view/qt/thread.py
+++ b/bauh/view/qt/thread.py
@@ -5,11 +5,12 @@ import traceback
from datetime import datetime, timedelta
from io import StringIO
from pathlib import Path
-from typing import List, Type, Set, Tuple
+from typing import List, Type, Set, Tuple, Optional
import requests
from PyQt5.QtCore import QThread, pyqtSignal
from PyQt5.QtGui import QIcon
+from PyQt5.QtWidgets import QWidget
from bauh import LOGS_PATH
from bauh.api.abstract.cache import MemoryCache
@@ -22,7 +23,7 @@ from bauh.api.exception import NoInternetException
from bauh.commons import user
from bauh.commons.html import bold
from bauh.commons.system import get_human_size_str, ProcessHandler, SimpleProcess
-from bauh.view.core import timeshift
+from bauh.view.core import timeshift, config
from bauh.view.core.config import read_config
from bauh.view.qt import commons
from bauh.view.qt.view_model import PackageView, PackageViewStatus
@@ -43,7 +44,7 @@ class AsyncAction(QThread, ProcessWatcher):
signal_root_password = pyqtSignal()
signal_progress_control = pyqtSignal(bool)
- def __init__(self):
+ def __init__(self, root_password: Optional[Tuple[bool, str]] = None):
super(AsyncAction, self).__init__()
self.wait_confirmation = False
self.confirmation_res = None
@@ -62,7 +63,7 @@ class AsyncAction(QThread, ProcessWatcher):
self.wait_user()
return self.confirmation_res
- def request_root_password(self) -> Tuple[str, bool]:
+ def request_root_password(self) -> Tuple[bool, Optional[str]]:
self.wait_confirmation = True
self.signal_root_password.emit()
self.wait_user()
@@ -74,8 +75,8 @@ class AsyncAction(QThread, ProcessWatcher):
self.confirmation_res = res
self.wait_confirmation = False
- def set_root_password(self, password: str, valid: bool):
- self.root_password = (password, valid)
+ def set_root_password(self, valid: bool, password: str):
+ self.root_password = (valid, password)
self.wait_confirmation = False
def wait_user(self):
@@ -286,9 +287,9 @@ class UpgradeSelected(AsyncAction):
return FormComponent(label=lb, components=comps), (required_size, extra_size)
- def _request_password(self) -> Tuple[bool, str]:
+ def _request_password(self) -> Tuple[bool, Optional[str]]:
if not user.is_root():
- pwd, success = self.request_root_password()
+ success, pwd = self.request_root_password()
if not success:
return False, None
@@ -1006,3 +1007,20 @@ class IgnorePackageUpdates(AsyncAction):
finally:
self.pkg = None
+
+
+class SaveTheme(QThread):
+
+ def __init__(self, theme_key: Optional[str], parent: Optional[QWidget] = None):
+ super(SaveTheme, self).__init__(parent=parent)
+ self.theme_key = theme_key
+
+ def run(self):
+ if self.theme_key:
+ core_config = read_config()
+ core_config['ui']['theme'] = self.theme_key
+
+ try:
+ config.save(core_config)
+ except:
+ traceback.print_exc()
diff --git a/bauh/view/qt/window.py b/bauh/view/qt/window.py
index 6823bcc1..ac955c3a 100755
--- a/bauh/view/qt/window.py
+++ b/bauh/view/qt/window.py
@@ -1,16 +1,15 @@
import logging
-import logging
import operator
import time
import traceback
from pathlib import Path
-from typing import List, Type, Set, Tuple
+from typing import List, Type, Set, Tuple, Optional
-from PyQt5.QtCore import QEvent, Qt, QSize, pyqtSignal
+from PyQt5.QtCore import QEvent, Qt, pyqtSignal
from PyQt5.QtGui import QIcon, QWindowStateChangeEvent, QCursor
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QCheckBox, QHeaderView, QToolBar, \
- QLabel, QPlainTextEdit, QLineEdit, QProgressBar, QPushButton, QComboBox, QApplication, QListView, QSizePolicy, \
- QMenu, QAction
+ QLabel, QPlainTextEdit, QProgressBar, QPushButton, QComboBox, QApplication, QListView, QSizePolicy, \
+ QMenu, QHBoxLayout
from bauh import LOGS_PATH
from bauh.api.abstract.cache import MemoryCache
@@ -21,16 +20,20 @@ from bauh.api.abstract.view import MessageType
from bauh.api.http import HttpClient
from bauh.commons import user
from bauh.commons.html import bold
+from bauh.context import set_theme
+from bauh.stylesheet import read_all_themes_metadata, ThemeMetadata
+from bauh.view.core.config import read_config
from bauh.view.core.tray_client import notify_tray
-from bauh.view.qt import dialog, commons, qt_utils, root, styles
+from bauh.view.qt import dialog, commons, qt_utils
from bauh.view.qt.about import AboutDialog
-from bauh.view.qt.apps_table import AppsTable, UpdateToggleButton
-from bauh.view.qt.colors import GREEN
-from bauh.view.qt.components import new_spacer, InputFilter, IconButton, QtComponentsManager
-from bauh.view.qt.confirmation import ConfirmationDialog
+from bauh.view.qt.apps_table import PackagesTable, UpgradeToggleButton
+from bauh.view.qt.commons import sum_updates_displayed
+from bauh.view.qt.components import new_spacer, IconButton, QtComponentsManager, to_widget, QSearchBar, \
+ QCustomMenuAction
+from bauh.view.qt.dialog import ConfirmationDialog
from bauh.view.qt.history import HistoryDialog
from bauh.view.qt.info import InfoDialog
-from bauh.view.qt.root import ask_root_password
+from bauh.view.qt.root import RootDialog
from bauh.view.qt.screenshots import ScreenshotsDialog
from bauh.view.qt.settings import SettingsWindow
from bauh.view.qt.thread import UpgradeSelected, RefreshApps, UninstallPackage, DowngradePackage, ShowPackageInfo, \
@@ -38,7 +41,7 @@ from bauh.view.qt.thread import UpgradeSelected, RefreshApps, UninstallPackage,
ListWarnings, \
AsyncAction, LaunchPackage, ApplyFilters, CustomSoftwareAction, ShowScreenshots, CustomAction, \
NotifyInstalledLoaded, \
- IgnorePackageUpdates
+ IgnorePackageUpdates, SaveTheme
from bauh.view.qt.view_model import PackageView, PackageViewStatus
from bauh.view.util import util, resource
from bauh.view.util.translation import I18n
@@ -46,19 +49,6 @@ from bauh.view.util.translation import I18n
DARK_ORANGE = '#FF4500'
-def toolbar_button_style(bg: str = None, color: str = None):
- style = 'QPushButton { font-weight: 500;'
-
- if bg:
- style += 'background: {};'.format(bg)
-
- if color:
- style += 'color: {};'.format(color)
-
- style += ' }'
- return style
-
-
# action ids
ACTION_APPLY_FILTERS = 1
ACTION_SEARCH = 2
@@ -84,10 +74,11 @@ CHECK_APPS = 7
COMBO_TYPES = 8
COMBO_CATEGORIES = 9
INP_NAME = 10
-CHECK_CONSOLE = 11
+CHECK_DETAILS = 11
BT_SETTINGS = 12
BT_CUSTOM_ACTIONS = 13
BT_ABOUT = 14
+BT_THEMES = 15
# component groups ids
GROUP_FILTERS = 1
@@ -99,13 +90,14 @@ GROUP_LOWER_BTS = 5
class ManageWindow(QWidget):
signal_user_res = pyqtSignal(bool)
- signal_root_password = pyqtSignal(str, bool)
+ signal_root_password = pyqtSignal(bool, str)
signal_table_update = pyqtSignal()
signal_stop_notifying = pyqtSignal()
def __init__(self, i18n: I18n, icon_cache: MemoryCache, manager: SoftwareManager, screen_size, config: dict,
context: ApplicationContext, http_client: HttpClient, logger: logging.Logger, icon: QIcon):
super(ManageWindow, self).__init__()
+ self.setObjectName('manage_window')
self.comp_manager = QtComponentsManager()
self.i18n = i18n
self.logger = logger
@@ -127,95 +119,56 @@ class ManageWindow(QWidget):
self.layout = QVBoxLayout()
self.setLayout(self.layout)
- self.toolbar_top = QToolBar()
- self.toolbar_top.addWidget(new_spacer())
+ self.toolbar_status = QToolBar()
+ self.toolbar_status.setObjectName('toolbar_status')
+ self.toolbar_status.addWidget(new_spacer())
self.label_status = QLabel()
+ self.label_status.setObjectName('label_status')
self.label_status.setText('')
- self.label_status.setStyleSheet("font-weight: bold")
- self.toolbar_top.addWidget(self.label_status)
+ self.toolbar_status.addWidget(self.label_status)
- self.search_bar = QToolBar()
- self.search_bar.setStyleSheet("spacing: 0px;")
- self.search_bar.setContentsMargins(0, 0, 0, 0)
+ self.search_bar = QSearchBar(search_callback=self.search)
+ self.search_bar.set_placeholder(i18n['window_manage.search_bar.placeholder'] + "...")
+ self.search_bar.set_tooltip(i18n['window_manage.search_bar.tooltip'])
+ self.search_bar.set_button_tooltip(i18n['window_manage.search_bar.button_tooltip'])
+ self.comp_manager.register_component(SEARCH_BAR, self.search_bar, self.toolbar_status.addWidget(self.search_bar))
- self.inp_search = QLineEdit()
- self.inp_search.setFrame(False)
- self.inp_search.setPlaceholderText(self.i18n['window_manage.input_search.placeholder'] + "...")
- self.inp_search.setToolTip(self.i18n['window_manage.input_search.tooltip'])
- self.inp_search.setStyleSheet("""QLineEdit {
- color: grey;
- spacing: 0;
- height: 30px;
- font-size: 12px;
- width: 300px;
- border-bottom: 1px solid lightgrey;
- border-top: 1px solid lightgrey;
- }
- """)
- self.inp_search.returnPressed.connect(self.search)
- search_background_color = self.inp_search.palette().color(self.inp_search.backgroundRole()).name()
+ self.toolbar_status.addWidget(new_spacer())
+ self.layout.addWidget(self.toolbar_status)
- label_pre_search = QLabel()
- label_pre_search.setStyleSheet("""
- border-top-left-radius: 5px;
- border-bottom-left-radius: 5px;
- border-left: 1px solid lightgrey;
- border-top: 1px solid lightgrey;
- border-bottom: 1px solid lightgrey;
- background: %s;
- """ % search_background_color)
-
- self.search_bar.addWidget(label_pre_search)
-
- self.search_bar.addWidget(self.inp_search)
-
- label_pos_search = QLabel()
- label_pos_search.setPixmap(QIcon(resource.get_path('img/search.svg')).pixmap(QSize(10, 10)))
- label_pos_search.setStyleSheet("""
- padding-right: 10px;
- border-top-right-radius: 5px;
- border-bottom-right-radius: 5px;
- border-right: 1px solid lightgrey;
- border-top: 1px solid lightgrey;
- border-bottom: 1px solid lightgrey;
- background: %s;
- """ % search_background_color)
-
- self.search_bar.addWidget(label_pos_search)
-
- self.comp_manager.register_component(SEARCH_BAR, self.search_bar, self.toolbar_top.addWidget(self.search_bar))
-
- self.toolbar_top.addWidget(new_spacer())
- self.layout.addWidget(self.toolbar_top)
-
- self.toolbar = QToolBar()
- self.toolbar.setStyleSheet('QToolBar {spacing: 4px; margin-top: 15px;}')
- self.toolbar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
- self.toolbar.setContentsMargins(0, 0, 0, 0)
+ self.toolbar_filters = QWidget()
+ self.toolbar_filters.setObjectName('table_filters')
+ self.toolbar_filters.setLayout(QHBoxLayout())
+ self.toolbar_filters.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
+ self.toolbar_filters.setContentsMargins(0, 0, 0, 0)
self.check_updates = QCheckBox()
+ self.check_updates.setObjectName('check_updates')
self.check_updates.setCursor(QCursor(Qt.PointingHandCursor))
self.check_updates.setText(self.i18n['updates'].capitalize())
self.check_updates.stateChanged.connect(self._handle_updates_filter)
self.check_updates.sizePolicy().setRetainSizeWhenHidden(True)
- self.comp_manager.register_component(CHECK_UPDATES, self.check_updates, self.toolbar.addWidget(self.check_updates))
+ self.toolbar_filters.layout().addWidget(self.check_updates)
+ self.comp_manager.register_component(CHECK_UPDATES, self.check_updates)
self.check_apps = QCheckBox()
+ self.check_apps.setObjectName('check_apps')
self.check_apps.setCursor(QCursor(Qt.PointingHandCursor))
self.check_apps.setText(self.i18n['manage_window.checkbox.only_apps'])
self.check_apps.setChecked(True)
self.check_apps.stateChanged.connect(self._handle_filter_only_apps)
self.check_apps.sizePolicy().setRetainSizeWhenHidden(True)
- self.comp_manager.register_component(CHECK_APPS, self.check_apps, self.toolbar.addWidget(self.check_apps))
+ self.toolbar_filters.layout().addWidget(self.check_apps)
+ self.comp_manager.register_component(CHECK_APPS, self.check_apps)
self.any_type_filter = 'any'
self.cache_type_filter_icons = {}
self.combo_filter_type = QComboBox()
+ self.combo_filter_type.setObjectName('combo_types')
self.combo_filter_type.setCursor(QCursor(Qt.PointingHandCursor))
self.combo_filter_type.setView(QListView())
- self.combo_filter_type.setStyleSheet('QLineEdit { height: 2px; }')
- self.combo_filter_type.setIconSize(QSize(14, 14))
+ self.combo_filter_type.view().setCursor(QCursor(Qt.PointingHandCursor))
self.combo_filter_type.setSizeAdjustPolicy(QComboBox.AdjustToContents)
self.combo_filter_type.setEditable(True)
self.combo_filter_type.lineEdit().setReadOnly(True)
@@ -223,78 +176,73 @@ class ManageWindow(QWidget):
self.combo_filter_type.activated.connect(self._handle_type_filter)
self.combo_filter_type.addItem('--- {} ---'.format(self.i18n['type'].capitalize()), self.any_type_filter)
self.combo_filter_type.sizePolicy().setRetainSizeWhenHidden(True)
- self.comp_manager.register_component(COMBO_TYPES, self.combo_filter_type, self.toolbar.addWidget(self.combo_filter_type))
+ self.toolbar_filters.layout().addWidget(self.combo_filter_type)
+ self.comp_manager.register_component(COMBO_TYPES, self.combo_filter_type)
self.any_category_filter = 'any'
self.combo_categories = QComboBox()
+ self.combo_categories.setObjectName('combo_categories')
self.combo_categories.setCursor(QCursor(Qt.PointingHandCursor))
- self.combo_categories.setStyleSheet('QLineEdit { height: 2px; }')
self.combo_categories.setSizeAdjustPolicy(QComboBox.AdjustToContents)
+ self.combo_categories.view().setCursor(QCursor(Qt.PointingHandCursor))
self.combo_categories.setEditable(True)
self.combo_categories.lineEdit().setReadOnly(True)
self.combo_categories.lineEdit().setAlignment(Qt.AlignCenter)
self.combo_categories.activated.connect(self._handle_category_filter)
self.combo_categories.sizePolicy().setRetainSizeWhenHidden(True)
self.combo_categories.addItem('--- {} ---'.format(self.i18n['category'].capitalize()), self.any_category_filter)
- self.comp_manager.register_component(COMBO_CATEGORIES, self.combo_categories, self.toolbar.addWidget(self.combo_categories))
+ self.toolbar_filters.layout().addWidget(self.combo_categories)
+ self.comp_manager.register_component(COMBO_CATEGORIES, self.combo_categories)
- self.input_name = InputFilter(self.begin_apply_filters)
- self.input_name.setPlaceholderText(self.i18n['manage_window.name_filter.placeholder'] + '...')
- self.input_name.setToolTip(self.i18n['manage_window.name_filter.tooltip'])
- self.input_name.setStyleSheet("QLineEdit { color: grey; }")
- self.input_name.setFixedWidth(130)
+ self.input_name = QSearchBar(search_callback=self.begin_apply_filters)
+ self.input_name.palette().swap(self.combo_categories.palette())
+ self.input_name.setObjectName('name_filter')
+ self.input_name.set_placeholder(self.i18n['manage_window.name_filter.placeholder'] + '...')
+ self.input_name.set_tooltip(self.i18n['manage_window.name_filter.tooltip'])
+ self.input_name.set_button_tooltip(self.i18n['manage_window.name_filter.button_tooltip'])
self.input_name.sizePolicy().setRetainSizeWhenHidden(True)
- self.comp_manager.register_component(INP_NAME, self.input_name, self.toolbar.addWidget(self.input_name))
+ self.toolbar_filters.layout().addWidget(self.input_name)
+ self.comp_manager.register_component(INP_NAME, self.input_name)
- self.toolbar.addWidget(new_spacer())
+ self.toolbar_filters.layout().addWidget(new_spacer())
toolbar_bts = []
- if config['suggestions']['enabled']:
- bt_sugs = QPushButton()
- bt_sugs.setCursor(QCursor(Qt.PointingHandCursor))
- bt_sugs.setToolTip(self.i18n['manage_window.bt.suggestions.tooltip'])
- bt_sugs.setText(self.i18n['manage_window.bt.suggestions.text'].capitalize())
- bt_sugs.setIcon(QIcon(resource.get_path('img/suggestions.svg')))
- bt_sugs.setStyleSheet(toolbar_button_style())
- bt_sugs.clicked.connect(lambda: self._begin_load_suggestions(filter_installed=True))
- bt_sugs.sizePolicy().setRetainSizeWhenHidden(True)
- ref_bt_sugs = self.toolbar.addWidget(bt_sugs)
- toolbar_bts.append(bt_sugs)
- self.comp_manager.register_component(BT_SUGGESTIONS, bt_sugs, ref_bt_sugs)
-
bt_inst = QPushButton()
+ bt_inst.setObjectName('bt_installed')
+ bt_inst.setProperty('root', 'true')
bt_inst.setCursor(QCursor(Qt.PointingHandCursor))
bt_inst.setToolTip(self.i18n['manage_window.bt.installed.tooltip'])
- bt_inst.setIcon(QIcon(resource.get_path('img/disk.svg')))
bt_inst.setText(self.i18n['manage_window.bt.installed.text'].capitalize())
bt_inst.clicked.connect(self._begin_loading_installed)
- bt_inst.setStyleSheet(toolbar_button_style())
bt_inst.sizePolicy().setRetainSizeWhenHidden(True)
toolbar_bts.append(bt_inst)
- self.comp_manager.register_component(BT_INSTALLED, bt_inst, self.toolbar.addWidget(bt_inst))
+ self.toolbar_filters.layout().addWidget(bt_inst)
+ self.comp_manager.register_component(BT_INSTALLED, bt_inst)
bt_ref = QPushButton()
+ bt_ref.setObjectName('bt_refresh')
+ bt_ref.setProperty('root', 'true')
bt_ref.setCursor(QCursor(Qt.PointingHandCursor))
bt_ref.setToolTip(i18n['manage_window.bt.refresh.tooltip'])
- bt_ref.setIcon(QIcon(resource.get_path('img/refresh.svg')))
bt_ref.setText(self.i18n['manage_window.bt.refresh.text'])
- bt_ref.setStyleSheet(toolbar_button_style())
bt_ref.clicked.connect(self.begin_refresh_packages)
bt_ref.sizePolicy().setRetainSizeWhenHidden(True)
toolbar_bts.append(bt_ref)
- self.comp_manager.register_component(BT_REFRESH, bt_ref, self.toolbar.addWidget(bt_ref))
+ self.toolbar_filters.layout().addWidget(bt_ref)
+ self.comp_manager.register_component(BT_REFRESH, bt_ref)
self.bt_upgrade = QPushButton()
+ self.bt_upgrade.setProperty('root', 'true')
+ self.bt_upgrade.setObjectName('bt_upgrade')
self.bt_upgrade.setCursor(QCursor(Qt.PointingHandCursor))
self.bt_upgrade.setToolTip(i18n['manage_window.bt.upgrade.tooltip'])
- self.bt_upgrade.setIcon(QIcon(resource.get_path('img/app_update.svg')))
self.bt_upgrade.setText(i18n['manage_window.bt.upgrade.text'])
- self.bt_upgrade.setStyleSheet(toolbar_button_style(GREEN, 'white'))
self.bt_upgrade.clicked.connect(self.upgrade_selected)
self.bt_upgrade.sizePolicy().setRetainSizeWhenHidden(True)
toolbar_bts.append(self.bt_upgrade)
- self.comp_manager.register_component(BT_UPGRADE, self.bt_upgrade, self.toolbar.addWidget(self.bt_upgrade))
+ self.toolbar_filters.layout().addWidget(self.bt_upgrade)
+ self.comp_manager.register_component(BT_UPGRADE, self.bt_upgrade)
# setting all buttons to the same size:
bt_biggest_size = 0
@@ -308,44 +256,59 @@ class ManageWindow(QWidget):
if bt_biggest_size > bt_width:
bt.setFixedWidth(bt_biggest_size)
- self.layout.addWidget(self.toolbar)
+ self.layout.addWidget(self.toolbar_filters)
self.table_container = QWidget()
+ self.table_container.setObjectName('table_container')
self.table_container.setContentsMargins(0, 0, 0, 0)
self.table_container.setLayout(QVBoxLayout())
self.table_container.layout().setContentsMargins(0, 0, 0, 0)
- self.table_apps = AppsTable(self, self.icon_cache, download_icons=bool(self.config['download']['icons']))
+ self.table_apps = PackagesTable(self, self.icon_cache, download_icons=bool(self.config['download']['icons']))
self.table_apps.change_headers_policy()
self.table_container.layout().addWidget(self.table_apps)
self.layout.addWidget(self.table_container)
- toolbar_console = QToolBar()
+ self.toolbar_console = QWidget()
+ self.toolbar_console.setObjectName('console_toolbar')
+ self.toolbar_console.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
+ self.toolbar_console.setLayout(QHBoxLayout())
+ self.toolbar_console.setContentsMargins(0, 0, 0, 0)
- self.check_console = QCheckBox()
- self.check_console.setCursor(QCursor(Qt.PointingHandCursor))
- self.check_console.setText(self.i18n['manage_window.checkbox.show_details'])
- self.check_console.stateChanged.connect(self._handle_console)
- self.comp_manager.register_component(CHECK_CONSOLE, self.check_console, toolbar_console.addWidget(self.check_console))
+ self.check_details = QCheckBox()
+ self.check_details.setObjectName('check_details')
+ self.check_details.setCursor(QCursor(Qt.PointingHandCursor))
+ self.check_details.setText(self.i18n['manage_window.checkbox.show_details'])
+ self.check_details.stateChanged.connect(self._handle_console)
+ self.toolbar_console.layout().addWidget(self.check_details)
+ self.comp_manager.register_component(CHECK_DETAILS, self.check_details)
- toolbar_console.addWidget(new_spacer())
+ self.toolbar_console.layout().addWidget(new_spacer())
self.label_displayed = QLabel()
- toolbar_console.addWidget(self.label_displayed)
+ self.label_displayed.setObjectName('apps_displayed')
+ self.label_displayed.setCursor(QCursor(Qt.WhatsThisCursor))
+ self.label_displayed.setToolTip(self.i18n['manage_window.label.apps_displayed.tip'])
+ self.toolbar_console.layout().addWidget(self.label_displayed)
+ self.label_displayed.hide()
- self.layout.addWidget(toolbar_console)
+ self.layout.addWidget(self.toolbar_console)
- self.textarea_output = QPlainTextEdit(self)
- self.textarea_output.resize(self.table_apps.size())
- self.textarea_output.setStyleSheet("background: black; color: white;")
- self.layout.addWidget(self.textarea_output)
- self.textarea_output.setVisible(False)
- self.textarea_output.setReadOnly(True)
+ self.textarea_details = QPlainTextEdit(self)
+ self.textarea_details.setObjectName('textarea_details')
+ self.textarea_details.setProperty('console', 'true')
+ self.textarea_details.resize(self.table_apps.size())
+ self.layout.addWidget(self.textarea_details)
+ self.textarea_details.setVisible(False)
+ self.textarea_details.setReadOnly(True)
self.toolbar_substatus = QToolBar()
+ self.toolbar_substatus.setObjectName('toolbar_substatus')
self.toolbar_substatus.addWidget(new_spacer())
+
self.label_substatus = QLabel()
+ self.label_substatus.setObjectName('label_substatus')
self.label_substatus.setCursor(QCursor(Qt.WaitCursor))
self.toolbar_substatus.addWidget(self.label_substatus)
self.toolbar_substatus.addWidget(new_spacer())
@@ -383,43 +346,65 @@ class ManageWindow(QWidget):
self.thread_ignore_updates = IgnorePackageUpdates(manager=self.manager)
self._bind_async_action(self.thread_ignore_updates, finished_call=self.finish_ignore_updates)
- self.toolbar_bottom = QToolBar()
- self.toolbar_bottom.setIconSize(QSize(16, 16))
- self.toolbar_bottom.setStyleSheet('QToolBar { spacing: 3px }')
+ self.container_bottom = QWidget()
+ self.container_bottom.setObjectName('container_bottom')
+ self.container_bottom.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
+ self.container_bottom.setLayout(QHBoxLayout())
+ self.setContentsMargins(0, 0, 0, 0)
+ self.container_bottom.layout().setContentsMargins(0, 0, 0, 0)
- self.toolbar_bottom.addWidget(new_spacer())
+ self.container_bottom.layout().addWidget(new_spacer())
self.progress_bar = QProgressBar()
+ self.progress_bar.setObjectName('progress_manage')
self.progress_bar.setCursor(QCursor(Qt.WaitCursor))
- self.progress_bar.setStyleSheet(styles.PROGRESS_BAR)
self.progress_bar.setMaximumHeight(10 if QApplication.instance().style().objectName().lower() == 'windows' else 4)
self.progress_bar.setTextVisible(False)
- self.ref_progress_bar = self.toolbar_bottom.addWidget(self.progress_bar)
+ self.container_bottom.layout().addWidget(self.progress_bar)
- self.toolbar_bottom.addWidget(new_spacer())
+ self.container_bottom.layout().addWidget(new_spacer())
+
+ if config['suggestions']['enabled']:
+ bt_sugs = IconButton(action=lambda: self._begin_load_suggestions(filter_installed=True),
+ i18n=i18n,
+ tooltip=self.i18n['manage_window.bt.suggestions.tooltip'])
+ bt_sugs.setObjectName('suggestions')
+ self.container_bottom.layout().addWidget(bt_sugs)
+ self.comp_manager.register_component(BT_SUGGESTIONS, bt_sugs)
+
+ bt_themes = IconButton(self.show_themes,
+ i18n=self.i18n,
+ tooltip=self.i18n['manage_window.bt_themes.tip'])
+ bt_themes.setObjectName('themes')
+ self.container_bottom.layout().addWidget(bt_themes)
+ self.comp_manager.register_component(BT_THEMES, bt_themes)
self.custom_actions = manager.get_custom_actions()
- bt_custom_actions = IconButton(QIcon(resource.get_path('img/custom_actions.svg')),
- action=self.show_custom_actions,
+ bt_custom_actions = IconButton(action=self.show_custom_actions,
i18n=self.i18n,
tooltip=self.i18n['manage_window.bt_custom_actions.tip'])
- bt_custom_actions.setVisible(bool(self.custom_actions))
- self.comp_manager.register_component(BT_CUSTOM_ACTIONS, bt_custom_actions, self.toolbar_bottom.addWidget(bt_custom_actions))
+ bt_custom_actions.setObjectName('custom_actions')
- bt_settings = IconButton(QIcon(resource.get_path('img/settings.svg')),
- action=self.show_settings,
+ bt_custom_actions.setVisible(bool(self.custom_actions))
+ self.container_bottom.layout().addWidget(bt_custom_actions)
+ self.comp_manager.register_component(BT_CUSTOM_ACTIONS, bt_custom_actions)
+
+ bt_settings = IconButton(action=self.show_settings,
i18n=self.i18n,
tooltip=self.i18n['manage_window.bt_settings.tooltip'])
- self.comp_manager.register_component(BT_SETTINGS, bt_settings, self.toolbar_bottom.addWidget(bt_settings))
+ bt_settings.setObjectName('settings')
+ self.container_bottom.layout().addWidget(bt_settings)
+ self.comp_manager.register_component(BT_SETTINGS, bt_settings)
- bt_about = IconButton(QIcon(resource.get_path('img/info.svg')),
- action=self._show_about,
+ bt_about = IconButton(action=self._show_about,
i18n=self.i18n,
tooltip=self.i18n['manage_window.settings.about'])
- self.comp_manager.register_component(BT_ABOUT, bt_about, self.toolbar_bottom.addWidget(bt_about))
+ bt_about.setObjectName('about')
+ self.container_bottom.layout().addWidget(bt_about)
+ self.comp_manager.register_component(BT_ABOUT, bt_about)
- self.layout.addWidget(self.toolbar_bottom)
+ self.layout.addWidget(self.container_bottom)
qt_utils.centralize(self)
@@ -442,6 +427,8 @@ class ManageWindow(QWidget):
self.settings_window = None
self.search_performed = False
+ self.thread_save_theme = SaveTheme(theme_key='')
+
self.thread_load_installed = NotifyInstalledLoaded()
self.thread_load_installed.signal_loaded.connect(self._finish_loading_installed)
self.setMinimumHeight(int(screen_size.height() * 0.5))
@@ -457,14 +444,14 @@ class ManageWindow(QWidget):
BT_INSTALLED, BT_SUGGESTIONS) # buttons
self.comp_manager.register_group(GROUP_VIEW_INSTALLED, False,
- BT_SUGGESTIONS, BT_REFRESH, BT_UPGRADE, # buttons
+ BT_REFRESH, BT_UPGRADE, # buttons
*filters)
self.comp_manager.register_group(GROUP_UPPER_BAR, False,
CHECK_APPS, CHECK_UPDATES, COMBO_CATEGORIES, COMBO_TYPES, INP_NAME,
BT_INSTALLED, BT_SUGGESTIONS, BT_REFRESH, BT_UPGRADE)
- self.comp_manager.register_group(GROUP_LOWER_BTS, False, BT_ABOUT, BT_SETTINGS, BT_CUSTOM_ACTIONS)
+ self.comp_manager.register_group(GROUP_LOWER_BTS, False, BT_SUGGESTIONS, BT_THEMES, BT_CUSTOM_ACTIONS, BT_SETTINGS, BT_ABOUT)
def update_custom_actions(self):
self.custom_actions = self.manager.get_custom_actions()
@@ -540,26 +527,27 @@ class ManageWindow(QWidget):
def _ask_confirmation(self, msg: dict):
self.thread_animate_progress.pause()
+ extra_widgets = [to_widget(comp=c, i18n=self.i18n) for c in msg['components']] if msg.get('components') else None
diag = ConfirmationDialog(title=msg['title'],
body=msg['body'],
i18n=self.i18n,
- components=msg['components'],
+ widgets=extra_widgets,
confirmation_label=msg['confirmation_label'],
deny_label=msg['deny_label'],
deny_button=msg['deny_button'],
window_cancel=msg['window_cancel'],
- confirmation_button=msg.get('confirmation_button', True),
- screen_size=self.screen_size)
- res = diag.is_confirmed()
+ confirmation_button=msg.get('confirmation_button', True))
+ diag.ask()
+ res = diag.confirmed
self.thread_animate_progress.animate()
self.signal_user_res.emit(res)
def _pause_and_ask_root_password(self):
self.thread_animate_progress.pause()
- password, valid = root.ask_root_password(self.context, i18n=self.i18n, comp_manager=self.comp_manager)
+ valid, password = RootDialog.ask_password(self.context, i18n=self.i18n, comp_manager=self.comp_manager)
self.thread_animate_progress.animate()
- self.signal_root_password.emit(password, valid)
+ self.signal_root_password.emit(valid, password)
def _show_message(self, msg: dict):
self.thread_animate_progress.pause()
@@ -582,8 +570,8 @@ class ManageWindow(QWidget):
self.thread_warnings.start()
def _begin_loading_installed(self):
- self.inp_search.setText('')
- self.input_name.setText('')
+ self.search_bar.clear()
+ self.input_name.set_text('')
self._begin_action(self.i18n['manage_window.status.installed'])
self._handle_console_option(False)
self.comp_manager.set_components_visible(False)
@@ -666,20 +654,20 @@ class ManageWindow(QWidget):
def _handle_console(self, checked: bool):
if checked:
- self.textarea_output.show()
+ self.textarea_details.show()
else:
- self.textarea_output.hide()
+ self.textarea_details.hide()
def _handle_console_option(self, enable: bool):
if enable:
- self.textarea_output.clear()
+ self.textarea_details.clear()
- self.comp_manager.set_component_visible(CHECK_CONSOLE, enable)
- self.check_console.setChecked(False)
- self.textarea_output.hide()
+ self.comp_manager.set_component_visible(CHECK_DETAILS, enable)
+ self.check_details.setChecked(False)
+ self.textarea_details.hide()
def begin_refresh_packages(self, pkg_types: Set[Type[SoftwarePackage]] = None):
- self.inp_search.clear()
+ self.search_bar.clear()
self._begin_action(self.i18n['manage_window.status.refreshing'])
self.comp_manager.set_components_visible(False)
@@ -710,7 +698,7 @@ class ManageWindow(QWidget):
self.types_changed = False
def _begin_load_suggestions(self, filter_installed: bool):
- self.inp_search.clear()
+ self.search_bar.clear()
self._begin_action(self.i18n['manage_window.status.suggestions'])
self._handle_console_option(False)
self.comp_manager.set_components_visible(False)
@@ -825,15 +813,21 @@ class ManageWindow(QWidget):
def _update_table(self, pkgs_info: dict, signal: bool = False):
self.pkgs = pkgs_info['pkgs_displayed']
- self.table_apps.update_packages(self.pkgs, update_check_enabled=pkgs_info['not_installed'] == 0)
+ if pkgs_info['not_installed'] == 0:
+ update_check = sum_updates_displayed(pkgs_info) > 0
+ else:
+ update_check = False
+
+ self.table_apps.update_packages(self.pkgs, update_check_enabled=update_check)
if not self._maximized:
+ self.label_displayed.show()
self.table_apps.change_headers_policy(QHeaderView.Stretch)
self.table_apps.change_headers_policy()
self._resize(accept_lower_width=len(self.pkgs) > 0)
self.label_displayed.setText('{} / {}'.format(len(self.pkgs), len(self.pkgs_available)))
else:
- self.label_displayed.setText('')
+ self.label_displayed.hide()
if signal:
self.signal_table_update.emit()
@@ -887,12 +881,12 @@ class ManageWindow(QWidget):
'type': self.type_filter,
'category': self.category_filter,
'updates': False if ignore_updates else self.filter_updates,
- 'name': self.input_name.get_text().lower() if self.input_name.get_text() else None,
+ 'name': self.input_name.text().lower() if self.input_name.text() else None,
'display_limit': None if self.filter_updates else self.display_limit
}
def update_pkgs(self, new_pkgs: List[SoftwarePackage], as_installed: bool, types: Set[type] = None, ignore_updates: bool = False, keep_filters: bool = False) -> bool:
- self.input_name.setText('')
+ self.input_name.set_text('')
pkgs_info = commons.new_pkgs_info()
filters = self._gen_filters(ignore_updates=ignore_updates)
@@ -1053,8 +1047,8 @@ class ManageWindow(QWidget):
def _resize(self, accept_lower_width: bool = True):
table_width = self.table_apps.get_width()
- toolbar_width = self.toolbar.sizeHint().width()
- topbar_width = self.toolbar_top.sizeHint().width()
+ toolbar_width = self.toolbar_filters.sizeHint().width()
+ topbar_width = self.toolbar_status.sizeHint().width()
new_width = max(table_width, toolbar_width, topbar_width)
new_width *= 1.05 # this extra size is not because of the toolbar button, but the table upgrade buttons
@@ -1066,13 +1060,14 @@ class ManageWindow(QWidget):
self.progress_controll_enabled = enabled
def upgrade_selected(self):
- if dialog.ask_confirmation(title=self.i18n['manage_window.upgrade_all.popup.title'],
- body=self.i18n['manage_window.upgrade_all.popup.body'],
- i18n=self.i18n,
- widgets=[UpdateToggleButton(pkg=None,
- root=self,
- i18n=self.i18n,
- clickable=False)]):
+ body = QWidget()
+ body.setLayout(QHBoxLayout())
+ body.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Preferred)
+ body.layout().addWidget(QLabel(self.i18n['manage_window.upgrade_all.popup.body']))
+ body.layout().addWidget(UpgradeToggleButton(pkg=None, root=self, i18n=self.i18n, clickable=False))
+ if ConfirmationDialog(title=self.i18n['manage_window.upgrade_all.popup.title'],
+ i18n=self.i18n, body=None,
+ widgets=[body]).ask():
self._begin_action(action_label=self.i18n['manage_window.status.upgrading'],
action_id=ACTION_UPGRADE)
@@ -1085,7 +1080,7 @@ class ManageWindow(QWidget):
self._finish_action()
if res.get('id'):
- output = self.textarea_output.toPlainText()
+ output = self.textarea_details.toPlainText()
if output:
try:
@@ -1094,8 +1089,8 @@ class ManageWindow(QWidget):
with open(logs_path, 'w+') as f:
f.write(output)
- self.textarea_output.appendPlainText('\n*Upgrade summary generated at: {}'.format(UpgradeSelected.SUMMARY_FILE.format(res['id'])))
- self.textarea_output.appendPlainText('*Upgrade logs generated at: {}'.format(logs_path))
+ self.textarea_details.appendPlainText('\n*Upgrade summary generated at: {}'.format(UpgradeSelected.SUMMARY_FILE.format(res['id'])))
+ self.textarea_details.appendPlainText('*Upgrade logs generated at: {}'.format(logs_path))
except:
traceback.print_exc()
@@ -1118,19 +1113,19 @@ class ManageWindow(QWidget):
self.update_custom_actions()
def _show_console_errors(self):
- if self.textarea_output.toPlainText():
- self.check_console.setChecked(True)
+ if self.textarea_details.toPlainText():
+ self.check_details.setChecked(True)
else:
self._handle_console_option(False)
- self.comp_manager.set_component_visible(CHECK_CONSOLE, False)
+ self.comp_manager.set_component_visible(CHECK_DETAILS, False)
def _update_action_output(self, output: str):
- self.textarea_output.appendPlainText(output)
+ self.textarea_details.appendPlainText(output)
def _begin_action(self, action_label: str, action_id: int = None):
self.thread_animate_progress.stop = False
self.thread_animate_progress.start()
- self.ref_progress_bar.setVisible(True)
+ self.progress_bar.setVisible(True)
if action_id is not None:
self.comp_manager.save_states(action_id, only_visible=True)
@@ -1149,7 +1144,7 @@ class ManageWindow(QWidget):
self.thread_animate_progress.stop = True
self.thread_animate_progress.wait(msecs=1000)
- self.ref_progress_bar.setVisible(False)
+ self.progress_bar.setVisible(False)
self.progress_bar.setValue(0)
self.progress_bar.setTextVisible(False)
@@ -1246,8 +1241,8 @@ class ManageWindow(QWidget):
if res.get('error'):
self._handle_console_option(True)
- self.textarea_output.appendPlainText(res['error'])
- self.check_console.setChecked(True)
+ self.textarea_details.appendPlainText(res['error'])
+ self.check_details.setChecked(True)
elif not res['history'].history:
dialog.show_message(title=self.i18n['action.history.no_history.title'],
body=self.i18n['action.history.no_history.body'].format(bold(res['history'].pkg.name)),
@@ -1261,7 +1256,7 @@ class ManageWindow(QWidget):
self._begin_action('{} {}'.format(self.i18n['manage_window.status.searching'], word if word else ''), action_id=action_id)
def search(self):
- word = self.inp_search.text().strip()
+ word = self.search_bar.text().strip()
if word:
self._handle_console(False)
self._begin_search(word, action_id=ACTION_SEARCH)
@@ -1284,13 +1279,13 @@ class ManageWindow(QWidget):
self.comp_manager.restore_state(ACTION_SEARCH)
dialog.show_message(title=self.i18n['warning'].capitalize(), body=self.i18n[res['error']], type_=MessageType.WARNING)
- def _ask_root_password(self, action: str, pkg: PackageView) -> Tuple[str, bool]:
+ def _ask_root_password(self, action: str, pkg: PackageView) -> Tuple[Optional[str], bool]:
pwd = None
requires_root = self.manager.requires_root(action, pkg.model)
if not user.is_root() and requires_root:
- pwd, ok = ask_root_password(self.context, i18n=self.i18n, comp_manager=self.comp_manager)
- if not ok:
+ valid, pwd = RootDialog.ask_password(self.context, i18n=self.i18n, comp_manager=self.comp_manager)
+ if not valid:
return pwd, False
return pwd, True
@@ -1312,7 +1307,7 @@ class ManageWindow(QWidget):
def _finish_install(self, res: dict):
self._finish_action(action_id=ACTION_INSTALL)
- console_output = self.textarea_output.toPlainText()
+ console_output = self.textarea_details.toPlainText()
if console_output:
log_path = '{}/install/{}/{}'.format(LOGS_PATH, res['pkg'].model.get_type(), res['pkg'].model.name)
@@ -1323,9 +1318,9 @@ class ManageWindow(QWidget):
with open(log_file, 'w+') as f:
f.write(console_output)
- self.textarea_output.appendPlainText(self.i18n['console.install_logs.path'].format('"{}"'.format(log_file)))
+ self.textarea_details.appendPlainText(self.i18n['console.install_logs.path'].format('"{}"'.format(log_file)))
except:
- self.textarea_output.appendPlainText("[warning] Could not write install log file to '{}'".format(log_path))
+ self.textarea_details.appendPlainText("[warning] Could not write install log file to '{}'".format(log_path))
if res['success']:
if self._can_notify_user():
@@ -1385,6 +1380,9 @@ class ManageWindow(QWidget):
self.pkgs_installed.insert(idx, PackageView(model, self.i18n))
self.update_custom_actions()
+ self.table_apps.change_headers_policy(policy=QHeaderView.Stretch, maximized=self._maximized)
+ self.table_apps.change_headers_policy(policy=QHeaderView.ResizeToContents, maximized=self._maximized)
+ self._resize(accept_lower_width=False)
else:
self._show_console_errors()
if self._can_notify_user():
@@ -1393,19 +1391,19 @@ class ManageWindow(QWidget):
def _update_progress(self, value: int):
self.progress_bar.setValue(value)
- def begin_execute_custom_action(self, pkg: PackageView, action: CustomSoftwareAction):
- if pkg is None and not dialog.ask_confirmation(title=self.i18n['confirmation'].capitalize(),
- body=self.i18n['custom_action.proceed_with'].capitalize().format('"{}"'.format(self.i18n[action.i18n_label_key])),
- icon=QIcon(action.icon_path) if action.icon_path else QIcon(resource.get_path('img/logo.svg')),
- i18n=self.i18n):
+ def begin_execute_custom_action(self, pkg: Optional[PackageView], action: CustomSoftwareAction):
+ if pkg is None and not ConfirmationDialog(title=self.i18n['confirmation'].capitalize(),
+ body='{}
'.format(self.i18n['custom_action.proceed_with'].capitalize().format(bold(self.i18n[action.i18n_label_key]))),
+ icon=QIcon(action.icon_path) if action.icon_path else QIcon(resource.get_path('img/logo.svg')),
+ i18n=self.i18n).ask():
return False
pwd = None
if not user.is_root() and action.requires_root:
- pwd, ok = ask_root_password(self.context, i18n=self.i18n, comp_manager=self.comp_manager)
+ valid, pwd = RootDialog.ask_password(self.context, i18n=self.i18n, comp_manager=self.comp_manager)
- if not ok:
+ if not valid:
return
self._begin_action(action_label='{}{}'.format(self.i18n[action.i18n_status_key], ' {}'.format(pkg.model.name) if pkg else ''),
@@ -1434,10 +1432,10 @@ class ManageWindow(QWidget):
self._show_console_errors()
def _show_console_checkbox_if_output(self):
- if self.textarea_output.toPlainText():
- self.comp_manager.set_component_visible(CHECK_CONSOLE, True)
+ if self.textarea_details.toPlainText():
+ self.comp_manager.set_component_visible(CHECK_DETAILS, True)
else:
- self.comp_manager.set_component_visible(CHECK_CONSOLE, False)
+ self.comp_manager.set_component_visible(CHECK_DETAILS, False)
def show_settings(self):
if self.settings_window:
@@ -1450,8 +1448,7 @@ class ManageWindow(QWidget):
qt_utils.centralize(self.settings_window)
self.settings_window.show()
- def _map_custom_action(self, action: CustomSoftwareAction) -> QAction:
- custom_action = QAction(self.i18n[action.i18n_label_key])
+ def _map_custom_action(self, action: CustomSoftwareAction, parent: QWidget) -> QCustomMenuAction:
if action.icon_path:
try:
@@ -1459,20 +1456,21 @@ class ManageWindow(QWidget):
icon = QIcon(action.icon_path)
else:
icon = QIcon.fromTheme(action.icon_path)
-
- custom_action.setIcon(icon)
-
except:
- pass
+ icon = None
+ else:
+ icon = None
- custom_action.triggered.connect(lambda: self.begin_execute_custom_action(None, action))
- return custom_action
+ return QCustomMenuAction(parent=parent,
+ label=self.i18n[action.i18n_label_key],
+ action=lambda: self.begin_execute_custom_action(None, action),
+ icon=icon)
def show_custom_actions(self):
if self.custom_actions:
menu_row = QMenu()
menu_row.setCursor(QCursor(Qt.PointingHandCursor))
- actions = [self._map_custom_action(a) for a in self.custom_actions]
+ actions = [self._map_custom_action(a, menu_row) for a in self.custom_actions]
menu_row.addActions(actions)
menu_row.adjustSize()
menu_row.popup(QCursor.pos())
@@ -1547,3 +1545,48 @@ class ManageWindow(QWidget):
self.__add_category(cat)
else:
self._update_categories(pkg_categories)
+
+ def _map_theme_action(self, theme: ThemeMetadata, menu: QMenu) -> QCustomMenuAction:
+ def _change_theme():
+ set_theme(theme_key=theme.key, app=QApplication.instance(), logger=self.context.logger)
+ self.thread_save_theme.theme_key = theme.key
+ self.thread_save_theme.start()
+
+ return QCustomMenuAction(label=theme.get_i18n_name(self.i18n),
+ action=_change_theme,
+ parent=menu,
+ tooltip=theme.get_i18n_description(self.i18n))
+
+ def show_themes(self):
+ menu_row = QMenu()
+ menu_row.setCursor(QCursor(Qt.PointingHandCursor))
+ menu_row.addActions(self._map_theme_actions(menu_row))
+ menu_row.adjustSize()
+ menu_row.popup(QCursor.pos())
+ menu_row.exec_()
+
+ def _map_theme_actions(self, menu: QMenu) -> List[QCustomMenuAction]:
+ core_config = read_config()
+
+ current_theme_key, current_action = core_config['ui']['theme'], None
+
+ actions = []
+
+ for t in read_all_themes_metadata():
+ if not t.abstract:
+ action = self._map_theme_action(t, menu)
+
+ if current_action is None and current_theme_key is not None and current_theme_key == t.key:
+ action.button.setProperty('current', 'true')
+ current_action = action
+ else:
+ actions.append(action)
+
+ if not current_action:
+ invalid_action = QCustomMenuAction(label=self.i18n['manage_window.bt_themes.option.invalid'], parent=menu)
+ invalid_action.button.setProperty('current', 'true')
+ current_action = invalid_action
+
+ actions.sort(key=lambda a: a.get_label())
+ actions.insert(0, current_action)
+ return actions
diff --git a/bauh/view/resources/img/app_actions.svg b/bauh/view/resources/img/app_actions.svg
deleted file mode 100644
index 41844bb8..00000000
--- a/bauh/view/resources/img/app_actions.svg
+++ /dev/null
@@ -1,62 +0,0 @@
-
-
-
-
-
- image/svg+xml
-
-
-
-
-
-
-
-
-
diff --git a/bauh/view/resources/img/clean.svg b/bauh/view/resources/img/clean.svg
deleted file mode 100644
index 38a92d8f..00000000
--- a/bauh/view/resources/img/clean.svg
+++ /dev/null
@@ -1,124 +0,0 @@
-
-
-
-image/svg+xml
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/bauh/view/resources/img/custom_actions.svg b/bauh/view/resources/img/custom_actions.svg
deleted file mode 100644
index 1dde019b..00000000
--- a/bauh/view/resources/img/custom_actions.svg
+++ /dev/null
@@ -1,62 +0,0 @@
-
-
-
-
-
- image/svg+xml
-
-
-
-
-
-
-
-
-
diff --git a/bauh/view/resources/img/disk.svg b/bauh/view/resources/img/disk.svg
deleted file mode 100755
index 7a5633ec..00000000
--- a/bauh/view/resources/img/disk.svg
+++ /dev/null
@@ -1,130 +0,0 @@
-
-
-
-image/svg+xml
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/bauh/view/resources/img/history.svg b/bauh/view/resources/img/history.svg
deleted file mode 100755
index 798d4c22..00000000
--- a/bauh/view/resources/img/history.svg
+++ /dev/null
@@ -1,216 +0,0 @@
-
-
-
-image/svg+xml
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/bauh/view/resources/img/ignore_update.svg b/bauh/view/resources/img/ignore_update.svg
deleted file mode 100644
index 804dafb4..00000000
--- a/bauh/view/resources/img/ignore_update.svg
+++ /dev/null
@@ -1,134 +0,0 @@
-
-image/svg+xml
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/bauh/view/resources/img/install.svg b/bauh/view/resources/img/install.svg
deleted file mode 100755
index 25a66cab..00000000
--- a/bauh/view/resources/img/install.svg
+++ /dev/null
@@ -1,120 +0,0 @@
-
-
-
-image/svg+xml
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/bauh/view/resources/img/lock.svg b/bauh/view/resources/img/lock.svg
deleted file mode 100644
index 4aacce30..00000000
--- a/bauh/view/resources/img/lock.svg
+++ /dev/null
@@ -1,127 +0,0 @@
-
-
-
-image/svg+xml
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/bauh/view/resources/img/red_cross.svg b/bauh/view/resources/img/red_cross.svg
deleted file mode 100755
index 28054ffc..00000000
--- a/bauh/view/resources/img/red_cross.svg
+++ /dev/null
@@ -1,115 +0,0 @@
-
-
-
-image/svg+xml
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/bauh/view/resources/img/settings.svg b/bauh/view/resources/img/settings.svg
deleted file mode 100644
index 87a157d5..00000000
--- a/bauh/view/resources/img/settings.svg
+++ /dev/null
@@ -1,133 +0,0 @@
-
-
-
-image/svg+xml
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/bauh/view/resources/img/suggestions.svg b/bauh/view/resources/img/suggestions.svg
deleted file mode 100644
index 63c1f185..00000000
--- a/bauh/view/resources/img/suggestions.svg
+++ /dev/null
@@ -1,160 +0,0 @@
-
-
-
-image/svg+xml
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/bauh/view/resources/img/tools.svg b/bauh/view/resources/img/tools.svg
deleted file mode 100644
index 1f501bae..00000000
--- a/bauh/view/resources/img/tools.svg
+++ /dev/null
@@ -1,97 +0,0 @@
-
-
-
-
-
- image/svg+xml
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/bauh/view/resources/img/uninstall.svg b/bauh/view/resources/img/uninstall.svg
deleted file mode 100755
index b228fbcd..00000000
--- a/bauh/view/resources/img/uninstall.svg
+++ /dev/null
@@ -1,96 +0,0 @@
-
-
-
-
-
- image/svg+xml
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/bauh/view/resources/locale/ca b/bauh/view/resources/locale/ca
index c16279f1..473107cb 100644
--- a/bauh/view/resources/locale/ca
+++ b/bauh/view/resources/locale/ca
@@ -230,10 +230,12 @@ core.config.ui.auto_scale.tip=It activates the auto screen scale factor ( {} ).
core.config.ui.hdpi=HDPI
core.config.ui.hdpi.tip=If improvements related to HDPI resolutions should be activated
core.config.ui.max_displayed=Applications displayed
-core.config.ui.max_displayed.tip=Maximum number of applications displayed on the table
+core.config.ui.max_displayed.tip=Maximum number of applications displayed on the table. Use 0 for no limit.
+core.config.ui.qt_style.tooltip=QT style to be used. Requires restarting to take effect.
core.config.ui.scale_factor=Scale
core.config.ui.scale_factor.tip=It defines the interface display scaling factor (Qt). Raise the value to raise the interface size. Restart required.
-core.config.ui.style.default=Default
+core.config.ui.system_theme=System theme
+core.config.ui.system_theme.tip=If the system's theme/stylesheet should be merged with {app}'s
core.config.ui.tray.default_icon=Default icon
core.config.ui.tray.default_icon.tip=The default icon for {app} displayed on the tray
core.config.ui.tray.updates_icon=Update icon
@@ -298,20 +300,23 @@ manage_window.bt.installed.text=instal·lada
manage_window.bt.installed.tooltip=Feu clic aquí per a mostrar les aplicacions instal·lades
manage_window.bt.refresh.text=Refresca
manage_window.bt.refresh.tooltip=Carrega novament les dades quant a les aplicacions instal·lades
-manage_window.bt.suggestions.text=Suggeriments
manage_window.bt.suggestions.tooltip=Feu clic aquí per a mostrar alguns suggeriments d’aplicacions
manage_window.bt.upgrade.text=Actualitza
manage_window.bt.upgrade.tooltip=Actualitza totes les aplicacions marcades
manage_window.bt_custom_actions.tip=Click here to show more actions
manage_window.bt_settings.tooltip=Feu clic aquí per mostrar la configuració
+manage_window.bt_themes.tip=Click here to choose a theme
+manage_window.bt_themes.option.invalid=Invalid
manage_window.checkbox.only_apps=Aplicacions
manage_window.checkbox.show_details=Mostra detalls
manage_window.columns.installed=Installed
manage_window.columns.latest_version=Versió més recent
manage_window.columns.update=Voleu actualitzar?
+manage_window.label.apps_displayed.tip=Applications displayed / available
manage_window.label.updates=Actualitzacions
-manage_window.name_filter.placeholder=Filtra per nom
-manage_window.name_filter.tooltip=Escriviu aquí per a filtrar aplicacions per nom
+manage_window.name_filter.button_tooltip=Click here to filter apps by name
+manage_window.name_filter.placeholder=Nom
+manage_window.name_filter.tooltip=Type and press ENTER to filter apps by name
manage_window.settings.about=Quant a
manage_window.status.downgrading=S’està revertint
manage_window.status.filtering=S’està filtrant
@@ -352,9 +357,9 @@ popup.history.selected.tooltip=Versió actual
popup.history.title=Historial
popup.root.bad_password.body=La contrasenya és incorrecta
popup.root.bad_password.last_try=S’han acabat els intents. S’ha cancel·lat l’operació.
-popup.root.bad_password.title=Error
-popup.root.continue=continuar
-popup.root.title=Requireix la vostra contrasenya per a continuar
+popup.root.continue=Continuar
+popup.root.msg=Requireix la vostra contrasenya per a continuar
+popup.root.title=Autenticació
popup.screenshots.no_screenshot.body=no s’han pogut recuperar captures de pantalla del {}
prepare.bt_hide_details=Hide details
prepare.bt_icon.no_output=There are no progress details for this task to be displayed
@@ -369,8 +374,7 @@ repository=dipòsit
screenshots.bt_back.label=anterior
screenshots.bt_next.label=següent
screenshots.download.no_content=No hi ha contingut a mostrar
-screenshots.download.no_response=No s’ha trobat la imatge
-screenshots.download.running=s’està baixant la imatge
+screenshots.image.loading=Loading
settings=configuració
settings.changed.success.reboot=Restart now ?
settings.changed.success.warning=Settings successfully changed. Some of them will only take effect after a restart.
@@ -403,6 +407,7 @@ view.components.file_chooser.placeholder=Feu clic aquí per seleccionar
warning=avís
warning.update_available=There is a new {} version available ({}). Check out the news at {}.
welcome=us donem la benvinguda
-window_manage.input_search.placeholder=Cerca
-window_manage.input_search.tooltip=Escriviu i premeu Intro per a cercar aplicacions
+window_manage.search_bar.button_tooltip=Click here to search for applications
+window_manage.search_bar.placeholder=Cerca
+window_manage.search_bar.tooltip=Escriviu i premeu Intro per a cercar aplicacions
yes=sí
\ No newline at end of file
diff --git a/bauh/view/resources/locale/de b/bauh/view/resources/locale/de
index 075b306f..28240714 100644
--- a/bauh/view/resources/locale/de
+++ b/bauh/view/resources/locale/de
@@ -229,10 +229,12 @@ core.config.ui.auto_scale.tip=Aktiviert den automatischen Skalierungsfaktor ({})
core.config.ui.hdpi=HiDPI
core.config.ui.hdpi.tip=Falls aktiviert werden Optimierungen für hochauflösende Displays vorgenommen
core.config.ui.max_displayed=Anzahl der Anwendungen
-core.config.ui.max_displayed.tip=Maximale Anzahl von Anwendungen, die in der Tabelle angezeigt werden
+core.config.ui.max_displayed.tip=Maximale Anzahl von Anwendungen, die in der Tabelle angezeigt werden. Use 0 for no limit.
+core.config.ui.qt_style.tooltip=QT style to be used. Requires restarting to take effect.
core.config.ui.scale_factor=Scale
core.config.ui.scale_factor.tip=It defines the interface display scaling factor (Qt). Raise the value to raise the interface size. Restart required.
-core.config.ui.style.default=Default
+core.config.ui.system_theme=System theme
+core.config.ui.system_theme.tip=If the system's theme/stylesheet should be merged with {app}'s
core.config.ui.tray.default_icon=Anwendungs-Icon
core.config.ui.tray.default_icon.tip=The default icon for {app} displayed on the tray
core.config.ui.tray.updates_icon=Icon bei Updates
@@ -297,20 +299,23 @@ manage_window.bt.installed.text=installiert
manage_window.bt.installed.tooltip=Installierte Anwendungen anzeigen
manage_window.bt.refresh.text=Aktualisieren
manage_window.bt.refresh.tooltip=Informationen über installierte Anwendungen aktualisieren
-manage_window.bt.suggestions.text=Vorschläge
manage_window.bt.suggestions.tooltip=Vorschläge für Apps anzeigen
manage_window.bt.upgrade.text=Upgraden
manage_window.bt.upgrade.tooltip=Alle ausgewählten Anwendungen upgraden
manage_window.bt_custom_actions.tip=Click here to show more actions
manage_window.bt_settings.tooltip=Einstellungen anpassen
+manage_window.bt_themes.tip=Click here to choose a theme
+manage_window.bt_themes.option.invalid=Invalid
manage_window.checkbox.only_apps=Apps
manage_window.checkbox.show_details=Details anzeigen
manage_window.columns.installed=Installed
manage_window.columns.latest_version=Neueste Version
manage_window.columns.update=Upgraden?
+manage_window.label.apps_displayed.tip=Applications displayed / available
manage_window.label.updates=Updates
-manage_window.name_filter.placeholder=Nach Name filtern
-manage_window.name_filter.tooltip=Apps nach Name filtern
+manage_window.name_filter.button_tooltip=Click here to filter apps by name
+manage_window.name_filter.placeholder=Name
+manage_window.name_filter.tooltip=Type and press ENTER to filter apps by name
manage_window.settings.about=Über
manage_window.status.downgrading=Downgraden
manage_window.status.filtering=Filtern
@@ -351,9 +356,9 @@ popup.history.selected.tooltip=Aktuelle Version
popup.history.title=Verlauf
popup.root.bad_password.body=Ungültiges Passwort
popup.root.bad_password.last_try=Zu viele Fehlversuche. Aktion abgebrochen.
-popup.root.bad_password.title=Fehler
popup.root.continue=Fortfahren
-popup.root.title=Zum Fortfahren wird das Passwort benötigt.
+popup.root.msg=Zum Fortfahren wird das Passwort benötigt.
+popup.root.title=Authentifizierung
popup.screenshots.no_screenshot.body={} Screenshots konnten nicht heruntergeladen werden.
prepare.bt_hide_details=Hide details
prepare.bt_icon.no_output=There are no progress details for this task to be displayed
@@ -369,7 +374,7 @@ screenshots.bt_back.label=Zurück
screenshots.bt_next.label=Fortfahren
screenshots.download.no_content=Kein Inhalt
screenshots.download.no_response=Screenshot nicht gefunden
-screenshots.download.running=Screenshot herunterladen
+screenshots.image.loading=Loading
settings=Einstellungen
settings.changed.success.reboot=Anwendung neustarten?
settings.changed.success.warning=Einstellungen gespeichert. Einige werden erst beim nächsten Anwedungsstart sichtbar.
@@ -402,6 +407,7 @@ view.components.file_chooser.placeholder=Auswählen
warning=Warnung
warning.update_available=There is a new {} version available ({}). Check out the news at {}.
welcome=Willkommen
-window_manage.input_search.placeholder=Suchbegriff
-window_manage.input_search.tooltip=Suchbegriff eingeben und Enter drücken, um zu suchen
+window_manage.search_bar.button_tooltip=Click here to search for applications
+window_manage.search_bar.placeholder=Suchbegriff
+window_manage.search_bar.tooltip=Suchbegriff eingeben und Enter drücken, um zu suchen
yes=Ja
\ No newline at end of file
diff --git a/bauh/view/resources/locale/en b/bauh/view/resources/locale/en
index 257612a0..dfc83250 100644
--- a/bauh/view/resources/locale/en
+++ b/bauh/view/resources/locale/en
@@ -228,11 +228,13 @@ core.config.ui.auto_scale.tip=It activates the auto screen scale factor ( {} ).
core.config.ui.auto_scale=Auto scale
core.config.ui.hdpi.tip=If improvements related to HDPI resolutions should be activated
core.config.ui.hdpi=HDPI
-core.config.ui.max_displayed.tip=Maximum number of applications displayed on the table
+core.config.ui.max_displayed.tip=Maximum number of applications displayed on the table. Use 0 for no limit.
core.config.ui.max_displayed=Applications displayed
+core.config.ui.qt_style.tooltip=QT style to be used. Requires restarting to take effect.
core.config.ui.scale_factor=Scale
core.config.ui.scale_factor.tip=It defines the interface display scaling factor (Qt). Raise the value to raise the interface size. Restart required.
-core.config.ui.style.default=Default
+core.config.ui.system_theme=System theme
+core.config.ui.system_theme.tip=If the system's theme/stylesheet should be merged with {app}'s
core.config.ui.tray.default_icon.tip=The default icon for {app} displayed on the tray
core.config.ui.tray.default_icon=Default icon
core.config.ui.tray.updates_icon.tip=The displayed icon when there are updates available
@@ -298,20 +300,23 @@ manage_window.bt.installed.text=installed
manage_window.bt.installed.tooltip=Click here to show the installed applications
manage_window.bt.refresh.text=Refresh
manage_window.bt.refresh.tooltip=Reload the data about installed applications
-manage_window.bt.suggestions.text=Suggestions
manage_window.bt.suggestions.tooltip=Click here to show some applications suggestions
manage_window.bt.upgrade.text=Upgrade
manage_window.bt.upgrade.tooltip=Upgrade all checked applications
manage_window.bt_custom_actions.tip=Click here to show more actions
manage_window.bt_settings.tooltip=Click here to display the settings
+manage_window.bt_themes.tip=Click here to choose a theme
+manage_window.bt_themes.option.invalid=Invalid
manage_window.checkbox.only_apps=Apps
manage_window.checkbox.show_details=Show details
manage_window.columns.installed=Installed
manage_window.columns.latest_version=Latest Version
manage_window.columns.update=Upgrade ?
+manage_window.label.apps_displayed.tip=Applications displayed / available
manage_window.label.updates=Updates
-manage_window.name_filter.placeholder=Filter by name
-manage_window.name_filter.tooltip=Type here to filter apps by name
+manage_window.name_filter.button_tooltip=Click here to filter apps by name
+manage_window.name_filter.placeholder=Name
+manage_window.name_filter.tooltip=Type and press ENTER to filter apps by name
manage_window.settings.about=About
manage_window.status.downgrading=Downgrading
manage_window.status.filtering=Filtering
@@ -321,7 +326,7 @@ manage_window.status.ignore_updates_reverse=Reverting updates ignored from {}
manage_window.status.info=Retrieving information
manage_window.status.installed=Loading installed
manage_window.status.installing=Installing
-manage_window.status.refreshing=Refreshing
+manage_window.status.refreshing=Refreshing apps
manage_window.status.refreshing_app=Refreshing
manage_window.status.running_app=Launching {}
manage_window.status.screenshots=Retrieving {} screenshots
@@ -352,9 +357,9 @@ popup.history.selected.tooltip=Current version
popup.history.title=History
popup.root.bad_password.body=Wrong password
popup.root.bad_password.last_try=Attempts finished. Action cancelled.
-popup.root.bad_password.title=Error
-popup.root.continue=continue
-popup.root.title=Requires your password to continue
+popup.root.continue=Continue
+popup.root.msg=Requires your password to continue
+popup.root.title=Authentication
popup.screenshots.no_screenshot.body=it was not possible to retrieve {} screenshots
prepare.bt_hide_details=Hide details
prepare.bt_icon.no_output=There are no progress details for this task to be displayed
@@ -370,7 +375,7 @@ screenshots.bt_back.label=previous
screenshots.bt_next.label=next
screenshots.download.no_content=No content to display
screenshots.download.no_response=Image not found
-screenshots.download.running=downloading image
+screenshots.image.loading=Loading
settings.changed.success.reboot=Restart now ?
settings.changed.success.warning=Settings successfully changed. Some of them will only take effect after a restart.
settings.error=It was not possible to properly change all the settings
@@ -403,6 +408,7 @@ view.components.file_chooser.placeholder=Click here to select
warning=warning
welcome=welcome
warning.update_available=There is a new {} version available ({}). Check out the news at {}.
-window_manage.input_search.placeholder=Search
-window_manage.input_search.tooltip=Type and press ENTER to search for applications
+window_manage.search_bar.button_tooltip=Click here to search for applications
+window_manage.search_bar.placeholder=Search
+window_manage.search_bar.tooltip=Type and press ENTER to search for applications
yes=yes
diff --git a/bauh/view/resources/locale/es b/bauh/view/resources/locale/es
index 2587cf4a..9d75459a 100644
--- a/bauh/view/resources/locale/es
+++ b/bauh/view/resources/locale/es
@@ -98,7 +98,7 @@ amount=cantidad
and=y
any=cualquiera
app.name=nombre de la aplicación
-applications=applications
+applications=aplicaciones
ask=preguntar
ask.continue=¿Quiere continuar?
author=autor
@@ -230,10 +230,12 @@ core.config.ui.auto_scale.tip=Activa el factor de escala de pantalla automática
core.config.ui.hdpi=HDPI
core.config.ui.hdpi.tip=Si se deben activar las mejoras relacionadas con las resoluciones HDPI
core.config.ui.max_displayed=Aplicaciones mostradas
-core.config.ui.max_displayed.tip=Número máximo de aplicaciones que se muestran en la tabla
+core.config.ui.max_displayed.tip=Número máximo de aplicaciones que se muestran en la tabla. Utilice 0 para no tener límite.
+core.config.ui.qt_style.tooltip=Estilo QT que se utilizará. Requiere reiniciar para que surta efecto.
core.config.ui.scale_factor=Escala
core.config.ui.scale_factor.tip=Define el factor de escala de visualización de la interfaz (Qt). Aumente el valor para aumentar el tamaño de la interfaz. Reinicio requerido.
-core.config.ui.style.default=Predeterminado
+core.config.ui.system_theme=Tema del sistema
+core.config.ui.system_theme.tip=Si el tema/hoja de estilo del sistema debe fusionarse con de {app}
core.config.ui.tray.default_icon=Ícono predeterminado
core.config.ui.tray.default_icon.tip=El icono predeterminado que se muestra en la bandeja
core.config.ui.tray.updates_icon=Icono de actualización
@@ -274,6 +276,7 @@ locale.ca=catalán
locale.de=alemán
locale.en=inglés
locale.es=español
+locale.fr=francés
locale.it=italiano
locale.pt=portugués
locale.ru=ruso
@@ -298,20 +301,23 @@ manage_window.bt.installed.text=instaladas
manage_window.bt.installed.tooltip=Pulse aquí para mostrar las aplicaciones instaladas
manage_window.bt.refresh.text=Recargar
manage_window.bt.refresh.tooltip=Recarga los datos sobre las aplicaciones instaladas
-manage_window.bt.suggestions.text=Sugerencias
manage_window.bt.suggestions.tooltip=Pulse aquí para mostrar algunas sugerencias de aplicaciones
manage_window.bt.upgrade.text=Actualizar
manage_window.bt.upgrade.tooltip=Actualiza todas las aplicaciones marcadas
manage_window.bt_custom_actions.tip=Pulse aquí para mostrar más acciones
manage_window.bt_settings.tooltip=Pulse aquí para exhibir las configuraciones
+manage_window.bt_themes.tip=Pulse aquí para elegir un tema
+manage_window.bt_themes.option.invalid=Inválido
manage_window.checkbox.only_apps=Aplicaciones
manage_window.checkbox.show_details=Mostrar detalles
manage_window.columns.installed=Instaladas
manage_window.columns.latest_version=Versión más reciente
manage_window.columns.update=¿Quiere actualizar?
+manage_window.label.apps_displayed.tip=Applicaciones mostradas / disponibles
manage_window.label.updates=Actualizaciones
-manage_window.name_filter.placeholder=Filtrar por nombre
-manage_window.name_filter.tooltip=Escriba aquí para filtrar aplicaciones por nombre
+manage_window.name_filter.button_tooltip=Haga clic aquí para filtrar aplicaciones por nombre
+manage_window.name_filter.placeholder=Nombre
+manage_window.name_filter.tooltip=Escriba y oprima Intro para filtrar aplicaciones por nombre
manage_window.settings.about=Acerca de
manage_window.status.downgrading=Revirtiendo
manage_window.status.filtering=Filtrando
@@ -321,7 +327,7 @@ manage_window.status.ignore_updates_reverse=Revertir actualizaciones ignoradas d
manage_window.status.info=Obteniendo información
manage_window.status.installed=Cargando instalados
manage_window.status.installing=Instalando
-manage_window.status.refreshing=Recargando
+manage_window.status.refreshing=Cargando applicaciones
manage_window.status.refreshing_app=Actualizando
manage_window.status.running_app=Ejecutando {}
manage_window.status.screenshots=Obteniendo imágenes de {}
@@ -352,9 +358,9 @@ popup.history.selected.tooltip=Versión actual
popup.history.title=Histórico
popup.root.bad_password.body=Contraseña incorrecta
popup.root.bad_password.last_try=Intentos finalizados. Acción cancelada.
-popup.root.bad_password.title=Error
-popup.root.continue=continuar
-popup.root.title=Proporcione su contraseña para continuar
+popup.root.continue=Continuar
+popup.root.msg=Proporcione su contraseña para continuar
+popup.root.title=Autenticación
popup.screenshots.no_screenshot.body=No fue posible recuperar las fotos de {}
prepare.bt_hide_details=Ocultar detalles
prepare.bt_icon.no_output=No hay detalles del progreso de esta tarea para mostrar
@@ -371,7 +377,7 @@ screenshots.bt_back.label=anterior
screenshots.bt_next.label=siguiente
screenshots.download.no_content=No hay contenido para mostrar
screenshots.download.no_response=No se encontró la imagen
-screenshots.download.running=descargando imagen
+screenshots.image.loading=Cargando
settings=configuraciones
settings.changed.success.reboot=¿Reiniciar ahora?
settings.changed.success.warning=Las configuraciones se cambiaron con éxito. Algunas solo tendrán efecto después del reinicio.
@@ -404,6 +410,7 @@ view.components.file_chooser.placeholder=Pulse aquí para seleccionar
warning=aviso
warning.update_available=Hay una nueva versión de {} disponible ({}). Vea las novedades en {}.
welcome=bienvenido
-window_manage.input_search.placeholder=Buscar
-window_manage.input_search.tooltip=Escriba y oprima Intro para buscar aplicaciones
+window_manage.search_bar.button_tooltip=Haga clic aquí para buscar aplicaciones
+window_manage.search_bar.placeholder=Buscar
+window_manage.search_bar.tooltip=Escriba y oprima Intro para buscar aplicaciones
yes=sí
\ No newline at end of file
diff --git a/bauh/view/resources/locale/fr b/bauh/view/resources/locale/fr
index 1736cf15..3c44651a 100644
--- a/bauh/view/resources/locale/fr
+++ b/bauh/view/resources/locale/fr
@@ -230,9 +230,9 @@ core.config.ui.hdpi.tip=Si les améliorations liées à HDPI doivent être activ
core.config.ui.hdpi=HDPI
core.config.ui.max_displayed.tip=Nombre maximal d'application affichées dans le tableau
core.config.ui.max_displayed=Applications affichées
+core.config.ui.qt_style.tooltip=QT style to be used. Requires restarting to take effect.
core.config.ui.scale_factor=Mettre à l'échelle
core.config.ui.scale_factor.tip=Définit le facteur de mise à l'échelle (Qt). L'augmenter agrandit l'interface. Redémarrage nécessaire.
-core.config.ui.style.default=Défaut
core.config.ui.tray.default_icon.tip=Icône par défaut pour {app} dans la barre système
core.config.ui.tray.default_icon=Icône par dafaut
core.config.ui.tray.updates_icon.tip=Icône affichée quand il y a des mises à jour disponibles
@@ -303,6 +303,8 @@ manage_window.bt.upgrade.text=Mettre à jour
manage_window.bt.upgrade.tooltip=Mettre à jour toutes les applications sélectionnées
manage_window.bt_custom_actions.tip=Cliquez ici pour plus d'actions
manage_window.bt_settings.tooltip=Cliquez ici pour afficher les paramètres
+manage_window.bt_themes.tip=Click here to choose a theme
+manage_window.bt_themes.option.invalid=Invalid
manage_window.checkbox.only_apps=Apps
manage_window.checkbox.show_details=Détails
manage_window.columns.installed=Installé
diff --git a/bauh/view/resources/locale/it b/bauh/view/resources/locale/it
index b5d3b617..b833aaba 100644
--- a/bauh/view/resources/locale/it
+++ b/bauh/view/resources/locale/it
@@ -230,10 +230,12 @@ core.config.ui.auto_scale.tip=It activates the auto screen scale factor ( {} ).
core.config.ui.hdpi=HDPI
core.config.ui.hdpi.tip=If improvements related to HDPI resolutions should be activated
core.config.ui.max_displayed=Applications displayed
-core.config.ui.max_displayed.tip=Maximum number of applications displayed on the table
+core.config.ui.max_displayed.tip=Maximum number of applications displayed on the table. Use 0 for no limit.
+core.config.ui.qt_style.tooltip=QT style to be used. Requires restarting to take effect.
core.config.ui.scale_factor=Scale
core.config.ui.scale_factor.tip=It defines the interface display scaling factor (Qt). Raise the value to raise the interface size. Restart required.
-core.config.ui.style.default=Default
+core.config.ui.system_theme=System theme
+core.config.ui.system_theme.tip=If the system's theme/stylesheet should be merged with {app}'s
core.config.ui.tray.default_icon=Default icon
core.config.ui.tray.default_icon.tip=The default icon for {app} displayed on the tray
core.config.ui.tray.updates_icon=Update icon
@@ -299,20 +301,23 @@ manage_window.bt.installed.text=installato
manage_window.bt.installed.tooltip=Clicca qui per mostrare le applicazioni installate
manage_window.bt.refresh.text=Refresh
manage_window.bt.refresh.tooltip=Ricarica i dati sulle applicazioni installate
-manage_window.bt.suggestions.text=Suggerimenti
manage_window.bt.suggestions.tooltip=Clicca qui per mostrare alcuni suggerimenti sulle applicazioni
manage_window.bt.upgrade.text=Aggiorna
manage_window.bt.upgrade.tooltip=Aggiorna tutte le applicazioni controllate
manage_window.bt_custom_actions.tip=Click here to show more actions
manage_window.bt_settings.tooltip=Fai clic qui per visualizzare le impostazioni
+manage_window.bt_themes.tip=Click here to choose a theme
+manage_window.bt_themes.option.invalid=Invalid
manage_window.checkbox.only_apps=Apps
manage_window.checkbox.show_details=Mostra dettagli
manage_window.columns.installed=Installed
manage_window.columns.latest_version=Ultima Versione
manage_window.columns.update=Upgrade ?
+manage_window.label.apps_displayed.tip=Applications displayed / available
manage_window.label.updates=Aggiornamenti
+manage_window.name_filter.button_tooltip=Click here to filter apps by name
manage_window.name_filter.placeholder=Filtra per nome
-manage_window.name_filter.tooltip=Digita qui per filtrare le app per nome
+manage_window.name_filter.tooltip=Type and press ENTER to filter apps by name
manage_window.settings.about=Informazione su
manage_window.status.downgrading=Downgrading
manage_window.status.filtering=Filtraggio
@@ -353,9 +358,9 @@ popup.history.selected.tooltip=Version corrente
popup.history.title=Cronlogia
popup.root.bad_password.body=Password errata
popup.root.bad_password.last_try=Tentativi conclusi. Azione annullata.
-popup.root.bad_password.title=Errore
-popup.root.continue=continuare
-popup.root.title=Richiede la tua password per continuare
+popup.root.continue=Continuare
+popup.root.msg=Richiede la tua password per continuare
+popup.root.title=Autenticazione
popup.screenshots.no_screenshot.body=non è stato possibile recuperare {} schermate
prepar_panel.bt_skip.label=saltare
prepare.bt_hide_details=Hide details
@@ -372,7 +377,7 @@ screenshots.bt_back.label=precedente
screenshots.bt_next.label=prossimo
screenshots.download.no_content=Nessun contenuto da visualizzare
screenshots.download.no_response=Immagine non trovata
-screenshots.download.running=scaricare l'immagine
+screenshots.image.loading=Loading
settings=impostazioni
settings.changed.success.reboot=Restart now ?
settings.changed.success.warning=Settings successfully changed. Some of them will only take effect after a restart.
@@ -405,6 +410,7 @@ view.components.file_chooser.placeholder=Fai clic qui per selezionare
warning=Avviso
warning.update_available=There is a new {} version available ({}). Check out the news at {}.
welcome=benvenuto
-window_manage.input_search.placeholder=Cerca
-window_manage.input_search.tooltip=Digitare e premere INVIO per cercare applicazioni
+window_manage.search_bar.button_tooltip=Click here to search for applications
+window_manage.search_bar.placeholder=Cerca
+window_manage.search_bar.tooltip=Digitare e premere INVIO per cercare applicazioni
yes=si
\ No newline at end of file
diff --git a/bauh/view/resources/locale/pt b/bauh/view/resources/locale/pt
index e33a7530..71d2f5a1 100644
--- a/bauh/view/resources/locale/pt
+++ b/bauh/view/resources/locale/pt
@@ -228,11 +228,13 @@ core.config.ui.auto_scale.tip=Ativa o fator de escala automático ( {} ). Corrig
core.config.ui.auto_scale=Escala automática
core.config.ui.hdpi.tip=Se melhorias para resoluções HDPI devem ser ativadas
core.config.ui.hdpi=HDPI
-core.config.ui.max_displayed.tip=Número máximo de aplicativos exibidos na tabela
+core.config.ui.max_displayed.tip=Número máximo de aplicativos exibidos na tabela. Use 0 para não haver limite.
core.config.ui.max_displayed=Aplicativos exibidos
+core.config.ui.qt_style.tooltip=Estilo QT a ser utilizado. Requer reinicialização para surtir efeito.
core.config.ui.scale_factor=Escala
core.config.ui.scale_factor.tip=Define o fator de escala de exibição da interface (Qt). Aumente o valor para aumentar o tamanho da interface. Reinicialização necessária.
-core.config.ui.style.default=Padrão
+core.config.ui.system_theme=Tema do sistema
+core.config.ui.system_theme.tip=Se o tema/folha de estilo do sistema deve ser mesclado com o do {app}
core.config.ui.tray.default_icon.tip=O ícone padrão exibido na bandeja
core.config.ui.tray.default_icon=Ícone padrão
core.config.ui.tray.updates_icon.tip=O ícone exibido quando há atualizações disponíveis
@@ -273,6 +275,7 @@ locale.ca=catalão
locale.de=alemão
locale.en=inglês
locale.es=espanhol
+locale.fr=francês
locale.it=italiano
locale.pt=português
locale.ru=russo
@@ -297,20 +300,23 @@ manage_window.bt.installed.text=instalados
manage_window.bt.installed.tooltip=Clique aqui para exibir os aplicativos instalados
manage_window.bt.refresh.text=Recarregar
manage_window.bt.refresh.tooltip=Recarrega os dados sobre os aplicativos instalados
-manage_window.bt.suggestions.text=Sugestões
manage_window.bt.suggestions.tooltip=Clique aqui para exibir algumas sugestões de aplicativos
manage_window.bt.upgrade.text=Atualizar
manage_window.bt.upgrade.tooltip=Atualiza todos os aplicativos marcados
manage_window.bt_custom_actions.tip=Clique aqui para exibir mais ações
manage_window.bt_settings.tooltip=Clique aqui para exibir as configurações
+manage_window.bt_themes.tip=Clique aqui para escolher um tema
+manage_window.bt_themes.option.invalid=Inválido
manage_window.checkbox.only_apps=Aplicativos
manage_window.checkbox.show_details=Mostrar detalhes
manage_window.columns.installed=Instalado
manage_window.columns.latest_version=Última Versão
manage_window.columns.update=Atualizar ?
+manage_window.label.apps_displayed.tip=Aplicações exibidas / disponíveis
manage_window.label.updates=Atualizações
+manage_window.name_filter.button_tooltip=Clique aqui para filtrar aplicativos por nome
manage_window.name_filter.placeholder=Filtrar por nome
-manage_window.name_filter.tooltip=Digite aqui para filtrar aplicativos pelo nome
+manage_window.name_filter.tooltip=Digite e pressione ENTER para filtrar aplicativos pelo nome
manage_window.settings.about=Sobre
manage_window.status.downgrading=Revertendo
manage_window.status.filtering=Filtrando
@@ -320,7 +326,7 @@ manage_window.status.ignore_updates_reverse=Revertendo atualizações ignoradas
manage_window.status.info=Obtendo informação
manage_window.status.installed=Carregando instalados
manage_window.status.installing=Instalando
-manage_window.status.refreshing=Recarregando
+manage_window.status.refreshing=Carregando aplicativos
manage_window.status.refreshing_app=Atualizando
manage_window.status.running_app=Iniciando {}
manage_window.status.screenshots=Obtendo imagens de {}
@@ -351,9 +357,9 @@ popup.history.selected.tooltip=Versão atual
popup.history.title=Histórico
popup.root.bad_password.body=Senha incorreta
popup.root.bad_password.last_try=Tentativas finalizadas. Ação cancelada.
-popup.root.bad_password.title=Erro
-popup.root.continue=prosseguir
-popup.root.title=Requer sua senha para prosseguir
+popup.root.continue=Prosseguir
+popup.root.msg=Requer sua senha para prosseguir
+popup.root.title=Autenticação
popup.screenshots.no_screenshot.body=Não foi possível obter as fotos de {}
prepare.bt_hide_details=Ocultar detalhes
prepare.bt_icon.no_output=Não existe detalhes do progresso dessa tarefa para ser exibido
@@ -365,11 +371,11 @@ proceed=continuar
publisher.verified=verificado
publisher=publicador
repository=repositório
-screenshots.download.running=baixando imagem
screenshots.bt_back.label=anterior
screenshots.bt_next.label=próxima
screenshots.download.no_content=Sem conteúdo para exibir
screenshots.download.no_response=Imagem não encontrada
+screenshots.image.loading=Carregando
settings.changed.success.reboot=Reiniciar agora ?
settings.changed.success.warning=Configurações alteradas com sucesso ! Algumas delas só surtirão após a reinicialização.
settings.error=Não foi possível alterar todas as configurações adequadamente
@@ -402,6 +408,7 @@ view.components.file_chooser.placeholder=Clique aqui para selecionar
warning=aviso
warning.update_available=Há uma nova versão do {} disponível ({}). Confira as novidades em {}
welcome=bem vindo
-window_manage.input_search.placeholder=Buscar
-window_manage.input_search.tooltip=Digite e pressione ENTER para buscar aplicativos
+window_manage.search_bar.button_tooltip=Clique aqui para buscar aplicativos
+window_manage.search_bar.placeholder=Buscar
+window_manage.search_bar.tooltip=Digite e pressione ENTER para buscar aplicativos
yes=sim
\ No newline at end of file
diff --git a/bauh/view/resources/locale/ru b/bauh/view/resources/locale/ru
index 0760e91e..85ea3b55 100644
--- a/bauh/view/resources/locale/ru
+++ b/bauh/view/resources/locale/ru
@@ -229,10 +229,12 @@ core.config.ui.auto_scale.tip=Он активирует автоматическ
core.config.ui.hdpi=HDPI
core.config.ui.hdpi.tip=Если улучшения, связанные с разрешением HDPI должны быть активированы
core.config.ui.max_displayed=Отображаемые приложения
-core.config.ui.max_displayed.tip=Максимальное количество приложений, отображаемых в таблице
+core.config.ui.max_displayed.tip=Максимальное количество приложений, отображаемых в таблице. Use 0 for no limit.
+core.config.ui.qt_style.tooltip=QT style to be used. Requires restarting to take effect.
core.config.ui.scale_factor=Scale
core.config.ui.scale_factor.tip=It defines the interface display scaling factor (Qt). Raise the value to raise the interface size. Restart required.
-core.config.ui.style.default=Default
+core.config.ui.system_theme=System theme
+core.config.ui.system_theme.tip=If the system's theme/stylesheet should be merged with {app}'s
core.config.ui.tray.default_icon=Значок по умолчанию
core.config.ui.tray.default_icon.tip=Значок по умолчанию для {app}, отображаемый в трее
core.config.ui.tray.updates_icon=Значок обновления
@@ -297,20 +299,23 @@ manage_window.bt.installed.text=Установленные
manage_window.bt.installed.tooltip=Нажмите здесь, чтобы показать установленные приложения
manage_window.bt.refresh.text=Обновить список
manage_window.bt.refresh.tooltip=Перезагрузите данные об установленных приложениях
-manage_window.bt.suggestions.text=Рекомендации
manage_window.bt.suggestions.tooltip=Нажмите здесь, чтобы показать некоторые рекомендаваные приложения
manage_window.bt.upgrade.text=Обновление
manage_window.bt.upgrade.tooltip=Обновить все выбранные приложения
manage_window.bt_custom_actions.tip=Нажмите здесь, чтобы показать больше действий
manage_window.bt_settings.tooltip=Нажмите здесь, чтобы отобразить настройки
+manage_window.bt_themes.tip=Click here to choose a theme
+manage_window.bt_themes.option.invalid=Invalid
manage_window.checkbox.only_apps=Приложения
manage_window.checkbox.show_details=Показать детали
manage_window.columns.installed=Installed
manage_window.columns.latest_version=Последняя версия
manage_window.columns.update=Обновить?
+manage_window.label.apps_displayed.tip=Applications displayed / available
manage_window.label.updates=Обновления
+manage_window.name_filter.button_tooltip=Click here to filter apps by name
manage_window.name_filter.placeholder=Фильтр по имени
-manage_window.name_filter.tooltip=Введите здесь, чтобы фильтровать приложения по имени
+manage_window.name_filter.tooltip=Type and press ENTER to filter apps by name
manage_window.settings.about=О программе
manage_window.status.downgrading=Откатить
manage_window.status.filtering=Фильтрация
@@ -351,9 +356,9 @@ popup.history.selected.tooltip=Текущая версия
popup.history.title=История версий
popup.root.bad_password.body=Неправильный пароль
popup.root.bad_password.last_try=Закончились попытки. Отмена действия.
-popup.root.bad_password.title=Ошибка
popup.root.continue=Продолжить
-popup.root.title=Требуется ваш пароль, чтобы продолжить
+popup.root.msg=Требуется ваш пароль, чтобы продолжить
+popup.root.title=Аутентификация
popup.screenshots.no_screenshot.body=не удалось получить скриншоты {}
prepare.bt_hide_details=Hide details
prepare.bt_icon.no_output=There are no progress details for this task to be displayed
@@ -369,7 +374,7 @@ screenshots.bt_back.label=Предыдущий
screenshots.bt_next.label=Следующий
screenshots.download.no_content=Нет материалов для отображени
screenshots.download.no_response=Изображение не найдено
-screenshots.download.running=Загрузка изображения
+screenshots.image.loading=Loading
settings=Настройки
settings.changed.success.reboot=Перезапустить сейчас?
settings.changed.success.warning=Настройки успешно изменены. Некоторые из них вступят в силу только после перезагрузки.
@@ -402,6 +407,7 @@ view.components.file_chooser.placeholder=Нажмите здесь, чтобы
warning=Предупреждение
warning.update_available=There is a new {} version available ({}). Check out the news at {}.
welcome=Добро пожаловать!
-window_manage.input_search.placeholder=Поиск
-window_manage.input_search.tooltip=Введите и нажмите ENTER для поиска приложений
+window_manage.search_bar.button_tooltip=Click here to search for applications
+window_manage.search_bar.placeholder=Поиск
+window_manage.search_bar.tooltip=Введите и нажмите ENTER для поиска приложений
yes=Да
diff --git a/bauh/view/resources/locale/tr b/bauh/view/resources/locale/tr
index c2ac8256..adbae589 100644
--- a/bauh/view/resources/locale/tr
+++ b/bauh/view/resources/locale/tr
@@ -228,11 +228,13 @@ core.config.ui.auto_scale.tip=Otomatik ekran ölçek faktörünü ( {} ) etkinle
core.config.ui.auto_scale=Oto ölçekleme
core.config.ui.hdpi.tip=HDPI çözünürlükleri ile ilgili iyileştirmeler etkinleştirilirse
core.config.ui.hdpi=HDPI
-core.config.ui.max_displayed.tip=Tabloda görüntülenen azami uygulama sayısı
+core.config.ui.max_displayed.tip=Tabloda görüntülenen azami uygulama sayısı. Use 0 for no limit.
core.config.ui.max_displayed=Görüntülenen uygulamalar
+core.config.ui.qt_style.tooltip=QT style to be used. Requires restarting to take effect.
core.config.ui.scale_factor=Scale
core.config.ui.scale_factor.tip=It defines the interface display scaling factor (Qt). Raise the value to raise the interface size. Restart required.
-core.config.ui.style.default=Default
+core.config.ui.system_theme=System theme
+core.config.ui.system_theme.tip=If the system's theme/stylesheet should be merged with {app}'s
core.config.ui.tray.default_icon.tip=Tepside görüntülenen {app} için varsayılan simge
core.config.ui.tray.default_icon=Varsayılan simge
core.config.ui.tray.updates_icon.tip=Güncelleme mevcut olduğunda görüntülenen simge
@@ -297,20 +299,23 @@ manage_window.bt.installed.text=yüklü
manage_window.bt.installed.tooltip=Yüklü uygulamaları görmek için burayı tıkla
manage_window.bt.refresh.text=Yenile
manage_window.bt.refresh.tooltip=Yüklü uygulamalar hakkındaki verileri yeniden yükle
-manage_window.bt.suggestions.text=Önerilenler
manage_window.bt.suggestions.tooltip=Bazı uygulama önerileri için burayı tıkla
manage_window.bt.upgrade.text=Yükselt
manage_window.bt.upgrade.tooltip=İşaretli tüm uygulamaları yükseltin
manage_window.bt_custom_actions.tip=Daha fazla eylem için burayı tıkla
manage_window.bt_settings.tooltip=Görünüm ayarları için burayı tıkla
+manage_window.bt_themes.tip=Click here to choose a theme
+manage_window.bt_themes.option.invalid=Invalid
manage_window.checkbox.only_apps=Uygulamalar
manage_window.checkbox.show_details=Detayları göster
manage_window.columns.installed=Yüklü
manage_window.columns.latest_version=Son sürüm
manage_window.columns.update=Yükselt ?
+manage_window.label.apps_displayed.tip=Applications displayed / available
manage_window.label.updates=Güncellemeler
+manage_window.name_filter.button_tooltip=Click here to filter apps by name
manage_window.name_filter.placeholder=Filtre adı
-manage_window.name_filter.tooltip=Uygulamaları ada göre filtrelemek için buraya yazın
+manage_window.name_filter.tooltip=Type and press ENTER to filter apps by name
manage_window.settings.about=Hakkında
manage_window.status.downgrading=Düşürülüyor
manage_window.status.filtering=Filtreleniyor
@@ -351,9 +356,9 @@ popup.history.selected.tooltip=Mevcut sürüm
popup.history.title=Geçmiş
popup.root.bad_password.body=Yanlış şifre
popup.root.bad_password.last_try=Çok sayıda yanlış deneme. İşlem iptal edildi.
-popup.root.bad_password.title=Hata
popup.root.continue=devam
-popup.root.title=Devam etmek için şifre girmelisiniz
+popup.root.msg=Devam etmek için şifre girmelisiniz
+popup.root.title=Doğrulama
popup.screenshots.no_screenshot.body={} ekran görüntüsünü almak mümkün değildi
prepare.bt_hide_details=Hide details
prepare.bt_icon.no_output=There are no progress details for this task to be displayed
@@ -369,7 +374,7 @@ screenshots.bt_back.label=önceki
screenshots.bt_next.label=sonraki
screenshots.download.no_content=Görüntülenecek içerik yok
screenshots.download.no_response=İmaj bulunamadı
-screenshots.download.running=imaj indiriliyor
+screenshots.image.loading=Loading
settings.changed.success.reboot=Şimdi yeniden başlat ?
settings.changed.success.warning=Ayarlar başarıyla değiştirildi ancak yeniden başlattıktan sonra devreye girecek.
settings.error=Tüm ayarları düzgün bir şekilde değiştirmek mümkün olmadı
@@ -402,6 +407,7 @@ view.components.file_chooser.placeholder=Seçmek için buraya tıkla
warning=uyarı
welcome=hoşgeldinis
warning.update_available=Yeni bir {} sürümü var ({}). {} Adresindeki haberleri inceleyin.
-window_manage.input_search.placeholder=Ara
-window_manage.input_search.tooltip=Uygulama aramak için yazın ve ENTER tuşuna basın
+window_manage.search_bar.button_tooltip=Click here to search for applications
+window_manage.search_bar.placeholder=Ara
+window_manage.search_bar.tooltip=Uygulama aramak için yazın ve ENTER tuşuna basın
yes=evet
diff --git a/bauh/view/resources/style/darcula/darcula.meta b/bauh/view/resources/style/darcula/darcula.meta
new file mode 100644
index 00000000..9eb0e8ec
--- /dev/null
+++ b/bauh/view/resources/style/darcula/darcula.meta
@@ -0,0 +1,6 @@
+name=Darcula
+description=Dark based on Darcula theme (JetBrains)
+description[pt]=Escuro baseada no tema Darcula (JetBrains)
+description[es]=Oscuro basado en el tema Darcula (JetBrains)
+version=1.0
+root_sheet=default
diff --git a/bauh/view/resources/style/darcula/darcula.qss b/bauh/view/resources/style/darcula/darcula.qss
new file mode 100644
index 00000000..aee17b89
--- /dev/null
+++ b/bauh/view/resources/style/darcula/darcula.qss
@@ -0,0 +1,433 @@
+QWidget, QWidget QToolTip {
+ background-color: @outer_widget.background.color;
+ color: @font.color;
+ selection-background-color: @tab.font.color;
+}
+
+QCheckBox::indicator:unchecked {
+ image: url("@style_dir/img/checkbox_unchecked.svg");
+}
+
+QCheckBox::indicator:unchecked:disabled {
+ image: url("@style_dir/img/checkbox_unchecked_disabled.svg");
+}
+
+QCheckBox::indicator:checked {
+ image: url("@style_dir/img/checkbox_checked.svg");
+}
+
+QCheckBox::indicator:checked:disabled {
+ image: url("@style_dir/img/checkbox_checked_disabled.svg");
+}
+
+QComboBox:focus, QSpinBox:focus, ManageWindow QComboBox:focus {
+ border-color: @focus.border.color;
+ color: @focus.border.color;
+}
+
+QLineEdit:focus {
+ border-color: @focus.border.color;
+}
+
+QPushButton {
+ border: 1px solid @pushbutton.border.color;
+ border-radius: 5px;
+ background-color: @pushbutton.background.color;
+}
+
+QPushButton:disabled {
+ background-color: @pushbutton.disabled.background.color;
+ color: @pushbutton.disabled.font.color;
+}
+
+QToolButton {
+ border: none;
+}
+
+QToolButton:hover {
+ background-color: @toolbutton.hover.background.color;
+ border-radius: 3px;
+}
+
+QLineEdit {
+ selection-color: @menu.item.selected.font.color;
+ selection-background-color: @menu.item.selected.background.color;
+ background-color: @lineedit.background.color;
+ border: 1px solid @lineedit.border.color;
+ border-radius: 5px;
+ padding: 3px;
+}
+
+QPlainTextEdit {
+ selection-color: @menu.item.selected.font.color;
+ selection-background-color: @menu.item.selected.background.color;
+ color: @font.color;
+ background-color: @inner_widget.background.color;
+}
+
+QScrollBar, QTableWidget QScrollBar {
+ background-color: @inner_widget.background.color;
+}
+
+QScrollBar::vertical{
+ max-width: 10px;
+}
+
+QScrollBar::handle {
+ background-color: @scrollbar.handle.background.color;
+}
+
+QScrollBar::add-line, QScrollBar::sub-line, QScrollBar::add-page, QScrollBar::sub-page {
+ height: 0px;
+}
+
+QComboBox, ManageWindow QComboBox, QSpinBox {
+ border-color: @combobox.border.color;
+}
+
+QComboxBox:!editable, QComboBox::drop-down {
+ border-left: 0;
+}
+
+QComboBox::down-arrow {
+ image: url("@style_dir/img/arrow_down.svg");
+ height: 10px;
+ width: 10px;
+ padding-right: 10px;
+}
+
+QSpinBox {
+ selection-color: @menu.item.selected.font.color;
+ selection-background-color: @menu.item.selected.background.color;
+}
+
+QSpinBox::down-arrow {
+ image: url("@style_dir/img/arrow_down.svg");
+}
+
+QSpinBox::up-arrow {
+ image: url("@style_dir/img/arrow_up.svg");
+}
+
+QSpinBox::up-arrow:disabled, QSpinBox::up-arrow:off, QSpinBox::down-arrow:disabled, QSpinBox::down-arrow:off {
+ image: none;
+}
+
+QTableWidget {
+ border: 1px solid @table.border.color;
+ background-color: @inner_widget.background.color;
+ selection-background-color: @table.selection.background.color;
+}
+
+QTableWidget * {
+ background-color: none;
+ color: @font.color;
+}
+
+QTableWidget QHeaderView {
+ background-color: @outer_widget.background.color;
+ font-weight: bold;
+}
+
+QTableWidget QToolButton:hover {
+ background-color: @table.button.hover.background.color;
+}
+
+QComboBox, QComboBox QAbstractItemView, QMenu QAbstractItemView {
+ selection-background-color: @menu.item.selected.background.color;
+ selection-color: @menu.item.selected.font.color;
+ color: @font.color;
+}
+
+QMenu QPushButton {
+ border-radius: 0px;
+}
+
+QMenu QPushButton[current = "true"] {
+ color: @color.yellow_dark;
+}
+
+QMenu QPushButton:hover {
+ background-color: @menu.item.selected.background.color;
+ color: @menu.item.selected.font.color;
+}
+
+QLabel[error = "true"] {
+ color: @error.color;
+}
+
+QPlainTextEdit[console = 'true'] {
+ background: @console.background.color;
+ color: @console.font.color;
+}
+
+QTabBar::tab:selected {
+ color: @tab.font.color;
+ border: 1px solid @inner_widget.background.color;
+ border-top-left-radius: 5px;
+ border-top-right-radius: 5px;
+ border-bottom: 2px solid @tab.underline.color;
+ background-color: @tab.highlight.color;
+ padding: 5px;
+}
+
+QProgressBar::chunk {
+ background-color: @progressbar.fill.color;
+}
+
+QSearchBar * {
+ background-color: @outer_widget.background.color;
+}
+
+QSearchBar QLabel#lb_left_corner {
+ border-left-color: @lineedit.border.color;
+ border-top-color: @lineedit.border.color;
+ border-bottom-color: @lineedit.border.color;
+}
+
+QSearchBar QLabel#lb_left_corner[focused = "true"] {
+ border-left-color: @focus.border.color;
+ border-top-color: @focus.border.color;
+ border-bottom-color: @focus.border.color;
+}
+
+QSearchBar QPushButton#search_button {
+ qproperty-icon: url("@style_dir/img/search.svg");
+ border-right-color: @lineedit.border.color;
+ border-top-color: @lineedit.border.color;
+ border-bottom-color: @lineedit.border.color;
+}
+
+QSearchBar QPushButton#search_button[focused = "true"] {
+ border-right-color: @focus.border.color;
+ border-top-color: @focus.border.color;
+ border-bottom-color: @focus.border.color;
+ qproperty-icon: url("@style_dir/img/search_focused.svg");
+}
+
+QSearchBar QLineEdit#inp_search {
+ border-bottom-color: @lineedit.border.color;
+ border-top-color: @lineedit.border.color;
+}
+
+QSearchBar QLineEdit#inp_search:focus {
+ border-bottom-color: @focus.border.color;
+ border-top-color: @focus.border.color;
+}
+
+QSearchBar#name_filter QPushButton#search_button {
+ qproperty-icon: url("@style_dir/img/filter.svg");
+}
+
+QSearchBar#name_filter QPushButton#search_button[focused = "true"] {
+ qproperty-icon: url("@style_dir/img/filter_focused.svg");
+}
+
+QPushButton#ok {
+ background-color: @button_ok.background.color;
+ color: @button_ok.font.color;
+ border-color: @button_ok.background.color;
+ font-weight: bold;
+}
+
+PreparePanel QTableWidget#tasks {
+ background-color: @inner_widget.background.color;
+}
+
+PreparePanel QLabel#task_status[status = "done"], QLabel#task_progress[status = "done"] {
+ color: @task.done.color;
+}
+
+ManageWindow QSearchBar#name_filter {
+ color: @font.color;
+}
+
+QPushButton#bt_upgrade {
+ qproperty-icon: url("@style_dir/img/update.svg");
+ border: 0.5px solid @upgrade.color;
+ background: @outer_widget.background.color;
+ color: @upgrade.color;
+}
+
+QPushButton#bt_upgrade:disabled {
+ color: @font.color;
+ border-color: @font.color;
+}
+
+PackagesTable UpgradeToggleButton:disabled {
+ color: @font.color;
+ border-color: @font.color;
+}
+
+PackagesTable QLabel#app_version[update = "true"] {
+ color: @table.app_version.update.color;
+ font-weight: @table.app_version.update.weight;
+}
+
+PackagesTable QLabel#app_version:disabled[update = "true"] {
+ color: @font.color;
+}
+
+PackagesTable QLabel#app_version[ignored = "true"] {
+ color: @table.app_version.update_ignored.color;
+}
+
+PackagesTable QLabel#app_version:disabled[ignored = "true"] {
+ color: @font.color;
+}
+
+PackagesTable UpgradeToggleButton, ConfirmationDialog UpgradeToggleButton {
+ qproperty-icon: url("@style_dir/img/update.svg");
+ border: 0.5px solid @upgrade.color;
+ border-radius: 2px;
+ background-color: @outer_widget.background.color;
+}
+
+PackagesTable UpgradeToggleButton[toggled = "false"] {
+ qproperty-icon: url("@style_dir/img/update_unchecked.svg");
+ background-color: @inner_widget.background.color;
+ border-color: @outer_widget.background.color;
+}
+
+PackagesTable UpgradeToggleButton[enabled = "false"] {
+ background: @table.update_check.disabled.color;
+}
+
+ManageWindow PackagesTable IconButton#app_info {
+ qproperty-icon: url("@style_dir/img/about.svg");
+ qproperty-iconSize: 13px 13px;
+}
+
+QPushButton#bt_refresh {
+ qproperty-icon: url("@style_dir/img/refresh.svg")
+}
+
+QPushButton#bt_installed {
+ qproperty-icon: url("@style_dir/img/installed.svg");
+}
+
+ManageWindow PackagesTable IconButton#app_run {
+ qproperty-icon: url("@style_dir/img/app_play.svg");
+}
+
+ManageWindow PackagesTable IconButton#app_run[_enabled = "false"] {
+ qproperty-icon: url("@style_dir/img/app_play_disabled.svg");
+}
+
+ManageWindow IconButton#suggestions {
+ qproperty-icon: url("@style_dir/img/suggestions.svg");
+}
+
+ManageWindow IconButton#themes {
+ qproperty-icon: url("@style_dir/img/themes.svg");
+}
+
+ManageWindow PackagesTable IconButton#app_actions {
+ qproperty-icon: url("@style_dir/img/menu.svg");
+}
+
+ManageWindow IconButton#settings {
+ qproperty-icon: url("@style_dir/img/settings.svg");
+}
+
+PackagesTable IconButton#app_screenshots {
+ qproperty-icon: url("@style_dir/img/camera.svg");
+}
+
+ManageWindow PackagesTable IconButton#app_screenshots[_enabled = "false"] {
+ qproperty-icon: url("@style_dir/img/camera_disabled.svg");
+}
+
+ManageWindow IconButton#custom_actions {
+ qproperty-icon: url("@style_dir/img/menu.svg");
+ qproperty-iconSize: 16px 16px;
+}
+
+ManageWindow IconButton#about {
+ qproperty-icon: url("@style_dir/img/about.svg");
+ qproperty-iconSize: 16px 16px;
+}
+
+QMenu#app_actions QPushButton#app_history {
+ qproperty-icon: url("@style_dir/img/history.svg");
+}
+
+QMenu#app_actions QPushButton#app_downgrade {
+ qproperty-icon: url("@style_dir/img/downgrade.svg");
+}
+
+QMenu#app_actions QPushButton#ignore_updates {
+ qproperty-icon: url("@style_dir/img/ignore_update.svg");
+}
+
+QMenu#app_actions QPushButton#revert_ignore_updates {
+ qproperty-icon: url("@style_dir/img/revert_update_ignored.svg");
+}
+
+PackagesTable QLabel[publisher_known = "false"]{
+ color: @table.app_publisher.unknown.font.color;
+}
+
+PackagesTable QLabel:disabled[publisher_known = "false"]{
+ color: @font.color;
+}
+
+PackagesTable QLabel#icon_publisher_verified {
+ qproperty-pixmap: url("@style_dir/img/verified.svg");
+}
+
+PackagesTable QToolButton#bt_install {
+ background-color: transparent;
+ color: @table.bt_install.font.color;
+}
+
+PackagesTable QToolButton#bt_install:hover, PackagesTable QToolButton#bt_uninstall:hover {
+ background-color: @table.button.hover.background.color;
+}
+
+PackagesTable QToolButton#bt_install:disabled {
+ color: @font.color;
+}
+
+PackagesTable QToolButton#bt_uninstall {
+ background-color: transparent;
+ color: @table.bt_uninstall.font.color;
+}
+
+PackagesTable QToolButton#bt_uninstall:disabled {
+ color: @font.color;
+}
+
+QLabel[help_icon = "true"] {
+ qproperty-pixmap: url("@style_dir/img/help.svg");
+}
+
+QLabel[tip_icon = "true"] {
+ qproperty-pixmap: url("@style_dir/img/help.svg");
+}
+
+FormQt IconButton#clean_field {
+ qproperty-icon: url("@style_dir/img/clean.svg");
+}
+
+PreparePanel QLabel#task_status[status = "running"], QLabel#task_progress[status = "running"], QLabel#task_substatus {
+ font-weight: normal;
+}
+
+HistoryDialog QTableWidget QLabel[even = "false"] {
+ background-color: @outer_widget.background.color;
+}
+
+HistoryDialog QTableWidget QLabel[outdated = "true"] {
+ background-color: @history.version.outdated;
+ color: @history.version.focus.color;
+}
+
+HistoryDialog QTableWidget QLabel[outdated = "false"] {
+ background-color: @history.version.updated;
+ color: @history.version.focus.color;
+}
+
+InfoDialog QPushButton#show {
+ height: 16px;
+}
\ No newline at end of file
diff --git a/bauh/view/resources/style/darcula/darcula.vars b/bauh/view/resources/style/darcula/darcula.vars
new file mode 100644
index 00000000..d8da129e
--- /dev/null
+++ b/bauh/view/resources/style/darcula/darcula.vars
@@ -0,0 +1,50 @@
+button_ok.background.color=#365880
+button_ok.background.color=#4C708C
+button_ok.font.color=#BABBBB
+color.blue_light=#3592C4
+color.green_editor=#6A8759
+color.green_light=#499C54
+color.grey_blue=#A9B7C6
+color.grey_editor=#2B2B2B
+color.grey_panel=#3C3F41
+color.orange=#CC752F
+color.red=#FF6B68
+color.warning=@color.yellow_dark
+color.yellow_dark=#BE9117
+combobox.border.color=#5E6060
+console.background.color=@inner_widget.background.color
+console.font.color=#BBBBA8
+error.color=@color.red
+focus.border.color=#6591B2
+font.color=@color.grey_blue
+history.version.focus.color=white;
+history.version.outdated=@color.warning;
+history.version.updated=@color.green_light;
+inner_widget.background.color=@color.grey_editor
+lineedit.background.color=#45494A
+lineedit.border.color=#646464
+outer_widget.background.color=@color.grey_panel
+progressbar.fill.color=@color.blue_light
+pushbutton.background.color=#4C5052
+pushbutton.border.color=@combobox.border.color
+pushbutton.disabled.background.color=@color.grey_panel
+pushbutton.disabled.font.color=#737373
+menu.item.selected.background.color=#2F65CA
+menu.item.selected.font.color=white
+scrollbar.handle.background.color=#4D4D4D
+tab.font.color=@focus.border.color
+tab.highlight.color=#4E5254
+tab.underline.color=#4A88C7
+table.app_publisher.unknown.font.color=@color.red
+table.app_version.update.color=@color.yellow_dark
+table.app_version.update.weight=normal
+table.app_version.update_ignored.color=@color.yellow_dark
+table.border.color=#555555
+table.bt_install.font.color=@color.green_light
+table.bt_uninstall.font.color=@color.red
+table.update_check.disabled.color=@color.warning
+table.selection.background.color=#0D293E
+table.button.hover.background.color=#323232
+task.done.color=@color.green_editor
+toolbutton.hover.background.color=@pushbutton.background.color
+upgrade.color=@color.green_light
diff --git a/bauh/view/resources/img/info.svg b/bauh/view/resources/style/darcula/img/about.svg
similarity index 73%
rename from bauh/view/resources/img/info.svg
rename to bauh/view/resources/style/darcula/img/about.svg
index 5572ba20..9d403596 100644
--- a/bauh/view/resources/img/info.svg
+++ b/bauh/view/resources/style/darcula/img/about.svg
@@ -9,12 +9,12 @@
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
id="Capa_1"
enable-background="new 0 0 512 512"
- height="12"
- viewBox="0 0 12 12"
- width="12"
+ height="512"
+ viewBox="0 0 512 512"
+ width="512"
version="1.1"
- sodipodi:docname="info.svg"
- inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)">
+ sodipodi:docname="about.svg"
+ inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)">
@@ -43,9 +43,9 @@
id="namedview873"
showgrid="false"
showborder="true"
- inkscape:zoom="11.335805"
- inkscape:cx="23.192934"
- inkscape:cy="10.264189"
+ inkscape:zoom="1.0019531"
+ inkscape:cx="197.9376"
+ inkscape:cy="236.73295"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
@@ -53,21 +53,22 @@
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
- fit-margin-bottom="0" />
+ fit-margin-bottom="0"
+ inkscape:document-rotation="0" />
+ transform="matrix(22.337858,0,0,21.3193,86.427173,-11.115837)"
+ style="fill:#8888c6;fill-opacity:1;stroke:none;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1">
+ style="fill:#8888c6;fill-opacity:1;stroke:none;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
diff --git a/bauh/view/resources/img/app_play.svg b/bauh/view/resources/style/darcula/img/app_play.svg
old mode 100755
new mode 100644
similarity index 84%
rename from bauh/view/resources/img/app_play.svg
rename to bauh/view/resources/style/darcula/img/app_play.svg
index 67dc214b..75306fe1
--- a/bauh/view/resources/img/app_play.svg
+++ b/bauh/view/resources/style/darcula/img/app_play.svg
@@ -1,6 +1,4 @@
-
-
image/svg+xml
+ transform="matrix(0.837209,0,0,0.83724953,-65.454588,41.15345)"
+ style="fill:#499c54;fill-opacity:1;stroke:none;stroke-width:7.81119;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1">
-
\ No newline at end of file
+
diff --git a/bauh/view/resources/style/darcula/img/app_play_disabled.svg b/bauh/view/resources/style/darcula/img/app_play_disabled.svg
new file mode 100644
index 00000000..109fd44b
--- /dev/null
+++ b/bauh/view/resources/style/darcula/img/app_play_disabled.svg
@@ -0,0 +1,159 @@
+
+image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/darcula/img/arrow_down.svg b/bauh/view/resources/style/darcula/img/arrow_down.svg
new file mode 100644
index 00000000..70a7e9ee
--- /dev/null
+++ b/bauh/view/resources/style/darcula/img/arrow_down.svg
@@ -0,0 +1,98 @@
+
+image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/darcula/img/arrow_up.svg b/bauh/view/resources/style/darcula/img/arrow_up.svg
new file mode 100644
index 00000000..c1bc0ea8
--- /dev/null
+++ b/bauh/view/resources/style/darcula/img/arrow_up.svg
@@ -0,0 +1,98 @@
+
+image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/darcula/img/camera.svg b/bauh/view/resources/style/darcula/img/camera.svg
new file mode 100644
index 00000000..8a7af2eb
--- /dev/null
+++ b/bauh/view/resources/style/darcula/img/camera.svg
@@ -0,0 +1,133 @@
+
+image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/darcula/img/camera_disabled.svg b/bauh/view/resources/style/darcula/img/camera_disabled.svg
new file mode 100644
index 00000000..a34a1f12
--- /dev/null
+++ b/bauh/view/resources/style/darcula/img/camera_disabled.svg
@@ -0,0 +1,137 @@
+
+image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/darcula/img/checkbox_checked.svg b/bauh/view/resources/style/darcula/img/checkbox_checked.svg
new file mode 100644
index 00000000..6cbd375f
--- /dev/null
+++ b/bauh/view/resources/style/darcula/img/checkbox_checked.svg
@@ -0,0 +1,103 @@
+
+
+
+
+
+
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/darcula/img/checkbox_checked_disabled.svg b/bauh/view/resources/style/darcula/img/checkbox_checked_disabled.svg
new file mode 100644
index 00000000..23e7f080
--- /dev/null
+++ b/bauh/view/resources/style/darcula/img/checkbox_checked_disabled.svg
@@ -0,0 +1,102 @@
+
+
+
+
+
+
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/darcula/img/checkbox_unchecked.svg b/bauh/view/resources/style/darcula/img/checkbox_unchecked.svg
new file mode 100644
index 00000000..291b741f
--- /dev/null
+++ b/bauh/view/resources/style/darcula/img/checkbox_unchecked.svg
@@ -0,0 +1,88 @@
+
+
+
+
+
+
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/darcula/img/checkbox_unchecked_disabled.svg b/bauh/view/resources/style/darcula/img/checkbox_unchecked_disabled.svg
new file mode 100644
index 00000000..698dc468
--- /dev/null
+++ b/bauh/view/resources/style/darcula/img/checkbox_unchecked_disabled.svg
@@ -0,0 +1,88 @@
+
+
+
+
+
+
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/darcula/img/clean.svg b/bauh/view/resources/style/darcula/img/clean.svg
new file mode 100644
index 00000000..fba112a9
--- /dev/null
+++ b/bauh/view/resources/style/darcula/img/clean.svg
@@ -0,0 +1,158 @@
+
+image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/darcula/img/downgrade.svg b/bauh/view/resources/style/darcula/img/downgrade.svg
new file mode 100644
index 00000000..dee81aea
--- /dev/null
+++ b/bauh/view/resources/style/darcula/img/downgrade.svg
@@ -0,0 +1,77 @@
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/darcula/img/filter.svg b/bauh/view/resources/style/darcula/img/filter.svg
new file mode 100644
index 00000000..4a1153ab
--- /dev/null
+++ b/bauh/view/resources/style/darcula/img/filter.svg
@@ -0,0 +1,108 @@
+
+image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/darcula/img/filter_focused.svg b/bauh/view/resources/style/darcula/img/filter_focused.svg
new file mode 100644
index 00000000..842a24ad
--- /dev/null
+++ b/bauh/view/resources/style/darcula/img/filter_focused.svg
@@ -0,0 +1,108 @@
+
+image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/img/about.svg b/bauh/view/resources/style/darcula/img/help.svg
old mode 100755
new mode 100644
similarity index 57%
rename from bauh/view/resources/img/about.svg
rename to bauh/view/resources/style/darcula/img/help.svg
index 008de7e6..29aafd06
--- a/bauh/view/resources/img/about.svg
+++ b/bauh/view/resources/style/darcula/img/help.svg
@@ -1,6 +1,4 @@
-
-
image/svg+xml
+ inkscape:current-layer="Capa_1"
+ inkscape:document-rotation="0" />
-
\ No newline at end of file
+ style="fill:#3592c4;fill-opacity:1;stroke:#3592c4;stroke-width:1.88256;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 255.87109 2.9414062 A 252.84608 253.05907 0 0 0 3.1542969 256 A 252.84608 253.05907 0 0 0 256 509.05859 A 252.84608 253.05907 0 0 0 508.8457 256 A 252.84608 253.05907 0 0 0 256 2.9414062 A 252.84608 253.05907 0 0 0 255.87109 2.9414062 z M 252.38672 122.42188 C 298.55766 122.42188 344.01367 143.70333 344.01367 194.60742 C 344.01367 241.54994 290.26733 259.60406 278.72461 276.56641 C 270.05905 289.1838 272.95161 306.91211 249.14258 306.91211 C 233.63311 306.91211 226.05859 294.28684 226.05859 282.73438 C 226.05859 239.74492 289.16797 230.01576 289.16797 194.61719 C 289.16797 175.13307 276.21203 163.58008 254.55664 163.58008 C 208.3857 163.58008 226.41598 211.2207 191.44727 211.2207 C 178.82347 211.2207 167.98633 203.63908 167.98633 189.20703 C 167.97733 153.79994 208.3694 122.42188 252.38672 122.42188 z M 250.58984 330.34961 C 266.79735 330.34961 280.16992 343.69063 280.16992 359.96289 C 280.16992 376.23515 266.82288 389.57812 250.58984 389.57812 C 234.35681 389.57812 221.00195 376.2522 221.00195 359.96289 C 221.00195 343.69914 234.35681 330.34961 250.58984 330.34961 z " />
diff --git a/bauh/view/resources/style/darcula/img/history.svg b/bauh/view/resources/style/darcula/img/history.svg
new file mode 100644
index 00000000..a8015077
--- /dev/null
+++ b/bauh/view/resources/style/darcula/img/history.svg
@@ -0,0 +1,73 @@
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/darcula/img/ignore_update.svg b/bauh/view/resources/style/darcula/img/ignore_update.svg
new file mode 100644
index 00000000..274f5542
--- /dev/null
+++ b/bauh/view/resources/style/darcula/img/ignore_update.svg
@@ -0,0 +1,72 @@
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/img/app_info.svg b/bauh/view/resources/style/darcula/img/info.svg
similarity index 75%
rename from bauh/view/resources/img/app_info.svg
rename to bauh/view/resources/style/darcula/img/info.svg
index d7c958e2..17272b51 100644
--- a/bauh/view/resources/img/app_info.svg
+++ b/bauh/view/resources/style/darcula/img/info.svg
@@ -9,12 +9,12 @@
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
id="Capa_1"
enable-background="new 0 0 512 512"
- height="12"
- viewBox="0 0 12 12"
- width="12"
+ height="512"
+ viewBox="0 0 512 512"
+ width="512"
version="1.1"
sodipodi:docname="app_info.svg"
- inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)">
+ inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)">
@@ -43,9 +43,9 @@
id="namedview873"
showgrid="false"
showborder="true"
- inkscape:zoom="11.335805"
- inkscape:cx="23.192934"
- inkscape:cy="10.264189"
+ inkscape:zoom="0.70848781"
+ inkscape:cx="186.38588"
+ inkscape:cy="282.80121"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
@@ -53,21 +53,22 @@
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
- fit-margin-bottom="0" />
+ fit-margin-bottom="0"
+ inkscape:document-rotation="0" />
+ transform="matrix(21.043431,0,0,20.083897,96.253524,4.3628939)"
+ style="stroke:#3592c4;stroke-width:1.53208;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1">
+ style="fill:none;stroke:#3592c4;stroke-width:1.53208;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
diff --git a/bauh/view/resources/style/darcula/img/installed.svg b/bauh/view/resources/style/darcula/img/installed.svg
new file mode 100644
index 00000000..2c402278
--- /dev/null
+++ b/bauh/view/resources/style/darcula/img/installed.svg
@@ -0,0 +1,78 @@
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/darcula/img/menu.svg b/bauh/view/resources/style/darcula/img/menu.svg
new file mode 100644
index 00000000..d38057c2
--- /dev/null
+++ b/bauh/view/resources/style/darcula/img/menu.svg
@@ -0,0 +1,67 @@
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/img/refresh.svg b/bauh/view/resources/style/darcula/img/refresh.svg
old mode 100755
new mode 100644
similarity index 81%
rename from bauh/view/resources/img/refresh.svg
rename to bauh/view/resources/style/darcula/img/refresh.svg
index 5c50f569..cdc8bae7
--- a/bauh/view/resources/img/refresh.svg
+++ b/bauh/view/resources/style/darcula/img/refresh.svg
@@ -1,6 +1,4 @@
-
-
image/svg+xml
+ fit-margin-bottom="0"
+ inkscape:document-rotation="0" />
+ transform="matrix(1.0938971,0,0,1.0217723,-10.489433,7.0809411)"
+ style="fill:#3592c4;fill-opacity:1;stroke:none;stroke-width:39.2413;stroke-miterlimit:4;stroke-dasharray:none">
+ style="fill:#3592c4;fill-opacity:1;stroke:none;stroke-width:39.2413;stroke-miterlimit:4;stroke-dasharray:none">
+ style="fill:#3592c4;fill-opacity:1;stroke:none;stroke-width:39.2413;stroke-miterlimit:4;stroke-dasharray:none" />
+ style="fill:#3592c4;fill-opacity:1;stroke:none;stroke-width:39.2413;stroke-miterlimit:4;stroke-dasharray:none" />
-
\ No newline at end of file
+
diff --git a/bauh/view/resources/img/revert_update_ignored.svg b/bauh/view/resources/style/darcula/img/revert_update_ignored.svg
similarity index 58%
rename from bauh/view/resources/img/revert_update_ignored.svg
rename to bauh/view/resources/style/darcula/img/revert_update_ignored.svg
index b327932f..800ff58c 100644
--- a/bauh/view/resources/img/revert_update_ignored.svg
+++ b/bauh/view/resources/style/darcula/img/revert_update_ignored.svg
@@ -11,12 +11,12 @@
id="Capa_1"
x="0px"
y="0px"
- viewBox="0 0 23.515307 24"
+ viewBox="0 0 512.00001 512"
xml:space="preserve"
sodipodi:docname="revert_update_ignored.svg"
- width="23.515306"
- height="24"
- inkscape:version="1.0 (4035a4fb49, 2020-05-01)">image/svg+xml
diff --git a/bauh/view/resources/style/darcula/img/search.svg b/bauh/view/resources/style/darcula/img/search.svg
new file mode 100644
index 00000000..3ab35012
--- /dev/null
+++ b/bauh/view/resources/style/darcula/img/search.svg
@@ -0,0 +1,119 @@
+
+image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/img/search.svg b/bauh/view/resources/style/darcula/img/search_focused.svg
old mode 100755
new mode 100644
similarity index 83%
rename from bauh/view/resources/img/search.svg
rename to bauh/view/resources/style/darcula/img/search_focused.svg
index 1519d2f8..d067d7e5
--- a/bauh/view/resources/img/search.svg
+++ b/bauh/view/resources/style/darcula/img/search_focused.svg
@@ -1,6 +1,4 @@
-
-
image/svg+xml
+ fit-margin-bottom="0"
+ inkscape:document-rotation="0" />
+ transform="matrix(2.0004545,0,0,2.0004545,5.6311166,5.6311166)"
+ style="fill:#6591b2;fill-opacity:1">
@@ -117,4 +116,4 @@
id="g119"
transform="translate(0,-238.312)">
-
\ No newline at end of file
+
diff --git a/bauh/view/resources/style/darcula/img/settings.svg b/bauh/view/resources/style/darcula/img/settings.svg
new file mode 100644
index 00000000..35939d3d
--- /dev/null
+++ b/bauh/view/resources/style/darcula/img/settings.svg
@@ -0,0 +1,102 @@
+
+image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/darcula/img/suggestions.svg b/bauh/view/resources/style/darcula/img/suggestions.svg
new file mode 100644
index 00000000..2c69f576
--- /dev/null
+++ b/bauh/view/resources/style/darcula/img/suggestions.svg
@@ -0,0 +1,137 @@
+
+image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/darcula/img/themes.svg b/bauh/view/resources/style/darcula/img/themes.svg
new file mode 100644
index 00000000..d69c345a
--- /dev/null
+++ b/bauh/view/resources/style/darcula/img/themes.svg
@@ -0,0 +1,56 @@
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/darcula/img/update.svg b/bauh/view/resources/style/darcula/img/update.svg
new file mode 100644
index 00000000..7af4e7ca
--- /dev/null
+++ b/bauh/view/resources/style/darcula/img/update.svg
@@ -0,0 +1,77 @@
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/img/app_update.svg b/bauh/view/resources/style/darcula/img/update_unchecked.svg
old mode 100755
new mode 100644
similarity index 75%
rename from bauh/view/resources/img/app_update.svg
rename to bauh/view/resources/style/darcula/img/update_unchecked.svg
index 0037b9e9..4a11e81d
--- a/bauh/view/resources/img/app_update.svg
+++ b/bauh/view/resources/style/darcula/img/update_unchecked.svg
@@ -8,13 +8,13 @@
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
- viewBox="0 0 48.000008 47.999886"
+ viewBox="0 0 512 512"
enable-background="new 0 0 26 26"
id="svg8"
- sodipodi:docname="update_white.svg"
- inkscape:version="0.92.4 5da689c313, 2019-01-14"
- width="48.000008"
- height="47.999886">
+ sodipodi:docname="update_unchecked.svg"
+ inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)"
+ width="512"
+ height="512">
@@ -42,32 +42,33 @@
inkscape:window-height="739"
id="namedview10"
showgrid="false"
- showborder="false"
- inkscape:zoom="3.2091769"
- inkscape:cx="11.492348"
- inkscape:cy="-1.3206439"
+ showborder="true"
+ inkscape:zoom="0.56730769"
+ inkscape:cx="154.91145"
+ inkscape:cy="139.84916"
inkscape:window-x="0"
- inkscape:window-y="432"
+ inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg8"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
- fit-margin-bottom="0" />
+ fit-margin-bottom="0"
+ inkscape:document-rotation="0" />
+ transform="matrix(19.331463,0,0,20.93888,4.690981,-16.20544)"
+ style="fill:#a9b7c6;fill-opacity:1;stroke:#75787c;stroke-width:0.0520416;stroke-opacity:1">
+ style="fill:#a9b7c6;fill-opacity:1;stroke:#75787c;stroke-width:0.0520416;stroke-opacity:1" />
+ style="fill:#a9b7c6;fill-opacity:1;stroke:#75787c;stroke-width:0.0520416;stroke-opacity:1" />
+ width="512"
+ height="512"
+ inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)">
@@ -42,21 +42,22 @@
inkscape:window-height="739"
id="namedview819"
showgrid="false"
- showborder="false"
+ showborder="true"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
- inkscape:zoom="13.489422"
- inkscape:cx="11.120405"
- inkscape:cy="5.0927218"
+ inkscape:zoom="0.84308888"
+ inkscape:cx="364.16486"
+ inkscape:cy="281.60119"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
- inkscape:current-layer="svg817" />
+ inkscape:current-layer="svg817"
+ inkscape:document-rotation="0" />
+ style="fill:#499c54;fill-opacity:1;stroke-width:22.3376" />
diff --git a/bauh/view/resources/style/default/default.meta b/bauh/view/resources/style/default/default.meta
new file mode 100644
index 00000000..eb97b7a6
--- /dev/null
+++ b/bauh/view/resources/style/default/default.meta
@@ -0,0 +1,8 @@
+name=Default
+name[pt]=Padrão
+name[es]=Predeterminada
+description=Base theme without icons and colors
+description[pt]=Tema base sem ícones e cores
+description[es]=Tema base sin iconos y colores
+version=1.0
+abstract=true
\ No newline at end of file
diff --git a/bauh/view/resources/style/default/default.qss b/bauh/view/resources/style/default/default.qss
new file mode 100644
index 00000000..f4bbda03
--- /dev/null
+++ b/bauh/view/resources/style/default/default.qss
@@ -0,0 +1,368 @@
+QPushButton, QToolButton {
+ padding: 5px;
+}
+
+QComboBox, QLineEdit, QSpinBox {
+ border-width: 1px;
+ border-style: solid;
+ border-radius: 5px;
+ padding: 3px;
+}
+
+QComboxBox:!editable, QComboBox::drop-down {
+ border-left: 0;
+}
+
+QComboBox::down-arrow {
+ height: 8px;
+ width: 8px;
+ padding-right: 10px;
+}
+
+QSpinBox::up-arrow:disabled, QSpinBox::up-arrow:off, QSpinBox::down-arrow:disabled, QSpinBox::down-arrow:off {
+ image: none;
+}
+
+QCheckBox::indicator {
+ width: 18px;
+ height: 18px;
+}
+
+QTableWidget QToolButton {
+ padding: 5px;
+ margin-top: 3px;
+ margin-bottom: 3px;
+}
+
+QTableWidget QToolButton#bt_task {
+ margin-top: 1px;
+ margin-bottom: 1px;
+}
+
+QTableWidget QToolButton[text_only = "true"] {
+ min-width: 80px;
+}
+
+QMenu QPushButton {
+ border: 0px;
+ background: none;
+ padding: 5px;
+ text-align: left;
+}
+
+QLabel[help_icon = "true"] {
+ qproperty-scaledContents: True;
+ min-height: 16;
+ max-height: 16;
+ min-width: 16;
+ max-width: 16;
+}
+
+QLabel[tip_icon = "true"] {
+ qproperty-scaledContents: True;
+ min-height: 12;
+ max-height: 12;
+ min-width: 12;
+ max-width: 12;
+}
+
+QCustomToolbar QWidget[spacer = "true"] {
+ min-width: 5px;
+}
+
+FormQt {
+ font-weight: bold;
+ font-size: 12px;
+ border-radius: 6px;
+ margin-top: 6px;
+}
+
+FormQt::title {
+ subcontrol-origin: margin;
+ left: 7px;
+ padding: 0px 5px 0px 5px;
+}
+
+FormQt IconButton#clean_field {
+ min-width: 14px;
+ max-width: 14px;
+ min-height: 14px;
+ max-height: 14px;
+}
+
+QSearchBar QPushButton {
+ padding: inherit;
+}
+
+QSearchBar QLabel#lb_left_corner {
+ border-top-left-radius: 5px;
+ border-bottom-left-radius: 5px;
+ border-left-width: 1px;
+ border-left-style: solid;
+ border-top-width: 1px;
+ border-top-style: solid;
+ border-bottom-width: 1px;
+ border-bottom-style: solid;
+ border-right: 0px;
+}
+
+QSearchBar QPushButton#search_button {
+ qproperty-iconSize: 10px 10px;
+ padding-right: 10px;
+ border-top-right-radius: 5px;
+ border-bottom-right-radius: 5px;
+ border-right-width: 1px;
+ border-right-style: solid;
+ border-top-width: 1px;
+ border-top-style: solid;
+ border-bottom-width: 1px;
+ border-bottom-style: solid;
+ height: 30px;
+ border-left: none;
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
+}
+
+QSearchBar QLineEdit#inp_search {
+ spacing: 0;
+ height: 30px;
+ font-size: 12px;
+ width: 300px;
+ border-top-width: 1px;
+ border-top-style: solid;
+ border-bottom-width: 1px;
+ border-bottom-style: solid;
+ border-left: none;
+ border-right: none;
+ border-radius: 0;
+ padding: 0;
+}
+
+RadioSelectQt {
+ font-weight: bold;
+}
+
+RadioSelectQt#radio_select_notitle {
+ padding-top: 5px;
+}
+
+ComboSelectQt QGridLayout, RangeInputQt QGridLayout, TextInputQt QGridLayout {
+ margin-left: 0;
+}
+
+ComboSelectQt QLabel, RangeInputQt QLabel, TextInputQt QLabel {
+ font-weight: bold;
+}
+
+MultipleSelectQt {
+ font-weight: bold;
+ font-size: 12px;
+ border: 1px solid none;
+ border-radius: 6px;
+ margin-top: 6px;
+}
+
+MultipleSelectQt::title {
+ subcontrol-origin: margin;
+ left: 7px;
+ padding: 0px 5px 0px 5px;
+}
+
+ManageWindow QSearchBar#name_filter QLabel#lb_left_corner, ManageWindow QSearchBar#name_filter QLineEdit#inp_search, ManageWindow QSearchBar#name_filter QPushButton#search_button {
+ min-height: 27px;
+ max-height: 27px;
+ height: 27px;
+}
+
+ManageWindow QSearchBar#name_filter QPushButton#search_button {
+ qproperty-iconSize: 9px 9px;
+}
+
+ManageWindow QWidget#table_filters QPushButton[root = "true"] {
+ min-width: 100px;
+ max-width: 100px;
+ min-height: 18px;
+ max-height: 18px;
+}
+
+ManageWindow QLabel#label_status {
+ font-weight: bold
+}
+
+ManageWindow QWidget#table_filters QCheckBox::indicator {
+ width: 22px;
+ height: 22px;
+}
+
+ConfirmationDialog QPushButton, QWidget[container = "true"] QPushButton, InfoDialog QPushButton {
+ min-width: 80px;
+}
+
+ConfirmationDialog UpgradeToggleButton {
+ height: 10px;
+}
+
+PreparePanel QLabel#prepare_status {
+ font-size: 14px;
+ font-weight: bold;
+}
+
+PreparePanel QPushButton#bt_hide_details {
+ text-decoration: underline;
+ border: 0px;
+ background: none;
+}
+
+PreparePanel QLabel#task_status[status = "done"], QLabel#task_progress[status = "done"] {
+ text-decoration: line-through;
+}
+
+PreparePanel QTableWidget QToolButton {
+ min-width: 14px;
+ max-width: 14px;
+ min-height: 14px;
+ max-height: 14px;
+}
+
+ManageWindow QComboBox#combo_categories, ManageWindow QComboBox#combo_types {
+ height: 20px;
+ min-width: 130px;
+}
+
+ManageWindow QComboBox#combo_types {
+ qproperty-iconSize: 14px 14px;
+}
+
+ManageWindow QComboBox#combo_types > QLineEdit, ManageWindow QComboBox#combo_categories > QLineEdit {
+ height: 2px;
+}
+
+ManageWindow QSearchBar#name_filter {
+ min-width: 170;
+ max-width: 170;
+}
+
+ManageWindow QPushButton#bt_installed, ManageWindow QPushButton#bt_refresh, ManageWindow QPushButton#suggestions, ManageWindow QPushButton#bt_upgrade {
+ font-weight: 500;
+}
+
+PackagesTable QToolButton, PackagesTable UpgradeToggleButton {
+ min-width: 16px;
+ max-width: 16px;
+ min-height: 14px;
+ max-height: 14px;
+}
+
+PackagesTable QLabel#icon_publisher_verified {
+ qproperty-scaledContents: True;
+ min-height: 10;
+ max-height: 10;
+ min-width: 10;
+ max-width: 10;
+}
+
+PackagesTable QLabel[icon = "true"] {
+ qproperty-alignment: AlignCenter;
+}
+
+PackagesTable QToolButton#bt_install {
+ font-size: 10px;
+ font-weight: bold;
+}
+
+PackagesTable QToolButton#bt_uninstall {
+ font-size: 10px;
+ font-weight: bold;
+}
+
+PackagesTable QLabel#app_version[ignored = "true"] {
+ font-weight: bold;
+}
+
+AboutDialog QLabel {
+ qproperty-alignment: AlignCenter;
+}
+
+AboutDialog QLabel#logo {
+ qproperty-pixmap: url("@images/logo.svg");
+ qproperty-scaledContents: true;
+ min-height: 0.071041667%h;
+ max-height: 0.071041667%h;
+ min-width: 0.046852123%w;
+ max-width: 0.046852123%w;
+}
+
+AboutDialog QLabel#app_name {
+ font-size: 0.018229167%h;
+ font-weight: bold;
+}
+
+AboutDialog QLabel#app_version {
+ font-size: 0.014322917%h;
+ font-weight: bold;
+}
+
+AboutDialog QLabel#app_description {
+ min-width: 0.292397661%w;
+ font-size: 0.015625%h;
+ font-weight: bold;
+}
+
+AboutDialog QLabel#app_more_information, AboutDialog QLabel#app_license {
+ font-size: 0.014322917%h;
+}
+
+AboutDialog > QLabel#app_trouble_question, AboutDialog > QLabel#app_rate_question {
+ font-size: 0.013020833%h;
+ font-weight: bold;
+}
+
+AboutDialog > QLabel#app_trouble_answer, AboutDialog > QLabel#app_rate_answer {
+ font-size: 0.013020833%h;
+}
+
+RootDialog QLineEdit#password {
+ qproperty-alignment: AlignCenter;
+ qproperty-echoMode: Password;
+}
+
+RootDialog QLabel#message {
+ qproperty-alignment: AlignCenter;
+}
+
+RootDialog QLabel[error = "true"] {
+ qproperty-alignment: AlignCenter;
+}
+
+HistoryDialog QTableWidget QLabel[outdated = "true"] {
+ font-weight: bold;
+}
+
+HistoryDialog QTableWidget QLabel[outdated = "false"] {
+ font-weight: bold;
+}
+
+InfoDialog QLabel#field_name {
+ font-weight: bold;
+}
+
+InfoDialog QLineEdit#field_value {
+ width: 400px;
+}
+
+InfoDialog QPushButton#show {
+ min-height: 16px;
+ max-height: 16px;
+}
+
+ScreenshotsDialog QLabel#image {
+ qproperty-alignment: AlignCenter;
+}
+
+SettingsWindow TabGroupQt#settings {
+ min-width: 400px;
+}
+
+QMenu QPushButton[current = "true"] {
+ font-weight: bold;
+}
\ No newline at end of file
diff --git a/bauh/view/resources/style/light/img/about.svg b/bauh/view/resources/style/light/img/about.svg
new file mode 100644
index 00000000..418257cb
--- /dev/null
+++ b/bauh/view/resources/style/light/img/about.svg
@@ -0,0 +1,74 @@
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/light/img/app_play.svg b/bauh/view/resources/style/light/img/app_play.svg
new file mode 100755
index 00000000..78658635
--- /dev/null
+++ b/bauh/view/resources/style/light/img/app_play.svg
@@ -0,0 +1,155 @@
+
+image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/light/img/app_play_disabled.svg b/bauh/view/resources/style/light/img/app_play_disabled.svg
new file mode 100644
index 00000000..dc270ecf
--- /dev/null
+++ b/bauh/view/resources/style/light/img/app_play_disabled.svg
@@ -0,0 +1,156 @@
+
+image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/light/img/arrow_down.svg b/bauh/view/resources/style/light/img/arrow_down.svg
new file mode 100644
index 00000000..aa26288b
--- /dev/null
+++ b/bauh/view/resources/style/light/img/arrow_down.svg
@@ -0,0 +1,98 @@
+
+image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/light/img/arrow_up.svg b/bauh/view/resources/style/light/img/arrow_up.svg
new file mode 100644
index 00000000..ffafe634
--- /dev/null
+++ b/bauh/view/resources/style/light/img/arrow_up.svg
@@ -0,0 +1,98 @@
+
+image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/img/camera.svg b/bauh/view/resources/style/light/img/camera.svg
similarity index 86%
rename from bauh/view/resources/img/camera.svg
rename to bauh/view/resources/style/light/img/camera.svg
index ab0b48d9..8db4b60b 100755
--- a/bauh/view/resources/img/camera.svg
+++ b/bauh/view/resources/style/light/img/camera.svg
@@ -1,6 +1,4 @@
-
-
image/svg+xml
@@ -130,4 +130,4 @@
id="g938"
transform="matrix(1.1013612,0,0,1.1013612,-0.37597036,-486.86991)">
-
\ No newline at end of file
+
diff --git a/bauh/view/resources/style/light/img/camera_disabled.svg b/bauh/view/resources/style/light/img/camera_disabled.svg
new file mode 100644
index 00000000..2766fe63
--- /dev/null
+++ b/bauh/view/resources/style/light/img/camera_disabled.svg
@@ -0,0 +1,134 @@
+
+image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/light/img/checkbox_checked.svg b/bauh/view/resources/style/light/img/checkbox_checked.svg
new file mode 100644
index 00000000..b09aa825
--- /dev/null
+++ b/bauh/view/resources/style/light/img/checkbox_checked.svg
@@ -0,0 +1,102 @@
+
+
+
+
+
+
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/light/img/checkbox_checked_disabled.svg b/bauh/view/resources/style/light/img/checkbox_checked_disabled.svg
new file mode 100644
index 00000000..c1027388
--- /dev/null
+++ b/bauh/view/resources/style/light/img/checkbox_checked_disabled.svg
@@ -0,0 +1,102 @@
+
+
+
+
+
+
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/light/img/checkbox_unchecked.svg b/bauh/view/resources/style/light/img/checkbox_unchecked.svg
new file mode 100644
index 00000000..027ea5e2
--- /dev/null
+++ b/bauh/view/resources/style/light/img/checkbox_unchecked.svg
@@ -0,0 +1,88 @@
+
+
+
+
+
+
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/light/img/clean.svg b/bauh/view/resources/style/light/img/clean.svg
new file mode 100644
index 00000000..a317447e
--- /dev/null
+++ b/bauh/view/resources/style/light/img/clean.svg
@@ -0,0 +1,158 @@
+
+image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/img/downgrade.svg b/bauh/view/resources/style/light/img/downgrade.svg
similarity index 75%
rename from bauh/view/resources/img/downgrade.svg
rename to bauh/view/resources/style/light/img/downgrade.svg
index 7036e4ac..b728e33c 100755
--- a/bauh/view/resources/img/downgrade.svg
+++ b/bauh/view/resources/style/light/img/downgrade.svg
@@ -8,13 +8,13 @@
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
- viewBox="0 0 23.882839 24"
+ viewBox="0 0 512 512"
enable-background="new 0 0 26 26"
id="svg8"
sodipodi:docname="downgrade.svg"
- inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)"
- width="23.882839"
- height="24">
+ inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)"
+ width="512"
+ height="512">
@@ -42,10 +42,10 @@
inkscape:window-height="739"
id="namedview10"
showgrid="false"
- showborder="false"
- inkscape:zoom="9.0769232"
- inkscape:cx="-9.2627565"
- inkscape:cy="4.4534573"
+ showborder="true"
+ inkscape:zoom="0.80229424"
+ inkscape:cx="65.887412"
+ inkscape:cy="257.44157"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
@@ -53,20 +53,21 @@
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
- fit-margin-bottom="0" />
+ fit-margin-bottom="0"
+ inkscape:document-rotation="0" />
+ transform="matrix(19.334486,0,0,20.945696,4.651682,-16.294048)"
+ style="fill:#db5860;fill-opacity:1;stroke:none;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none">
+ style="fill:#db5860;fill-opacity:1;stroke:none;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none" />
+ style="fill:#db5860;fill-opacity:1;stroke:none;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none" />
diff --git a/bauh/view/resources/img/exclamation.svg b/bauh/view/resources/style/light/img/exclamation.svg
similarity index 100%
rename from bauh/view/resources/img/exclamation.svg
rename to bauh/view/resources/style/light/img/exclamation.svg
diff --git a/bauh/view/resources/style/light/img/filter.svg b/bauh/view/resources/style/light/img/filter.svg
new file mode 100644
index 00000000..cea7bb79
--- /dev/null
+++ b/bauh/view/resources/style/light/img/filter.svg
@@ -0,0 +1,108 @@
+
+image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/light/img/filter_focused.svg b/bauh/view/resources/style/light/img/filter_focused.svg
new file mode 100644
index 00000000..843eb313
--- /dev/null
+++ b/bauh/view/resources/style/light/img/filter_focused.svg
@@ -0,0 +1,108 @@
+
+image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/light/img/help.svg b/bauh/view/resources/style/light/img/help.svg
new file mode 100755
index 00000000..599ce476
--- /dev/null
+++ b/bauh/view/resources/style/light/img/help.svg
@@ -0,0 +1,122 @@
+
+image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/light/img/history.svg b/bauh/view/resources/style/light/img/history.svg
new file mode 100644
index 00000000..06cf1a9b
--- /dev/null
+++ b/bauh/view/resources/style/light/img/history.svg
@@ -0,0 +1,72 @@
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/light/img/ignore_update.svg b/bauh/view/resources/style/light/img/ignore_update.svg
new file mode 100644
index 00000000..f038c5ca
--- /dev/null
+++ b/bauh/view/resources/style/light/img/ignore_update.svg
@@ -0,0 +1,72 @@
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/light/img/installed.svg b/bauh/view/resources/style/light/img/installed.svg
new file mode 100644
index 00000000..491d2131
--- /dev/null
+++ b/bauh/view/resources/style/light/img/installed.svg
@@ -0,0 +1,77 @@
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/light/img/menu.svg b/bauh/view/resources/style/light/img/menu.svg
new file mode 100644
index 00000000..9e935a86
--- /dev/null
+++ b/bauh/view/resources/style/light/img/menu.svg
@@ -0,0 +1,64 @@
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/light/img/refresh.svg b/bauh/view/resources/style/light/img/refresh.svg
new file mode 100755
index 00000000..560cfe8f
--- /dev/null
+++ b/bauh/view/resources/style/light/img/refresh.svg
@@ -0,0 +1,128 @@
+
+image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/light/img/revert_update_ignored.svg b/bauh/view/resources/style/light/img/revert_update_ignored.svg
new file mode 100644
index 00000000..054714d7
--- /dev/null
+++ b/bauh/view/resources/style/light/img/revert_update_ignored.svg
@@ -0,0 +1,189 @@
+
+image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/light/img/search.svg b/bauh/view/resources/style/light/img/search.svg
new file mode 100755
index 00000000..9eb0f41b
--- /dev/null
+++ b/bauh/view/resources/style/light/img/search.svg
@@ -0,0 +1,119 @@
+
+image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/light/img/search_focused.svg b/bauh/view/resources/style/light/img/search_focused.svg
new file mode 100644
index 00000000..ba77ae06
--- /dev/null
+++ b/bauh/view/resources/style/light/img/search_focused.svg
@@ -0,0 +1,119 @@
+
+image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/light/img/settings.svg b/bauh/view/resources/style/light/img/settings.svg
new file mode 100644
index 00000000..0d454af8
--- /dev/null
+++ b/bauh/view/resources/style/light/img/settings.svg
@@ -0,0 +1,102 @@
+
+image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/light/img/suggestions.svg b/bauh/view/resources/style/light/img/suggestions.svg
new file mode 100644
index 00000000..c556de2e
--- /dev/null
+++ b/bauh/view/resources/style/light/img/suggestions.svg
@@ -0,0 +1,138 @@
+
+image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/light/img/themes.svg b/bauh/view/resources/style/light/img/themes.svg
new file mode 100644
index 00000000..d8707d5f
--- /dev/null
+++ b/bauh/view/resources/style/light/img/themes.svg
@@ -0,0 +1,57 @@
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/light/img/update.svg b/bauh/view/resources/style/light/img/update.svg
new file mode 100755
index 00000000..ea8c9be4
--- /dev/null
+++ b/bauh/view/resources/style/light/img/update.svg
@@ -0,0 +1,73 @@
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/light/img/upgrade.svg b/bauh/view/resources/style/light/img/upgrade.svg
new file mode 100644
index 00000000..d61863fc
--- /dev/null
+++ b/bauh/view/resources/style/light/img/upgrade.svg
@@ -0,0 +1,73 @@
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/img/checked.svg b/bauh/view/resources/style/light/img/verified.svg
old mode 100755
new mode 100644
similarity index 58%
rename from bauh/view/resources/img/checked.svg
rename to bauh/view/resources/style/light/img/verified.svg
index 3a224b5c..7123c05a
--- a/bauh/view/resources/img/checked.svg
+++ b/bauh/view/resources/style/light/img/verified.svg
@@ -8,13 +8,13 @@
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
- viewBox="0 0 11.999997 12"
+ viewBox="0 0 512.00001 512"
enable-background="new 0 0 26 26"
id="svg817"
- sodipodi:docname="checked.svg"
- width="11.999997"
- height="12"
- inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)">
+ sodipodi:docname="verified.svg"
+ width="512"
+ height="512"
+ inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)">
@@ -42,21 +42,22 @@
inkscape:window-height="739"
id="namedview819"
showgrid="false"
- showborder="false"
+ showborder="true"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
- inkscape:zoom="9.5384616"
- inkscape:cx="-31.459683"
- inkscape:cy="14.456686"
+ inkscape:zoom="0.84308888"
+ inkscape:cx="364.16486"
+ inkscape:cy="281.60119"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
- inkscape:current-layer="svg817" />
+ inkscape:current-layer="svg817"
+ inkscape:document-rotation="0" />
+ style="fill:#59a869;fill-opacity:1;stroke-width:22.3376" />
diff --git a/bauh/view/resources/style/light/light.meta b/bauh/view/resources/style/light/light.meta
new file mode 100644
index 00000000..5f87b579
--- /dev/null
+++ b/bauh/view/resources/style/light/light.meta
@@ -0,0 +1,8 @@
+name=Light
+name[pt]=Claro
+name[es]=Claro
+description=Default light theme
+description[pt]=Tema padrão claro
+description[es]=Tema predeterminado claro
+version=1.0
+root_sheet=default
\ No newline at end of file
diff --git a/bauh/view/resources/style/light/light.qss b/bauh/view/resources/style/light/light.qss
new file mode 100644
index 00000000..cdf0531e
--- /dev/null
+++ b/bauh/view/resources/style/light/light.qss
@@ -0,0 +1,300 @@
+QWidget QToolTip {
+ background-color: @tooltip.background.color;
+ color: @tooltip.font.color;
+}
+
+QLineEdit, QComboBox, QSpinBox {
+ background-color: @field.background.color;
+ border-color: @border.color;
+ color: @font.color;
+}
+
+QLineEdit:disabled, QComboBox:disabled, QSpingBox:disabled {
+ background-color: @field.disabled.background.color;
+}
+
+QMenu QPushButton, QComboBox QAbstractItemView, QMenu QAbstractItemView {
+ background-color: @field.background.color;
+ color: @font.color;
+}
+
+QWidget, QAbstractItemView {
+ selection-background-color: @menu.item.selected.background.color;
+ selection-color: @menu.item.selected.font.color;
+}
+
+QProgressBar::chunk {
+ background-color: @progressbar.fill.color;
+}
+
+QCheckBox::indicator:unchecked {
+ image: url("@style_dir/img/checkbox_unchecked.svg");
+}
+
+QCheckBox::indicator:checked {
+ image: url("@style_dir/img/checkbox_checked.svg");
+}
+
+QCheckBox::indicator:checked:disabled {
+ image: url("@style_dir/img/checkbox_checked_disabled.svg");
+}
+
+QMenu QPushButton:hover {
+ background-color: @menu.item.selected.background.color;
+}
+
+QMenu#app_actions QPushButton#app_history {
+ qproperty-icon: url("@style_dir/img/history.svg");
+}
+
+QMenu#app_actions QPushButton#app_downgrade {
+ qproperty-icon: url("@style_dir/img/downgrade.svg");
+}
+
+QMenu#app_actions QPushButton#ignore_updates {
+ qproperty-icon: url("@style_dir/img/ignore_update.svg");
+}
+
+QMenu#app_actions QPushButton#revert_ignore_updates {
+ qproperty-icon: url("@style_dir/img/revert_update_ignored.svg");
+}
+
+QLabel[error = "true"] {
+ color: @error.color;
+}
+
+QLabel[help_icon = "true"] {
+ qproperty-pixmap: url("@style_dir/img/help.svg");
+}
+
+QLabel[tip_icon = "true"] {
+ qproperty-pixmap: url("@style_dir/img/help.svg");
+}
+
+QPlainTextEdit[console = 'true'] {
+ background: @console.background.color;
+ color: @console.font.color;
+}
+
+QPushButton#ok {
+ background: @button_ok.background.color;
+ border-color: @button_ok.border.color;
+ color: @button_ok.font.color;
+ font-weight: bold;
+}
+
+QPushButton#ok:disabled {
+ background-color: @disabled.color;
+}
+
+MultipleSelectQt {
+ border-color: @border.color;
+}
+
+QSearchBar QLabel#lb_left_corner {
+ border-left-color: @border.color;
+ border-top-color: @border.color;
+ border-bottom-color: @border.color;
+ background-color: @field.background.color;
+}
+
+QSearchBar QLabel#lb_left_corner:disabled {
+ background-color: @field.disabled.background.color;
+}
+
+QSearchBar QPushButton#search_button {
+ qproperty-icon: url("@style_dir/img/search.svg");
+ border-right-color: @border.color;
+ border-top-color: @border.color;
+ border-bottom-color: @border.color;
+ background-color: @field.background.color;
+}
+
+QSearchBar QPushButton#search_button:disabled {
+ background-color: @field.disabled.background.color;
+}
+
+QSearchBar QPushButton#search_button[focused = "true"] {
+ qproperty-icon: url("@style_dir/img/search_focused.svg");
+}
+
+QSearchBar QLineEdit#inp_search {
+ border-bottom-color: @border.color;
+ border-top-color: @border.color;
+ background-color: @field.background.color;
+ color: @color.grey;
+}
+
+QSearchBar QLineEdit#inp_search:disabled {
+ background-color: @field.disabled.background.color;
+}
+
+QSearchBar#name_filter QPushButton#search_button {
+ qproperty-icon: url("@style_dir/img/filter.svg");
+}
+
+QSearchBar#name_filter QPushButton#search_button[focused = "true"] {
+ qproperty-icon: url("@style_dir/img/filter_focused.svg");
+}
+
+FormQt IconButton#clean_field {
+ qproperty-icon: url("@style_dir/img/clean.svg");
+}
+
+PreparePanel QTableWidget#tasks {
+ background-color: @task.background.color;
+}
+
+PreparePanel QLabel#task_status[status = "running"], QLabel#task_progress[status = "running"], QLabel#task_substatus {
+ font-weight: bold;
+}
+
+PreparePanel QLabel#task_status[status = "done"], QLabel#task_progress[status = "done"] {
+ color: @task.done.color;
+}
+
+UpgradeToggleButton {
+ qproperty-icon: url("@style_dir/img/update.svg");
+ qproperty-iconSize: 14px 14px;
+ background-color: @upgrade.color;
+}
+
+UpgradeToggleButton:checked, UpgradeToggleButton:disabled {
+ background-color: @disabled.color;
+}
+
+UpgradeToggleButton[enabled = "false"] {
+ qproperty-icon: url("@style_dir/img/exclamation.svg");
+ background: @button_upgrade.disabled.background.color;
+}
+
+QMenu QPushButton:hover {
+ background-color: @menu.item.selected.background.color;
+ color: @menu.item.selected.font.color;
+}
+
+QComboBox::down-arrow, QSpinBox::down-arrow {
+ image: url("@style_dir/img/arrow_down.svg");
+}
+
+QSpinBox::up-arrow {
+ image: url("@style_dir/img/arrow_up.svg");
+}
+
+ManageWindow QPushButton#bt_refresh {
+ qproperty-icon: url("@style_dir/img/refresh.svg");
+ qproperty-iconSize: 16px 16px;
+}
+
+ManageWindow QPushButton#bt_installed {
+ qproperty-icon: url("@style_dir/img/installed.svg");
+}
+
+ManageWindow QPushButton#bt_upgrade {
+ qproperty-icon: url("@style_dir/img/upgrade.svg");
+}
+
+ManageWindow IconButton#suggestions {
+ qproperty-icon: url("@style_dir/img/suggestions.svg");
+}
+
+ManageWindow IconButton#themes {
+ qproperty-icon: url("@style_dir/img/themes.svg");
+}
+
+ManageWindow IconButton#custom_actions {
+ qproperty-icon: url("@style_dir/img/menu.svg");
+}
+
+ManageWindow IconButton#settings {
+ qproperty-icon: url("@style_dir/img/settings.svg");
+ qproperty-iconSize: 16px 16px;
+}
+
+ManageWindow IconButton#about {
+ qproperty-icon: url("@style_dir/img/about.svg");
+ qproperty-iconSize: 14px 14px;
+}
+
+PackagesTable QLabel#icon_publisher_verified {
+ qproperty-pixmap: url("@style_dir/img/verified.svg");
+}
+
+PackagesTable QLabel[publisher_known = "false"]{
+ color: @table.app_publisher.unknown.font.color;
+}
+
+PackagesTable QLabel:disabled[publisher_known = "false"]{
+ color: @disabled.color;
+}
+
+PackagesTable IconButton#app_run {
+ qproperty-icon: url("@style_dir/img/app_play.svg");
+ qproperty-iconSize: 12px 12px;
+}
+
+PackagesTable IconButton#app_run[_enabled = "false"] {
+ qproperty-icon: url("@style_dir/img/app_play_disabled.svg");
+}
+
+PackagesTable IconButton#app_actions {
+ qproperty-icon: url("@style_dir/img/menu.svg");
+ qproperty-iconSize: 14px 14px;
+}
+
+PackagesTable IconButton#app_screenshots {
+ qproperty-icon: url("@style_dir/img/camera.svg");
+}
+
+PackagesTable IconButton#app_screenshots[_enabled = "false"] {
+ qproperty-icon: url("@style_dir/img/camera_disabled.svg");
+}
+
+PackagesTable IconButton#app_info {
+ qproperty-icon: url("@style_dir/img/about.svg");
+ qproperty-iconSize: 12px 12px;
+}
+
+PackagesTable QToolButton#bt_install {
+ background: @table.bt_install.background.color;
+ color: @table.bt_install.font.color;
+}
+
+PackagesTable QToolButton#bt_install:disabled {
+ background: @disabled.color;
+}
+
+PackagesTable QToolButton#bt_uninstall {
+ color: @table.bt_uninstall.background.color;
+}
+
+PackagesTable QToolButton#bt_uninstall:disabled {
+ color: @disabled.color;
+}
+
+PackagesTable QLabel#app_version[update = "true"] {
+ color: @table.app_version.update.color;
+ font-weight: @table.app_version.update.weight;
+}
+
+PackagesTable QLabel#app_version:disabled[update = "true"] {
+ color: @disabled.color;
+}
+
+PackagesTable QLabel#app_version[ignored = "true"] {
+ color: @table.app_version.update_ignored.color;
+}
+
+HistoryDialog QTableWidget QLabel[even = "false"] {
+ background-color: @outer_widget.background.color;
+}
+
+HistoryDialog QTableWidget QLabel[outdated = "true"] {
+ color: @history.version.focus.color;
+ background: @history.version.outdated;
+}
+
+HistoryDialog QTableWidget QLabel[outdated = "false"] {
+ color: @history.version.focus.color;
+ background: @history.version.updated;
+}
diff --git a/bauh/view/resources/style/light/light.vars b/bauh/view/resources/style/light/light.vars
new file mode 100644
index 00000000..5f4044a3
--- /dev/null
+++ b/bauh/view/resources/style/light/light.vars
@@ -0,0 +1,36 @@
+border.color=#CDCDCD
+button_ok.border.color=#4279B5
+button_ok.background.color=#2675bf
+button_ok.font.color=white
+button_upgrade.disabled.background.color=#dda739
+color.blue_light=#37abc8
+color.green=#59a869
+color.grey=#6e6e6e
+color.grey_light=#cdcdcd
+color.red=#DB5860
+color.warning=#EBC700
+console.background.color=white
+console.font.color=@font.color
+disabled.color=@color.grey_light
+error.color=@color.red
+field.background.color=white
+field.disabled.background.color=transparent
+font.color=black
+history.version.focus.color=white
+history.version.outdated=@color.warning
+history.version.updated=@color.green
+progressbar.fill.color=#3991E9
+menu.item.selected.background.color=#a6d2ff
+menu.item.selected.font.color=black
+table.app_publisher.unknown.font.color=@color.red
+table.app_version.update.color=@color.green
+table.app_version.update.weight=bold
+table.app_version.update_ignored.color=@color.red
+table.bt_install.background.color=@color.green
+table.bt_install.font.color=white
+table.bt_uninstall.background.color=@color.red
+task.background.color=transparent
+task.done.color=@color.grey
+tooltip.background.color=white
+tooltip.font.color=black
+upgrade.color=@color.green
diff --git a/bauh/view/resources/img/question.svg b/bauh/view/resources/style/sublime/img/about.svg
similarity index 72%
rename from bauh/view/resources/img/question.svg
rename to bauh/view/resources/style/sublime/img/about.svg
index 1ad2bd4f..93f43b04 100644
--- a/bauh/view/resources/img/question.svg
+++ b/bauh/view/resources/style/sublime/img/about.svg
@@ -9,12 +9,12 @@
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
id="Capa_1"
enable-background="new 0 0 512 512"
- height="23.967955"
- viewBox="0 0 15.215549 23.967955"
- width="15.215549"
+ height="512"
+ viewBox="0 0 512 512"
+ width="512"
version="1.1"
- sodipodi:docname="question.svg"
- inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)">
+ sodipodi:docname="about.svg"
+ inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)">
@@ -42,10 +42,10 @@
inkscape:window-height="739"
id="namedview873"
showgrid="false"
- showborder="false"
- inkscape:zoom="5.6679028"
- inkscape:cx="-23.418989"
- inkscape:cy="9.6513721"
+ showborder="true"
+ inkscape:zoom="1.0019531"
+ inkscape:cx="197.9376"
+ inkscape:cy="236.73295"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
@@ -53,21 +53,22 @@
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
- fit-margin-bottom="0" />
+ fit-margin-bottom="0"
+ inkscape:document-rotation="0" />
+ transform="matrix(22.337858,0,0,21.3193,86.427173,-11.115837)"
+ style="fill:#d8d773;fill-opacity:1;stroke:none;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1">
+ style="fill:#d8d773;fill-opacity:1;stroke:none;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
diff --git a/bauh/view/resources/style/sublime/img/app_play.svg b/bauh/view/resources/style/sublime/img/app_play.svg
new file mode 100755
index 00000000..ebd3b5ef
--- /dev/null
+++ b/bauh/view/resources/style/sublime/img/app_play.svg
@@ -0,0 +1,155 @@
+
+image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/sublime/img/app_play_disabled.svg b/bauh/view/resources/style/sublime/img/app_play_disabled.svg
new file mode 100644
index 00000000..15d64bba
--- /dev/null
+++ b/bauh/view/resources/style/sublime/img/app_play_disabled.svg
@@ -0,0 +1,155 @@
+
+image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/sublime/img/arrow_down.svg b/bauh/view/resources/style/sublime/img/arrow_down.svg
new file mode 100644
index 00000000..e0027557
--- /dev/null
+++ b/bauh/view/resources/style/sublime/img/arrow_down.svg
@@ -0,0 +1,98 @@
+
+image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/sublime/img/arrow_up.svg b/bauh/view/resources/style/sublime/img/arrow_up.svg
new file mode 100644
index 00000000..f4290e9e
--- /dev/null
+++ b/bauh/view/resources/style/sublime/img/arrow_up.svg
@@ -0,0 +1,98 @@
+
+image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/sublime/img/camera.svg b/bauh/view/resources/style/sublime/img/camera.svg
new file mode 100755
index 00000000..73f14bea
--- /dev/null
+++ b/bauh/view/resources/style/sublime/img/camera.svg
@@ -0,0 +1,133 @@
+
+image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/sublime/img/camera_disabled.svg b/bauh/view/resources/style/sublime/img/camera_disabled.svg
new file mode 100644
index 00000000..af4f075a
--- /dev/null
+++ b/bauh/view/resources/style/sublime/img/camera_disabled.svg
@@ -0,0 +1,135 @@
+
+image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/sublime/img/checkbox_checked.svg b/bauh/view/resources/style/sublime/img/checkbox_checked.svg
new file mode 100644
index 00000000..945672e1
--- /dev/null
+++ b/bauh/view/resources/style/sublime/img/checkbox_checked.svg
@@ -0,0 +1,99 @@
+
+
+
+
+
+
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/sublime/img/checkbox_checked_disabled.svg b/bauh/view/resources/style/sublime/img/checkbox_checked_disabled.svg
new file mode 100644
index 00000000..82625922
--- /dev/null
+++ b/bauh/view/resources/style/sublime/img/checkbox_checked_disabled.svg
@@ -0,0 +1,99 @@
+
+
+
+
+
+
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/sublime/img/checkbox_unchecked.svg b/bauh/view/resources/style/sublime/img/checkbox_unchecked.svg
new file mode 100644
index 00000000..3e58f540
--- /dev/null
+++ b/bauh/view/resources/style/sublime/img/checkbox_unchecked.svg
@@ -0,0 +1,88 @@
+
+
+
+
+
+
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/sublime/img/clean.svg b/bauh/view/resources/style/sublime/img/clean.svg
new file mode 100644
index 00000000..6b580cb4
--- /dev/null
+++ b/bauh/view/resources/style/sublime/img/clean.svg
@@ -0,0 +1,158 @@
+
+image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/sublime/img/downgrade.svg b/bauh/view/resources/style/sublime/img/downgrade.svg
new file mode 100755
index 00000000..b0495498
--- /dev/null
+++ b/bauh/view/resources/style/sublime/img/downgrade.svg
@@ -0,0 +1,73 @@
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/sublime/img/exclamation.svg b/bauh/view/resources/style/sublime/img/exclamation.svg
new file mode 100644
index 00000000..fae3701d
--- /dev/null
+++ b/bauh/view/resources/style/sublime/img/exclamation.svg
@@ -0,0 +1,118 @@
+
+
+
+image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/bauh/view/resources/style/sublime/img/filter.svg b/bauh/view/resources/style/sublime/img/filter.svg
new file mode 100644
index 00000000..47fc665b
--- /dev/null
+++ b/bauh/view/resources/style/sublime/img/filter.svg
@@ -0,0 +1,108 @@
+
+image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/sublime/img/filter_focused.svg b/bauh/view/resources/style/sublime/img/filter_focused.svg
new file mode 100644
index 00000000..2bc75621
--- /dev/null
+++ b/bauh/view/resources/style/sublime/img/filter_focused.svg
@@ -0,0 +1,108 @@
+
+image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/sublime/img/help.svg b/bauh/view/resources/style/sublime/img/help.svg
new file mode 100755
index 00000000..6af6bec1
--- /dev/null
+++ b/bauh/view/resources/style/sublime/img/help.svg
@@ -0,0 +1,122 @@
+
+image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/sublime/img/history.svg b/bauh/view/resources/style/sublime/img/history.svg
new file mode 100644
index 00000000..c233eac0
--- /dev/null
+++ b/bauh/view/resources/style/sublime/img/history.svg
@@ -0,0 +1,73 @@
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/sublime/img/ignore_update.svg b/bauh/view/resources/style/sublime/img/ignore_update.svg
new file mode 100644
index 00000000..38da2ae0
--- /dev/null
+++ b/bauh/view/resources/style/sublime/img/ignore_update.svg
@@ -0,0 +1,72 @@
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/sublime/img/installed.svg b/bauh/view/resources/style/sublime/img/installed.svg
new file mode 100644
index 00000000..07dcdbf9
--- /dev/null
+++ b/bauh/view/resources/style/sublime/img/installed.svg
@@ -0,0 +1,78 @@
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/sublime/img/menu.svg b/bauh/view/resources/style/sublime/img/menu.svg
new file mode 100644
index 00000000..925c6be3
--- /dev/null
+++ b/bauh/view/resources/style/sublime/img/menu.svg
@@ -0,0 +1,64 @@
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/sublime/img/refresh.svg b/bauh/view/resources/style/sublime/img/refresh.svg
new file mode 100755
index 00000000..fe1557d0
--- /dev/null
+++ b/bauh/view/resources/style/sublime/img/refresh.svg
@@ -0,0 +1,128 @@
+
+image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/sublime/img/revert_update_ignored.svg b/bauh/view/resources/style/sublime/img/revert_update_ignored.svg
new file mode 100644
index 00000000..36379375
--- /dev/null
+++ b/bauh/view/resources/style/sublime/img/revert_update_ignored.svg
@@ -0,0 +1,189 @@
+
+image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/sublime/img/search.svg b/bauh/view/resources/style/sublime/img/search.svg
new file mode 100755
index 00000000..db7da9da
--- /dev/null
+++ b/bauh/view/resources/style/sublime/img/search.svg
@@ -0,0 +1,119 @@
+
+image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/sublime/img/search_focused.svg b/bauh/view/resources/style/sublime/img/search_focused.svg
new file mode 100644
index 00000000..716827c3
--- /dev/null
+++ b/bauh/view/resources/style/sublime/img/search_focused.svg
@@ -0,0 +1,119 @@
+
+image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/sublime/img/settings.svg b/bauh/view/resources/style/sublime/img/settings.svg
new file mode 100644
index 00000000..3962a460
--- /dev/null
+++ b/bauh/view/resources/style/sublime/img/settings.svg
@@ -0,0 +1,102 @@
+
+image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/sublime/img/suggestions.svg b/bauh/view/resources/style/sublime/img/suggestions.svg
new file mode 100644
index 00000000..c7cfca9e
--- /dev/null
+++ b/bauh/view/resources/style/sublime/img/suggestions.svg
@@ -0,0 +1,106 @@
+
+image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/sublime/img/themes.svg b/bauh/view/resources/style/sublime/img/themes.svg
new file mode 100644
index 00000000..f2ddd18f
--- /dev/null
+++ b/bauh/view/resources/style/sublime/img/themes.svg
@@ -0,0 +1,57 @@
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/sublime/img/upgrade.svg b/bauh/view/resources/style/sublime/img/upgrade.svg
new file mode 100644
index 00000000..4bdcca2b
--- /dev/null
+++ b/bauh/view/resources/style/sublime/img/upgrade.svg
@@ -0,0 +1,73 @@
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/sublime/img/upgrade_unchecked.svg b/bauh/view/resources/style/sublime/img/upgrade_unchecked.svg
new file mode 100644
index 00000000..2619e6bd
--- /dev/null
+++ b/bauh/view/resources/style/sublime/img/upgrade_unchecked.svg
@@ -0,0 +1,76 @@
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/sublime/img/verified.svg b/bauh/view/resources/style/sublime/img/verified.svg
new file mode 100644
index 00000000..99954b7b
--- /dev/null
+++ b/bauh/view/resources/style/sublime/img/verified.svg
@@ -0,0 +1,63 @@
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
diff --git a/bauh/view/resources/style/sublime/sublime.meta b/bauh/view/resources/style/sublime/sublime.meta
new file mode 100644
index 00000000..f88e7909
--- /dev/null
+++ b/bauh/view/resources/style/sublime/sublime.meta
@@ -0,0 +1,6 @@
+name=Sublime
+description=Dark based on Sublime Text's editor theme
+description[pt]=Escuro baseado no tema do editor Sublime Text
+description[es]=Oscuro basado en el tema del editor Sublime Text
+version=1.0
+root_sheet=default
\ No newline at end of file
diff --git a/bauh/view/resources/style/sublime/sublime.qss b/bauh/view/resources/style/sublime/sublime.qss
new file mode 100644
index 00000000..f5a53612
--- /dev/null
+++ b/bauh/view/resources/style/sublime/sublime.qss
@@ -0,0 +1,335 @@
+QWidget, QWidget QToolTip {
+ background-color: @outer_widget.background.color;
+ color: @font.color;
+}
+
+QTableWidget {
+ background-color: @inner_widget.background.color;
+ selection-background-color: @menu.item.selected.background.color;
+}
+
+QTableWidget * {
+ background-color: none;
+ color: @font.color;
+}
+
+QTableWidget QAbstractButton {
+ background-color: @outer_widget.background.color;
+}
+
+QTableWidget QHeaderView {
+ background-color: @outer_widget.background.color;
+ font-weight: bold;
+}
+
+QLineEdit, QSpinBox {
+ selection-color: @menu.item.selected.font.color;
+ selection-background-color: @menu.item.selected.background.color;
+}
+
+QLineEdit, QComboBox, QSpinBox {
+ border-color: @border.color;
+}
+
+QCheckBox::indicator:unchecked {
+ image: url("@style_dir/img/checkbox_unchecked.svg");
+}
+
+QCheckBox::indicator:checked {
+ image: url("@style_dir/img/checkbox_checked.svg");
+}
+
+QCheckBox::indicator:checked:disabled {
+ image: url("@style_dir/img/checkbox_checked_disabled.svg");
+}
+
+QComboBox, QComboBox QAbstractItemView, QMenu QAbstractItemView {
+ selection-background-color: @menu.item.selected.background.color;
+ selection-color: @menu.item.selected.font.color;
+ color: @font.color;
+}
+
+QComboBox:focus, QLineEdit:focus, QSpinBox:focus {
+ border-color: @focus.border.color;
+ color: @focus.font.color;
+}
+
+QProgressBar::chunk {
+ background-color: @progressbar.fill.color;
+}
+
+QScrollBar, QTableWidget QScrollBar {
+ background-color: @inner_widget.background.color;
+}
+
+QScrollBar::vertical{
+ max-width: 10px;
+}
+
+QScrollBar::handle {
+ background-color: @scrollbar.handle.background.color;
+}
+
+QScrollBar::add-line, QScrollBar::sub-line, QScrollBar::add-page, QScrollBar::sub-page {
+ height: 0px;
+}
+
+QPushButton:disabled {
+ color: @outer_widget.background.color;
+}
+
+QMenu QPushButton[current = "true"] {
+ color: @theme.selected.color;
+}
+
+QMenu QPushButton:hover {
+ background-color: @menu.item.selected.background.color;
+ color: @menu.item.selected.font.color;
+}
+
+QMenu#app_actions QPushButton#app_history {
+ qproperty-icon: url("@style_dir/img/history.svg");
+}
+
+QMenu#app_actions QPushButton#app_downgrade {
+ qproperty-icon: url("@style_dir/img/downgrade.svg");
+}
+
+QMenu#app_actions QPushButton#ignore_updates {
+ qproperty-icon: url("@style_dir/img/ignore_update.svg");
+}
+
+QMenu#app_actions QPushButton#revert_ignore_updates {
+ qproperty-icon: url("@style_dir/img/revert_update_ignored.svg");
+}
+
+QLabel[error = "true"] {
+ color: @error.color;
+}
+
+QLabel[help_icon = "true"] {
+ qproperty-pixmap: url("@style_dir/img/help.svg");
+}
+
+QLabel[tip_icon = "true"] {
+ qproperty-pixmap: url("@style_dir/img/help.svg");
+}
+
+QPlainTextEdit {
+ background: @texteditor.background.color;
+ color: @texteditor.font.color;
+ selection-background-color: @texteditor.selection.background.color;
+ selection-color: @texteditor.selection.font.color;
+}
+
+QPushButton#ok {
+ color: @button_ok.font.color;
+}
+
+QPushButton#ok:disabled {
+ background-color: @disabled.color;
+}
+
+MultipleSelectQt {
+ border-color: @border.color;
+}
+
+QSearchBar QLabel#lb_left_corner {
+ border-left-color: @border.color;
+ border-top-color: @border.color;
+ border-bottom-color: @border.color;
+}
+
+QSearchBar QLabel#lb_left_corner[focused = "true"] {
+ border-left-color: @focus.border.color;
+ border-top-color: @focus.border.color;
+ border-bottom-color: @focus.border.color;
+}
+
+QSearchBar QPushButton#search_button {
+ qproperty-icon: url("@style_dir/img/search.svg");
+ border-right-color: @border.color;
+ border-top-color: @border.color;
+ border-bottom-color: @border.color;
+}
+
+
+QSearchBar QPushButton#search_button[focused = "true"] {
+ qproperty-icon: url("@style_dir/img/search_focused.svg");
+ border-right-color: @focus.border.color;
+ border-top-color: @focus.border.color;
+ border-bottom-color: @focus.border.color;
+}
+
+QSearchBar QLineEdit#inp_search {
+ border-bottom-color: @border.color;
+ border-top-color: @border.color;
+}
+
+QSearchBar QLineEdit#inp_search:focus {
+ border-bottom-color: @focus.border.color;
+ border-top-color: @focus.border.color;
+}
+
+QSearchBar#name_filter QPushButton#search_button {
+ qproperty-icon: url("@style_dir/img/filter.svg");
+}
+
+QSearchBar#name_filter QPushButton#search_button[focused = "true"] {
+ qproperty-icon: url("@style_dir/img/filter_focused.svg");
+}
+
+FormQt IconButton#clean_field {
+ qproperty-icon: url("@style_dir/img/clean.svg");
+}
+
+PreparePanel QTableWidget#tasks {
+ background-color: @task.background.color;
+}
+
+PreparePanel QLabel#task_status[status = "running"], QLabel#task_progress[status = "running"], QLabel#task_substatus {
+ color: @task.running.color;
+}
+
+PreparePanel QLabel#task_status[status = "done"], QLabel#task_progress[status = "done"] {
+ color: @task.done.color;
+}
+
+UpgradeToggleButton {
+ qproperty-icon: url("@style_dir/img/upgrade.svg");
+ qproperty-iconSize: 14px 14px;
+}
+
+UpgradeToggleButton[toggled = "false"] {
+ qproperty-icon: url("@style_dir/img/upgrade_unchecked.svg");
+ qproperty-iconSize: 14px 14px;
+}
+
+UpgradeToggleButton[enabled = "false"] {
+ qproperty-icon: url("@style_dir/img/exclamation.svg");
+ background: @button_upgrade.disabled.background.color;
+}
+
+QComboBox::down-arrow, QSpinBox::down-arrow {
+ image: url("@style_dir/img/arrow_down.svg");
+}
+
+QSpinBox::up-arrow {
+ image: url("@style_dir/img/arrow_up.svg");
+}
+
+ManageWindow QPushButton#bt_refresh {
+ qproperty-icon: url("@style_dir/img/refresh.svg");
+ qproperty-iconSize: 16px 16px;
+}
+
+ManageWindow QPushButton#bt_installed {
+ qproperty-icon: url("@style_dir/img/installed.svg");
+}
+
+ManageWindow QPushButton#bt_upgrade {
+ qproperty-icon: url("@style_dir/img/upgrade.svg");
+}
+
+ManageWindow IconButton#suggestions {
+ qproperty-icon: url("@style_dir/img/suggestions.svg");
+}
+
+ManageWindow IconButton#themes {
+ qproperty-icon: url("@style_dir/img/themes.svg");
+}
+
+ManageWindow IconButton#custom_actions {
+ qproperty-icon: url("@style_dir/img/menu.svg");
+}
+
+ManageWindow IconButton#settings {
+ qproperty-icon: url("@style_dir/img/settings.svg");
+ qproperty-iconSize: 16px 16px;
+}
+
+ManageWindow IconButton#about {
+ qproperty-icon: url("@style_dir/img/about.svg");
+ qproperty-iconSize: 14px 14px;
+}
+
+PackagesTable QLabel#icon_publisher_verified {
+ qproperty-pixmap: url("@style_dir/img/verified.svg");
+}
+
+PackagesTable QLabel[publisher_known = "false"]{
+ color: @table.app_publisher.unknown.font.color;
+}
+
+PackagesTable QLabel:disabled[publisher_known = "false"]{
+ color: @disabled.color;
+}
+
+PackagesTable IconButton#app_run {
+ qproperty-icon: url("@style_dir/img/app_play.svg");
+ qproperty-iconSize: 12px 12px;
+}
+
+PackagesTable IconButton#app_run[_enabled = "false"] {
+ qproperty-icon: url("@style_dir/img/app_play_disabled.svg");
+}
+
+PackagesTable IconButton#app_actions {
+ qproperty-icon: url("@style_dir/img/menu.svg");
+ qproperty-iconSize: 14px 14px;
+}
+
+PackagesTable IconButton#app_screenshots {
+ qproperty-icon: url("@style_dir/img/camera.svg");
+}
+
+PackagesTable IconButton#app_screenshots[_enabled = "false"] {
+ qproperty-icon: url("@style_dir/img/camera_disabled.svg");
+}
+
+PackagesTable IconButton#app_info {
+ qproperty-icon: url("@style_dir/img/about.svg");
+ qproperty-iconSize: 12px 12px;
+}
+
+PackagesTable QToolButton#bt_install {
+ color: @table.bt_install.font.color;
+}
+
+PackagesTable QToolButton#bt_install:disabled {
+ color: @disabled.color;
+}
+
+PackagesTable QToolButton#bt_uninstall {
+ color: @table.bt_uninstall.background.color;
+}
+
+PackagesTable QToolButton#bt_uninstall:disabled {
+ color: @disabled.color;
+}
+
+PackagesTable QLabel#app_version[update = "true"] {
+ color: @table.app_version.update.color;
+}
+
+PackagesTable QLabel#app_version:disabled[update = "true"] {
+ color: @disabled.color;
+}
+
+PackagesTable QLabel#app_version[ignored = "true"] {
+ color: @table.app_version.update_ignored.color;
+}
+
+HistoryDialog QTableWidget QLabel[even = "false"] {
+ background-color: @outer_widget.background.color;
+}
+
+HistoryDialog QTableWidget QLabel[outdated = "true"] {
+ color: @history.version.focus.color;
+ background: @history.version.outdated;
+}
+
+HistoryDialog QTableWidget QLabel[outdated = "false"] {
+ color: @history.version.focus.color;
+ background: @history.version.updated;
+}
diff --git a/bauh/view/resources/style/sublime/sublime.vars b/bauh/view/resources/style/sublime/sublime.vars
new file mode 100644
index 00000000..56f09d56
--- /dev/null
+++ b/bauh/view/resources/style/sublime/sublime.vars
@@ -0,0 +1,36 @@
+border.color=#EBEDEF
+button_ok.font.color=@color.yellow
+button_upgrade.disabled.background.color=@color.yellow
+color.blue=#67D8EF
+color.green=#A6E22C
+color.orange=#FD9621
+color.pink=#F92472
+color.yellow=#E7DB74
+disabled.color=#ABABAB
+error.color=@color.pink
+focus.border.color=@color.yellow
+focus.font.color=@color.yellow
+font.color=#F8F8F2
+history.version.focus.color=@font.color
+history.version.outdated=@color.orange
+history.version.updated=@color.green
+inner_widget.background.color=#282925
+menu.item.selected.font.color=@color.yellow
+menu.item.selected.background.color=#48473D
+outer_widget.background.color=#2E2F2B
+progressbar.fill.color=@color.blue
+scrollbar.handle.background.color=#555652
+table.app_publisher.unknown.font.color=@color.pink
+table.app_version.update.color=@color.green
+table.app_version.update_ignored.color=@color.yellow
+table.bt_install.font.color=@color.green
+table.bt_uninstall.background.color=@color.orange
+task.background.color=transparent
+task.done.color=#74705D
+task.running.color=@color.yellow
+texteditor.background.color=@menu.item.selected.background.color
+texteditor.font.color=white
+texteditor.selection.background.color=#9EBCCC
+texteditor.selection.font.color=black
+theme.selected.color=@color.green
+upgrade.color=@color.green
\ No newline at end of file
diff --git a/setup.py b/setup.py
index 9d50b1cc..38e03cb6 100644
--- a/setup.py
+++ b/setup.py
@@ -32,7 +32,7 @@ setup(
python_requires=">=3.5",
url=URL,
packages=find_packages(exclude=["tests.*", "tests"]),
- package_data={NAME: ["view/resources/locale/*", "view/resources/img/*", "gems/*/resources/img/*", "gems/*/resources/locale/*", "desktop/*"]},
+ package_data={NAME: ["view/resources/locale/*", "view/resources/img/*", "view/styles/*", 'view/styles/*/img/*', "gems/*/resources/img/*", "gems/*/resources/locale/*", "desktop/*"]},
install_requires=requirements,
test_suite="tests",
entry_points={
diff --git a/tests/view/__init__.py b/tests/view/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/view/core/__init__.py b/tests/view/core/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/view/core/test_stylesheet.py b/tests/view/core/test_stylesheet.py
new file mode 100644
index 00000000..0935c939
--- /dev/null
+++ b/tests/view/core/test_stylesheet.py
@@ -0,0 +1,116 @@
+from unittest import TestCase
+
+from bauh import stylesheet
+
+
+class StylesheetTest(TestCase):
+
+ def test__process_var_of_vars__it_should_remove_vars_pointing_to_themselves(self):
+ var_map = {
+ 'abc': 'aaa',
+ 'xxx': '@xxx'
+ }
+
+ stylesheet.process_var_of_vars(var_map)
+
+ self.assertEqual(1, len(var_map))
+ self.assertIn('abc', var_map)
+ self.assertEqual('aaa', var_map['abc'])
+
+ def test__process_var_of_vars__it_should_remove_vars_pointing_to_unknown_vars(self):
+ var_map = {
+ 'abc': 'aaa',
+ 'xxx': '@def'
+ }
+
+ stylesheet.process_var_of_vars(var_map)
+
+ self.assertEqual(1, len(var_map))
+ self.assertIn('abc', var_map)
+ self.assertEqual('aaa', var_map['abc'])
+
+ def test__process_var_of_vars__it_should_not_replace_invalid_expressions(self):
+ var_map = {
+ 'abc': 'aaa',
+ 'bcd': '@ xpto' # has a space between @ and 'xpto'
+ }
+
+ self.assertEqual(2, len(var_map))
+ self.assertIn('abc', var_map)
+ self.assertEqual('aaa', var_map['abc'])
+ self.assertIn('bcd', var_map)
+ self.assertEqual('@ xpto', var_map['bcd'])
+
+ def test__process_var_of_vars__it_should_replace_value_at_first_iteration(self):
+ var_map = {
+ 'abc': 'aaa',
+ 'xxx': '@abc'
+ }
+
+ stylesheet.process_var_of_vars(var_map)
+
+ self.assertEqual(2, len(var_map))
+ self.assertIn('abc', var_map)
+ self.assertEqual('aaa', var_map['abc'])
+ self.assertIn('xxx', var_map)
+ self.assertEqual('aaa', var_map['xxx'])
+
+ def test__process_var_of_vars__it_should_replace_value_at_second_iteration(self):
+ var_map = {
+ 'abc': 'aaa',
+ 'def': '@abc',
+ 'xxx': '@def'
+ }
+
+ stylesheet.process_var_of_vars(var_map)
+
+ self.assertEqual(3, len(var_map))
+ self.assertIn('abc', var_map)
+ self.assertEqual('aaa', var_map['abc'])
+ self.assertIn('def', var_map)
+ self.assertEqual('aaa', var_map['def'])
+ self.assertIn('xxx', var_map)
+ self.assertEqual('aaa', var_map['xxx'])
+
+ def test__process_var_of_vars__it_should_replace_value_at_third_iteration(self):
+ var_map = {
+ 'abc': 'aaa',
+ 'def': '@abc',
+ 'fgh': '@def',
+ 'xxx': '@fgh'
+ }
+
+ stylesheet.process_var_of_vars(var_map)
+
+ self.assertEqual(4, len(var_map))
+ self.assertIn('abc', var_map)
+ self.assertEqual('aaa', var_map['abc'])
+ self.assertIn('def', var_map)
+ self.assertEqual('aaa', var_map['def'])
+ self.assertIn('fgh', var_map)
+ self.assertEqual('aaa', var_map['fgh'])
+ self.assertIn('xxx', var_map)
+ self.assertEqual('aaa', var_map['xxx'])
+
+ def test__process_var_of_vars__it_should_replace_multiple_vars(self):
+ var_map = {
+ 'abc': 'aaa',
+ 'def': '@abc',
+ 'fgh': 'bbb',
+ 'ijk': '@fgh',
+ 'lmn': '@ijk'
+ }
+
+ stylesheet.process_var_of_vars(var_map)
+
+ self.assertEqual(5, len(var_map))
+ self.assertIn('abc', var_map)
+ self.assertEqual('aaa', var_map['abc'])
+ self.assertIn('def', var_map)
+ self.assertEqual('aaa', var_map['def'])
+ self.assertIn('fgh', var_map)
+ self.assertEqual('bbb', var_map['fgh'])
+ self.assertIn('ijk', var_map)
+ self.assertEqual('bbb', var_map['ijk'])
+ self.assertIn('lmn', var_map)
+ self.assertEqual('bbb', var_map['lmn'])