commit 83fbee1e9b4a6326bfc141fb27af09dd1ff43513 Author: Dummerle Date: Wed Feb 10 11:48:25 2021 +0100 init diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 00000000..73f69e09 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/.idea/RareNew.iml b/.idea/RareNew.iml new file mode 100644 index 00000000..8388dbc8 --- /dev/null +++ b/.idea/RareNew.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 00000000..c926cefb --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,43 @@ + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 00000000..105ce2da --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 00000000..d56657ad --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 00000000..86f6b280 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/Rare/Components/MainWindow.py b/Rare/Components/MainWindow.py new file mode 100644 index 00000000..188856b0 --- /dev/null +++ b/Rare/Components/MainWindow.py @@ -0,0 +1,12 @@ +from PyQt5.QtWidgets import QMainWindow + +from Rare.Components.TabWidget import TabWidget + + +class MainWindow(QMainWindow): + def __init__(self, core): + super(MainWindow, self).__init__() + self.setGeometry(0, 0, 1000, 800) + self.setWindowTitle("Rare - GUI for legendary") + self.setCentralWidget(TabWidget(core)) + self.show() diff --git a/Rare/Components/TabWidget.py b/Rare/Components/TabWidget.py new file mode 100644 index 00000000..6145d6cf --- /dev/null +++ b/Rare/Components/TabWidget.py @@ -0,0 +1,67 @@ +from PyQt5.QtGui import QIcon +from PyQt5.QtWidgets import QTabWidget, QTabBar, QWidget, QToolButton, QWidgetAction, QMenu +from Rare import style_path +from Rare.Components.Tabs.Account.AccountWidget import MiniWidget +from Rare.Components.Tabs.Games.Games import Games + + +class TabWidget(QTabWidget): + def __init__(self, core): + super(TabWidget, self).__init__() + self.setTabBar(TabBar(2)) + + self.game_list = Games(core) + + self.addTab(self.game_list, self.tr("Games")) + + self.downloads = QWidget() + self.addTab(self.downloads, "Downloads") + + # Space Tab + self.addTab(QWidget(), "") + self.setTabEnabled(2, False) + + self.account = QWidget() + self.addTab(self.account, "") + self.setTabEnabled(3, False) + + # self.settings = SettingsTab(core) + self.settings = QWidget() + self.addTab(self.settings, QIcon(style_path + "/Icons/settings.png"), "") + + self.tabBar().setTabButton(3, self.tabBar().RightSide, TabButtonWidget(core)) + + def resizeEvent(self, event): + self.tabBar().setMinimumWidth(self.width()) + super(TabWidget, self).resizeEvent(event) + + def download(self): + self.downloads.download() + + +class TabBar(QTabBar): + def __init__(self, expanded): + super(TabBar, self).__init__() + self._expanded = expanded + + def tabSizeHint(self, index): + size = super(TabBar, self).tabSizeHint(index) + if index == self._expanded: + offset = self.width() + for index in range(self.count()): + offset -= super(TabBar, self).tabSizeHint(index).width() + size.setWidth(max(size.width(), size.width() + offset)) + return size + + +class TabButtonWidget(QToolButton): + def __init__(self, core): + super(TabButtonWidget, self).__init__() + self.setText("Icon") + self.setPopupMode(QToolButton.InstantPopup) + self.setIcon(QIcon(style_path+"/Icons/account.png")) + self.setToolTip("Account") + self.setMenu(QMenu()) + action = QWidgetAction(self) + action.setDefaultWidget(MiniWidget(core)) + self.menu().addAction(action) diff --git a/Rare/Components/Tabs/Account/AccountWidget.py b/Rare/Components/Tabs/Account/AccountWidget.py new file mode 100644 index 00000000..333a1aa8 --- /dev/null +++ b/Rare/Components/Tabs/Account/AccountWidget.py @@ -0,0 +1,37 @@ +import webbrowser + +from PyQt5.QtCore import QCoreApplication +from PyQt5.QtWidgets import QWidget, QVBoxLayout, QMessageBox, QLabel, QPushButton +from legendary.core import LegendaryCore + + +class MiniWidget(QWidget): + def __init__(self, core: LegendaryCore): + super(MiniWidget, self).__init__() + self.layout = QVBoxLayout() + self.core = core + self.layout.addWidget(QLabel("Account")) + self.layout.addWidget(QLabel("Logged in as "+ self.core.lgd.userdata["display_name"])) + + self.open_browser = QPushButton("Account settings") + self.open_browser.clicked.connect(self.open_account) + self.layout.addWidget(self.open_browser) + + self.logout_button = QPushButton("Logout") + self.logout_button.clicked.connect(self.logout) + self.layout.addWidget(self.logout_button) + self.setLayout(self.layout) + + def logout(self): + reply = QMessageBox.question(self.parent().parent(), 'Message', + "Do you really want to logout", QMessageBox.Yes | + QMessageBox.No, QMessageBox.No) + + if reply == QMessageBox.Yes: + self.core.lgd.invalidate_userdata() + QCoreApplication.exit(0) + # self.core.lgd.invalidate_userdata() + # exit() + + def open_account(self): + webbrowser.open("https://www.epicgames.com/account/personal?productName=epicgames") \ No newline at end of file diff --git a/Rare/Components/Tabs/Account/__pycache__/AccountWidget.cpython-38.pyc b/Rare/Components/Tabs/Account/__pycache__/AccountWidget.cpython-38.pyc new file mode 100644 index 00000000..524aba18 Binary files /dev/null and b/Rare/Components/Tabs/Account/__pycache__/AccountWidget.cpython-38.pyc differ diff --git a/Rare/Components/Tabs/Account/__pycache__/AccountWidget.cpython-39.pyc b/Rare/Components/Tabs/Account/__pycache__/AccountWidget.cpython-39.pyc new file mode 100644 index 00000000..8a1a5003 Binary files /dev/null and b/Rare/Components/Tabs/Account/__pycache__/AccountWidget.cpython-39.pyc differ diff --git a/Rare/Components/Tabs/Downloads/DownloadTab.py b/Rare/Components/Tabs/Downloads/DownloadTab.py new file mode 100644 index 00000000..d51c85c7 --- /dev/null +++ b/Rare/Components/Tabs/Downloads/DownloadTab.py @@ -0,0 +1,6 @@ +from PyQt5.QtWidgets import QWidget + + +class DownloadTab(QWidget): + def __init__(self): + super(DownloadTab, self).__init__() diff --git a/Rare/Components/Tabs/Downloads/__init__.py b/Rare/Components/Tabs/Downloads/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/Rare/Components/Tabs/Games/GameList.py b/Rare/Components/Tabs/Games/GameList.py new file mode 100644 index 00000000..864faaf3 --- /dev/null +++ b/Rare/Components/Tabs/Games/GameList.py @@ -0,0 +1,36 @@ +from PyQt5.QtCore import Qt +from PyQt5.QtWidgets import * +from legendary.core import LegendaryCore + +from Rare.Components.Tabs.Games.GameWidgetInstalled import GameWidgetInstalled +from Rare.Components.Tabs.Games.GameWidgetUninstalled import GameWidgetUninstalled +from Rare.utils.QtExtensions import FlowLayout + + +class GameList(QScrollArea): + def __init__(self, core: LegendaryCore): + super(GameList, self).__init__() + self.core = core + self.widget = QWidget() + self.setWidgetResizable(True) + self.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) + self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) + + self.layout = FlowLayout() + + for i in self.core.get_installed_list(): + # continue + self.layout.addWidget(GameWidgetInstalled(core, i)) + + uninstalled_games = [] + installed = [i.app_name for i in core.get_installed_list()] + for game in sorted(core.get_game_list(), key=lambda x: x.app_title): + if not game.app_name in installed: + uninstalled_games.append(game) + + for i in uninstalled_games: + self.layout.addWidget(GameWidgetUninstalled(core, i)) + + + self.widget.setLayout(self.layout) + self.setWidget(self.widget) \ No newline at end of file diff --git a/Rare/Components/Tabs/Games/GameWidgetInstalled.py b/Rare/Components/Tabs/Games/GameWidgetInstalled.py new file mode 100644 index 00000000..46aeae18 --- /dev/null +++ b/Rare/Components/Tabs/Games/GameWidgetInstalled.py @@ -0,0 +1,108 @@ +import os +from logging import getLogger + +from PyQt5.QtCore import Qt, QEvent +from PyQt5.QtGui import QPixmap, QIcon, QMouseEvent +from PyQt5.QtWidgets import * +from legendary.core import LegendaryCore +from legendary.models.game import InstalledGame + +from Rare import style_path +from Rare.utils import LegendaryApi +from Rare.utils.QtExtensions import ClickableLabel +from Rare.utils.RareConfig import IMAGE_DIR + +logger = getLogger("FlowWidget") + + +class GameWidgetInstalled(QWidget): + def __init__(self, core: LegendaryCore, game: InstalledGame): + super(GameWidgetInstalled, self).__init__() + self.setObjectName("game_widget_parent") + self.layout = QVBoxLayout() + self.core = core + self.game = game + self.running = False + + Qt.WA_MouseTracking = Qt.WA_Hover + + if os.path.exists(f"{IMAGE_DIR}/{game.app_name}/FinalArt.png"): + pixmap = QPixmap(f"{IMAGE_DIR}/{game.app_name}/FinalArt.png") + elif os.path.exists(f"{IMAGE_DIR}/{game.app_name}/DieselGameBoxTall.png"): + pixmap = QPixmap(f"{IMAGE_DIR}/{game.app_name}/DieselGameBoxTall.png") + elif os.path.exists(f"{IMAGE_DIR}/{game.app_name}/DieselGameBoxLogo.png"): + pixmap = QPixmap(f"{IMAGE_DIR}/{game.app_name}/DieselGameBoxLogo.png") + else: + logger.warning(f"No Image found: {self.game.title}") + pixmap = None + if pixmap: + w = 200 + pixmap = pixmap.scaled(w, int(w * 4 / 3)) + + self.image = ClickableLabel() + self.image.setObjectName("game_widget") + self.image.setPixmap(pixmap) + self.layout.addWidget(self.image) + + self.title_label = QLabel(f"

{game.title}

") + self.title_label.setWordWrap(True) + self.title_label.setFixedWidth(180) + minilayout = QHBoxLayout() + self.title_label.setObjectName("game_widget") + minilayout.addWidget(self.title_label) + # minilayout.addStretch(1) + self.menu = QPushButton(QIcon(style_path + "/Icons/menu.png"), "") + self.menu.setMenu(Menu()) + self.menu.setObjectName("menu") + self.menu.setFixedWidth(10) + minilayout.addWidget(self.menu) + minilayout.addStretch(1) + self.layout.addLayout(minilayout) + + self.info_label = QLabel("") + self.info_label.setObjectName("info_label") + self.layout.addWidget(self.info_label) + + self.setLayout(self.layout) + self.setFixedWidth(self.sizeHint().width()) + + def enterEvent(self, a0: QEvent) -> None: + if not self.running: + self.info_label.setText("Start Game") + + def leaveEvent(self, a0: QEvent) -> None: + self.info_label.setText("") + + + + #return QWidget.mouseMoveEvent(self, mouseEvent) + + def mousePressEvent(self, a0) -> None: + self.launch() + + def launch(self, offline=False): + if not self.running: + logger.info("Launching " + self.game.title) + self.proc = LegendaryApi.launch_game(self.core, self.game.app_name, offline) + if not self.proc: + logger.error("Could not start process") + return + self.proc.finished.connect(self.finished) + self.info_label.setText("Game running") + self.running = True + + def finished(self): + self.info_label.setText("") + self.running = False + +class Menu(QMenu): + def __init__(self): + super(Menu, self).__init__() + self.addAction("Game info", self.info) + self.addAction("Uninstall", self.uninstall) + + def info(self): + pass + + def uninstall(self): + pass \ No newline at end of file diff --git a/Rare/Components/Tabs/Games/GameWidgetUninstalled.py b/Rare/Components/Tabs/Games/GameWidgetUninstalled.py new file mode 100644 index 00000000..0f27df91 --- /dev/null +++ b/Rare/Components/Tabs/Games/GameWidgetUninstalled.py @@ -0,0 +1,57 @@ +import os +from logging import getLogger + +from PyQt5.QtGui import QPixmap +from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel, QInputDialog +from legendary.core import LegendaryCore +from legendary.models.game import Game + +from Rare.utils.Dialogs.InstallDialog import InstallDialog +from Rare.utils.QtExtensions import ClickableLabel +from Rare.utils.RareConfig import IMAGE_DIR + +logger = getLogger("Uninstalled") + + +class GameWidgetUninstalled(QWidget): + def __init__(self, core: LegendaryCore, game: Game): + super(GameWidgetUninstalled, self).__init__() + self.layout = QVBoxLayout() + self.core = core + self.game = game + + if os.path.exists(f"{IMAGE_DIR}/{game.app_name}/UninstalledArt.png"): + pixmap = QPixmap(f"{IMAGE_DIR}/{game.app_name}/UninstalledArt.png") + else: + logger.warning(f"No Image found: {self.game.app_title}") + pixmap = None + if pixmap: + w = 200 + pixmap = pixmap.scaled(w, int(w * 4 / 3)) + self.image = ClickableLabel() + self.image.setPixmap(pixmap) + self.layout.addWidget(self.image) + self.title_label = QLabel(f"

{game.app_title}

") + self.title_label.setStyleSheet(""" + QLabel{ + text-align: center; + } + """) + self.title_label.setWordWrap(True) + self.layout.addWidget(self.title_label) + + self.info_label = QLabel("") + self.layout.addWidget(self.info_label) + + self.setLayout(self.layout) + self.setFixedWidth(self.sizeHint().width()) + + def mousePressEvent(self, a0) -> None: + self.install() + + def install(self): + logger.info("Install " + self.game.app_title) + infos = InstallDialog().get_information() + if infos != 0: + path, max_workers = infos + print(path, max_workers) \ No newline at end of file diff --git a/Rare/Components/Tabs/Games/Games.py b/Rare/Components/Tabs/Games/Games.py new file mode 100644 index 00000000..460946c4 --- /dev/null +++ b/Rare/Components/Tabs/Games/Games.py @@ -0,0 +1,24 @@ +from PyQt5.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QCheckBox + +from Rare.Components.Tabs.Games.GameList import GameList + + +class Games(QWidget): + def __init__(self, core): + super(Games, self).__init__() + self.layout = QVBoxLayout() + + self.head_bar = GameListHeadBar() + self.game_list = GameList(core) + + self.layout.addLayout(self.head_bar) + self.layout.addWidget(self.game_list) + #self.layout.addStretch(1) + self.setLayout(self.layout) + + +class GameListHeadBar(QHBoxLayout): + def __init__(self): + super(GameListHeadBar, self).__init__() + self.installed_only = QCheckBox("Installed only") + self.addWidget(self.installed_only) diff --git a/Rare/Components/Tabs/Games/__init__.py b/Rare/Components/Tabs/Games/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/Rare/Components/Tabs/Games/__pycache__/GameList.cpython-38.pyc b/Rare/Components/Tabs/Games/__pycache__/GameList.cpython-38.pyc new file mode 100644 index 00000000..24165df8 Binary files /dev/null and b/Rare/Components/Tabs/Games/__pycache__/GameList.cpython-38.pyc differ diff --git a/Rare/Components/Tabs/Games/__pycache__/GameList.cpython-39.pyc b/Rare/Components/Tabs/Games/__pycache__/GameList.cpython-39.pyc new file mode 100644 index 00000000..0a020d58 Binary files /dev/null and b/Rare/Components/Tabs/Games/__pycache__/GameList.cpython-39.pyc differ diff --git a/Rare/Components/Tabs/Games/__pycache__/GameWidgetInstalled.cpython-39.pyc b/Rare/Components/Tabs/Games/__pycache__/GameWidgetInstalled.cpython-39.pyc new file mode 100644 index 00000000..a306ff1c Binary files /dev/null and b/Rare/Components/Tabs/Games/__pycache__/GameWidgetInstalled.cpython-39.pyc differ diff --git a/Rare/Components/Tabs/Games/__pycache__/GameWidgetUninstalled.cpython-38.pyc b/Rare/Components/Tabs/Games/__pycache__/GameWidgetUninstalled.cpython-38.pyc new file mode 100644 index 00000000..c338885d Binary files /dev/null and b/Rare/Components/Tabs/Games/__pycache__/GameWidgetUninstalled.cpython-38.pyc differ diff --git a/Rare/Components/Tabs/Games/__pycache__/GameWidgetUninstalled.cpython-39.pyc b/Rare/Components/Tabs/Games/__pycache__/GameWidgetUninstalled.cpython-39.pyc new file mode 100644 index 00000000..723e5f14 Binary files /dev/null and b/Rare/Components/Tabs/Games/__pycache__/GameWidgetUninstalled.cpython-39.pyc differ diff --git a/Rare/Components/Tabs/Games/__pycache__/Games.cpython-38.pyc b/Rare/Components/Tabs/Games/__pycache__/Games.cpython-38.pyc new file mode 100644 index 00000000..ae80f252 Binary files /dev/null and b/Rare/Components/Tabs/Games/__pycache__/Games.cpython-38.pyc differ diff --git a/Rare/Components/Tabs/Games/__pycache__/Games.cpython-39.pyc b/Rare/Components/Tabs/Games/__pycache__/Games.cpython-39.pyc new file mode 100644 index 00000000..2062f1a7 Binary files /dev/null and b/Rare/Components/Tabs/Games/__pycache__/Games.cpython-39.pyc differ diff --git a/Rare/Components/Tabs/Games/__pycache__/__init__.cpython-38.pyc b/Rare/Components/Tabs/Games/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 00000000..3c0b23f5 Binary files /dev/null and b/Rare/Components/Tabs/Games/__pycache__/__init__.cpython-38.pyc differ diff --git a/Rare/Components/Tabs/Games/__pycache__/__init__.cpython-39.pyc b/Rare/Components/Tabs/Games/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 00000000..433835e2 Binary files /dev/null and b/Rare/Components/Tabs/Games/__pycache__/__init__.cpython-39.pyc differ diff --git a/Rare/Components/Tabs/__init__.py b/Rare/Components/Tabs/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/Rare/Components/Tabs/__pycache__/__init__.cpython-38.pyc b/Rare/Components/Tabs/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 00000000..567bfa6d Binary files /dev/null and b/Rare/Components/Tabs/__pycache__/__init__.cpython-38.pyc differ diff --git a/Rare/Components/Tabs/__pycache__/__init__.cpython-39.pyc b/Rare/Components/Tabs/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 00000000..18a578ef Binary files /dev/null and b/Rare/Components/Tabs/__pycache__/__init__.cpython-39.pyc differ diff --git a/Rare/Components/__init__.py b/Rare/Components/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/Rare/Components/__pycache__/MainWindow.cpython-38.pyc b/Rare/Components/__pycache__/MainWindow.cpython-38.pyc new file mode 100644 index 00000000..32ac6a89 Binary files /dev/null and b/Rare/Components/__pycache__/MainWindow.cpython-38.pyc differ diff --git a/Rare/Components/__pycache__/MainWindow.cpython-39.pyc b/Rare/Components/__pycache__/MainWindow.cpython-39.pyc new file mode 100644 index 00000000..0fb0b5f8 Binary files /dev/null and b/Rare/Components/__pycache__/MainWindow.cpython-39.pyc differ diff --git a/Rare/Components/__pycache__/TabWidget.cpython-38.pyc b/Rare/Components/__pycache__/TabWidget.cpython-38.pyc new file mode 100644 index 00000000..29c02f81 Binary files /dev/null and b/Rare/Components/__pycache__/TabWidget.cpython-38.pyc differ diff --git a/Rare/Components/__pycache__/TabWidget.cpython-39.pyc b/Rare/Components/__pycache__/TabWidget.cpython-39.pyc new file mode 100644 index 00000000..fa3244eb Binary files /dev/null and b/Rare/Components/__pycache__/TabWidget.cpython-39.pyc differ diff --git a/Rare/Components/__pycache__/__init__.cpython-38.pyc b/Rare/Components/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 00000000..c7c64683 Binary files /dev/null and b/Rare/Components/__pycache__/__init__.cpython-38.pyc differ diff --git a/Rare/Components/__pycache__/__init__.cpython-39.pyc b/Rare/Components/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 00000000..629a852f Binary files /dev/null and b/Rare/Components/__pycache__/__init__.cpython-39.pyc differ diff --git a/Rare/Main.py b/Rare/Main.py new file mode 100644 index 00000000..8790cdaa --- /dev/null +++ b/Rare/Main.py @@ -0,0 +1,71 @@ +import logging +import os +import sys + +import requests +from PyQt5.QtCore import QTranslator +from PyQt5.QtWidgets import QApplication, QMessageBox +from legendary.core import LegendaryCore + +from Rare import style_path, lang_path +from Rare.Components.MainWindow import MainWindow + +# from Rare.Start.Launch import LaunchDialog +# from Rare.Start.Login import LoginWindow +# from Rare.utils.RareUtils import get_lang + +logging.basicConfig( + format='[%(name)s] %(levelname)s: %(message)s', + level=logging.INFO +) +logger = logging.getLogger("Rare") +core = LegendaryCore() + + +def main(): + app = QApplication(sys.argv) + translator = QTranslator() + # lang = get_lang() + lang = "de" + if os.path.exists(lang_path + lang + ".qm"): + translator.load(lang_path + lang + ".qm") + else: + logger.info("Your language is not supported") + app.installTranslator(translator) + + app.setStyleSheet(open(style_path + "RareStyle.qss").read()) + + offline = True + + logger.info("Try if you are logged in") + try: + if core.login(): + logger.info("You are logged in") + offline = False + else: + logger.error("Login Failed") + main() + + except ValueError: + logger.info("You are not logged in. Open Login Window") + login_window = LoginWindow(core) + if not login_window.login(): + return + + # Start Offline mode + except requests.exceptions.ConnectionError: + offline = True + QMessageBox.information(None, "Offline", "You are offline. Launching Rare in offline mode") + # Launch Offlienmode + if not offline: + # launch_dialog = LaunchDialog(core) + # launch_dialog.exec_() + pass + # mainwindow = MainWindow(core) + mainwindow = MainWindow(core) + + app.exec_() + + +if __name__ == '__main__': + main() diff --git a/Rare/Styles/Icons/account.png b/Rare/Styles/Icons/account.png new file mode 100644 index 00000000..b718b8f7 Binary files /dev/null and b/Rare/Styles/Icons/account.png differ diff --git a/Rare/Styles/Icons/loader.gif b/Rare/Styles/Icons/loader.gif new file mode 100644 index 00000000..28929508 Binary files /dev/null and b/Rare/Styles/Icons/loader.gif differ diff --git a/Rare/Styles/Icons/menu.png b/Rare/Styles/Icons/menu.png new file mode 100644 index 00000000..902bd2e4 Binary files /dev/null and b/Rare/Styles/Icons/menu.png differ diff --git a/Rare/Styles/Icons/settings.png b/Rare/Styles/Icons/settings.png new file mode 100644 index 00000000..0fb26453 Binary files /dev/null and b/Rare/Styles/Icons/settings.png differ diff --git a/Rare/Styles/RareStyle.qss b/Rare/Styles/RareStyle.qss new file mode 100644 index 00000000..0a907c1a --- /dev/null +++ b/Rare/Styles/RareStyle.qss @@ -0,0 +1,44 @@ +QWidget { + background-color: #202225; + color: white; +} + + +QTabBar::tab { + + padding: 5px; +} + +QTabBar::tab:hover { + background-color: black; +} + +QTabBar::tab:disabled { + color: transparent; + background-color: transparent; +} + + + +QToolButton { + padding: 5px; +} + + +QPushButton { + border: 2px solid white; + border-radius: 2px; +} + +QPushButton::menu-indicator { + subcontrol-position: right center; + subcontrol-origin: padding; + left: -2px; + border-style: none; +} + +QPushButton#menu { + padding: 0px; + margin: 0px; + border-style: none; +} diff --git a/Rare/Styles/__init__.py b/Rare/Styles/__init__.py new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/Rare/Styles/__init__.py @@ -0,0 +1 @@ + diff --git a/Rare/Styles/dark.qss b/Rare/Styles/dark.qss new file mode 100644 index 00000000..72295ee5 --- /dev/null +++ b/Rare/Styles/dark.qss @@ -0,0 +1,2174 @@ +/* --------------------------------------------------------------------------- + + Created by the qtsass compiler v0.1.1 + + The definitions are in the "qdarkstyle.qss._styles.scss" module + + WARNING! All changes made in this file will be lost! + +--------------------------------------------------------------------------- */ +/* QDarkStyleSheet ----------------------------------------------------------- + +This is the main style sheet, the palette has nine colors. + +It is based on three selecting colors, three greyish (background) colors +plus three whitish (foreground) colors. Each set of widgets of the same +type have a header like this: + + ------------------ + GroupName -------- + ------------------ + +And each widget is separated with a header like this: + + QWidgetName ------ + +This makes more easy to find and change some css field. The basic +configuration is described bellow. + + BACKGROUND ----------- + + Light (unpressed) + Normal (border, disabled, pressed, checked, toolbars, menus) + Dark (background) + + FOREGROUND ----------- + + Light (texts/labels) + Normal (not used yet) + Dark (disabled texts) + + SELECTION ------------ + + Light (selection/hover/active) + Normal (selected) + Dark (selected disabled) + +If a stranger configuration is required because of a bugfix or anything +else, keep the comment on the line above so nobody changes it, including the +issue number. + +*/ +/* + +See Qt documentation: + + - https://doc.qt.io/qt-5/stylesheet.html + - https://doc.qt.io/qt-5/stylesheet-reference.html + - https://doc.qt.io/qt-5/stylesheet-examples.html + +--------------------------------------------------------------------------- */ +/* QWidget ---------------------------------------------------------------- + +--------------------------------------------------------------------------- */ +QWidget { + background-color: #19232D; + border: 0px solid #32414B; + padding: 0px; + color: #F0F0F0; + selection-background-color: #1464A0; + selection-color: #F0F0F0; +} + +QWidget:disabled { + background-color: #19232D; + color: #787878; + selection-background-color: #14506E; + selection-color: #787878; +} + +QWidget::item:selected { + background-color: #1464A0; +} + +QWidget::item:hover { + background-color: #148CD2; + color: #32414B; +} + +/* QMainWindow ------------------------------------------------------------ + +This adjusts the splitter in the dock widget, not qsplitter +https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qmainwindow + +--------------------------------------------------------------------------- */ +QMainWindow::separator { + background-color: #32414B; + border: 0px solid #19232D; + spacing: 0px; + padding: 2px; +} + +QMainWindow::separator:hover { + background-color: #505F69; + border: 0px solid #148CD2; +} + +QMainWindow::separator:horizontal { + width: 5px; + margin-top: 2px; + margin-bottom: 2px; + image: url(":/qss_icons/rc/toolbar_separator_vertical.png"); +} + +QMainWindow::separator:vertical { + height: 5px; + margin-left: 2px; + margin-right: 2px; + image: url(":/qss_icons/rc/toolbar_separator_horizontal.png"); +} + +/* QToolTip --------------------------------------------------------------- + +https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qtooltip + +--------------------------------------------------------------------------- */ +QToolTip { + background-color: #148CD2; + border: 1px solid #19232D; + color: #19232D; + /* Remove padding, for fix combo box tooltip */ + padding: 0px; + /* Remove opacity, fix #174 - may need to use RGBA */ +} + +/* QStatusBar ------------------------------------------------------------- + +https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qstatusbar + +--------------------------------------------------------------------------- */ +QStatusBar { + border: 1px solid #32414B; + /* Fixes Spyder #9120, #9121 */ + background: #32414B; + /* Fixes #205, white vertical borders separating items */ +} + +QStatusBar::item { + border: none; +} + +QStatusBar QToolTip { + background-color: #148CD2; + border: 1px solid #19232D; + color: #19232D; + /* Remove padding, for fix combo box tooltip */ + padding: 0px; + /* Reducing transparency to read better */ + opacity: 230; +} + +QStatusBar QLabel { + /* Fixes Spyder #9120, #9121 */ + background: transparent; +} + +/* QCheckBox -------------------------------------------------------------- + +https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qcheckbox + +--------------------------------------------------------------------------- */ +QCheckBox { + background-color: #19232D; + color: #F0F0F0; + spacing: 4px; + outline: none; + padding-top: 4px; + padding-bottom: 4px; +} + +QCheckBox:focus { + border: none; +} + +QCheckBox QWidget:disabled { + background-color: #19232D; + color: #787878; +} + +QCheckBox::indicator { + margin-left: 4px; + height: 16px; + width: 16px; +} + +QCheckBox::indicator:unchecked { + image: url(":/qss_icons/rc/checkbox_unchecked.png"); +} + +QCheckBox::indicator:unchecked:hover, QCheckBox::indicator:unchecked:focus, QCheckBox::indicator:unchecked:pressed { + border: none; + image: url(":/qss_icons/rc/checkbox_unchecked_focus.png"); +} + +QCheckBox::indicator:unchecked:disabled { + image: url(":/qss_icons/rc/checkbox_unchecked_disabled.png"); +} + +QCheckBox::indicator:checked { + image: url(":/qss_icons/rc/checkbox_checked.png"); +} + +QCheckBox::indicator:checked:hover, QCheckBox::indicator:checked:focus, QCheckBox::indicator:checked:pressed { + border: none; + image: url(":/qss_icons/rc/checkbox_checked_focus.png"); +} + +QCheckBox::indicator:checked:disabled { + image: url(":/qss_icons/rc/checkbox_checked_disabled.png"); +} + +QCheckBox::indicator:indeterminate { + image: url(":/qss_icons/rc/checkbox_indeterminate.png"); +} + +QCheckBox::indicator:indeterminate:disabled { + image: url(":/qss_icons/rc/checkbox_indeterminate_disabled.png"); +} + +QCheckBox::indicator:indeterminate:focus, QCheckBox::indicator:indeterminate:hover, QCheckBox::indicator:indeterminate:pressed { + image: url(":/qss_icons/rc/checkbox_indeterminate_focus.png"); +} + +/* QGroupBox -------------------------------------------------------------- + +https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qgroupbox + +--------------------------------------------------------------------------- */ +QGroupBox { + font-weight: bold; + border: 1px solid #32414B; + border-radius: 4px; + padding: 4px; + margin-top: 16px; +} + +QGroupBox::title { + subcontrol-origin: margin; + subcontrol-position: top left; + left: 3px; + padding-left: 3px; + padding-right: 5px; + padding-top: 8px; + padding-bottom: 16px; +} + +QGroupBox::indicator { + margin-left: 2px; + height: 16px; + width: 16px; +} + +QGroupBox::indicator:unchecked { + border: none; + image: url(":/qss_icons/rc/checkbox_unchecked.png"); +} + +QGroupBox::indicator:unchecked:hover, QGroupBox::indicator:unchecked:focus, QGroupBox::indicator:unchecked:pressed { + border: none; + image: url(":/qss_icons/rc/checkbox_unchecked_focus.png"); +} + +QGroupBox::indicator:unchecked:disabled { + image: url(":/qss_icons/rc/checkbox_unchecked_disabled.png"); +} + +QGroupBox::indicator:checked { + border: none; + image: url(":/qss_icons/rc/checkbox_checked.png"); +} + +QGroupBox::indicator:checked:hover, QGroupBox::indicator:checked:focus, QGroupBox::indicator:checked:pressed { + border: none; + image: url(":/qss_icons/rc/checkbox_checked_focus.png"); +} + +QGroupBox::indicator:checked:disabled { + image: url(":/qss_icons/rc/checkbox_checked_disabled.png"); +} + +/* QRadioButton ----------------------------------------------------------- + +https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qradiobutton + +--------------------------------------------------------------------------- */ +QRadioButton { + background-color: #19232D; + color: #F0F0F0; + spacing: 4px; + padding: 0px; + border: none; + outline: none; +} + +QRadioButton:focus { + border: none; +} + +QRadioButton:disabled { + background-color: #19232D; + color: #787878; + border: none; + outline: none; +} + +QRadioButton QWidget { + background-color: #19232D; + color: #F0F0F0; + spacing: 0px; + padding: 0px; + outline: none; + border: none; +} + +QRadioButton::indicator { + border: none; + outline: none; + margin-left: 4px; + height: 16px; + width: 16px; +} + +QRadioButton::indicator:unchecked { + image: url(":/qss_icons/rc/radio_unchecked.png"); +} + +QRadioButton::indicator:unchecked:hover, QRadioButton::indicator:unchecked:focus, QRadioButton::indicator:unchecked:pressed { + border: none; + outline: none; + image: url(":/qss_icons/rc/radio_unchecked_focus.png"); +} + +QRadioButton::indicator:unchecked:disabled { + image: url(":/qss_icons/rc/radio_unchecked_disabled.png"); +} + +QRadioButton::indicator:checked { + border: none; + outline: none; + image: url(":/qss_icons/rc/radio_checked.png"); +} + +QRadioButton::indicator:checked:hover, QRadioButton::indicator:checked:focus, QRadioButton::indicator:checked:pressed { + border: none; + outline: none; + image: url(":/qss_icons/rc/radio_checked_focus.png"); +} + +QRadioButton::indicator:checked:disabled { + outline: none; + image: url(":/qss_icons/rc/radio_checked_disabled.png"); +} + +/* QMenuBar --------------------------------------------------------------- + +https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qmenubar + +--------------------------------------------------------------------------- */ +QMenuBar { + background-color: #32414B; + padding: 2px; + border: 1px solid #19232D; + color: #F0F0F0; +} + +QMenuBar:focus { + border: 1px solid #148CD2; +} + +QMenuBar::item { + background: transparent; + padding: 4px; +} + +QMenuBar::item:selected { + padding: 4px; + background: transparent; + border: 0px solid #32414B; +} + +QMenuBar::item:pressed { + padding: 4px; + border: 0px solid #32414B; + background-color: #148CD2; + color: #F0F0F0; + margin-bottom: 0px; + padding-bottom: 0px; +} + +/* QMenu ------------------------------------------------------------------ + +https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qmenu + +--------------------------------------------------------------------------- */ +QMenu { + border: 0px solid #32414B; + color: #F0F0F0; + margin: 0px; +} + +QMenu::separator { + height: 1px; + background-color: #505F69; + color: #F0F0F0; +} + +QMenu::icon { + margin: 0px; + padding-left: 8px; +} + +QMenu::item { + background-color: #32414B; + padding: 4px 24px 4px 24px; + /* Reserve space for selection border */ + border: 1px transparent #32414B; +} + +QMenu::item:selected { + color: #F0F0F0; +} + +QMenu::indicator { + width: 12px; + height: 12px; + padding-left: 6px; + /* non-exclusive indicator = check box style indicator (see QActionGroup::setExclusive) */ + /* exclusive indicator = radio button style indicator (see QActionGroup::setExclusive) */ +} + +QMenu::indicator:non-exclusive:unchecked { + image: url(":/qss_icons/rc/checkbox_unchecked.png"); +} + +QMenu::indicator:non-exclusive:unchecked:selected { + image: url(":/qss_icons/rc/checkbox_unchecked_disabled.png"); +} + +QMenu::indicator:non-exclusive:checked { + image: url(":/qss_icons/rc/checkbox_checked.png"); +} + +QMenu::indicator:non-exclusive:checked:selected { + image: url(":/qss_icons/rc/checkbox_checked_disabled.png"); +} + +QMenu::indicator:exclusive:unchecked { + image: url(":/qss_icons/rc/radio_unchecked.png"); +} + +QMenu::indicator:exclusive:unchecked:selected { + image: url(":/qss_icons/rc/radio_unchecked_disabled.png"); +} + +QMenu::indicator:exclusive:checked { + image: url(":/qss_icons/rc/radio_checked.png"); +} + +QMenu::indicator:exclusive:checked:selected { + image: url(":/qss_icons/rc/radio_checked_disabled.png"); +} + +QMenu::right-arrow { + margin: 5px; + image: url(":/qss_icons/rc/arrow_right.png"); + height: 12px; + width: 12px; +} + +/* QAbstractItemView ------------------------------------------------------ + +https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qcombobox + +--------------------------------------------------------------------------- */ +QAbstractItemView { + alternate-background-color: #19232D; + color: #F0F0F0; + border: 1px solid #32414B; + border-radius: 4px; +} + +QAbstractItemView QLineEdit { + padding: 2px; +} + +/* QAbstractScrollArea ---------------------------------------------------- + +https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qabstractscrollarea + +--------------------------------------------------------------------------- */ +QAbstractScrollArea { + background-color: #19232D; + border: 1px solid #32414B; + border-radius: 4px; + padding: 2px; + /* fix #159 */ + min-height: 1.25em; + /* fix #159 */ + color: #F0F0F0; +} + +QAbstractScrollArea:disabled { + color: #787878; +} + +/* QScrollArea ------------------------------------------------------------ + +--------------------------------------------------------------------------- */ +QScrollArea QWidget QWidget:disabled { + background-color: #19232D; +} + +/* QScrollBar ------------------------------------------------------------- + +https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qscrollbar + +--------------------------------------------------------------------------- */ +QScrollBar:horizontal { + height: 16px; + margin: 2px 16px 2px 16px; + border: 1px solid #32414B; + border-radius: 4px; + background-color: #19232D; +} + +QScrollBar:vertical { + background-color: #19232D; + width: 16px; + margin: 16px 2px 16px 2px; + border: 1px solid #32414B; + border-radius: 4px; +} + +QScrollBar::handle:horizontal { + background-color: #787878; + border: 1px solid #32414B; + border-radius: 4px; + min-width: 8px; +} + +QScrollBar::handle:horizontal:hover { + background-color: #148CD2; + border: 1px solid #148CD2; + border-radius: 4px; + min-width: 8px; +} + +QScrollBar::handle:horizontal:focus { + border: 1px solid #1464A0; +} + +QScrollBar::handle:vertical { + background-color: #787878; + border: 1px solid #32414B; + min-height: 8px; + border-radius: 4px; +} + +QScrollBar::handle:vertical:hover { + background-color: #148CD2; + border: 1px solid #148CD2; + border-radius: 4px; + min-height: 8px; +} + +QScrollBar::handle:vertical:focus { + border: 1px solid #1464A0; +} + +QScrollBar::add-line:horizontal { + margin: 0px 0px 0px 0px; + border-image: url(":/qss_icons/rc/arrow_right_disabled.png"); + height: 12px; + width: 12px; + subcontrol-position: right; + subcontrol-origin: margin; +} + +QScrollBar::add-line:horizontal:hover, QScrollBar::add-line:horizontal:on { + border-image: url(":/qss_icons/rc/arrow_right.png"); + height: 12px; + width: 12px; + subcontrol-position: right; + subcontrol-origin: margin; +} + +QScrollBar::add-line:vertical { + margin: 3px 0px 3px 0px; + border-image: url(":/qss_icons/rc/arrow_down_disabled.png"); + height: 12px; + width: 12px; + subcontrol-position: bottom; + subcontrol-origin: margin; +} + +QScrollBar::add-line:vertical:hover, QScrollBar::add-line:vertical:on { + border-image: url(":/qss_icons/rc/arrow_down.png"); + height: 12px; + width: 12px; + subcontrol-position: bottom; + subcontrol-origin: margin; +} + +QScrollBar::sub-line:horizontal { + margin: 0px 3px 0px 3px; + border-image: url(":/qss_icons/rc/arrow_left_disabled.png"); + height: 12px; + width: 12px; + subcontrol-position: left; + subcontrol-origin: margin; +} + +QScrollBar::sub-line:horizontal:hover, QScrollBar::sub-line:horizontal:on { + border-image: url(":/qss_icons/rc/arrow_left.png"); + height: 12px; + width: 12px; + subcontrol-position: left; + subcontrol-origin: margin; +} + +QScrollBar::sub-line:vertical { + margin: 3px 0px 3px 0px; + border-image: url(":/qss_icons/rc/arrow_up_disabled.png"); + height: 12px; + width: 12px; + subcontrol-position: top; + subcontrol-origin: margin; +} + +QScrollBar::sub-line:vertical:hover, QScrollBar::sub-line:vertical:on { + border-image: url(":/qss_icons/rc/arrow_up.png"); + height: 12px; + width: 12px; + subcontrol-position: top; + subcontrol-origin: margin; +} + +QScrollBar::up-arrow:horizontal, QScrollBar::down-arrow:horizontal { + background: none; +} + +QScrollBar::up-arrow:vertical, QScrollBar::down-arrow:vertical { + background: none; +} + +QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal { + background: none; +} + +QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { + background: none; +} + +/* QTextEdit -------------------------------------------------------------- + +https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-specific-widgets + +--------------------------------------------------------------------------- */ +QTextEdit { + background-color: #19232D; + color: #F0F0F0; + border-radius: 4px; + border: 1px solid #32414B; +} + +QTextEdit:hover { + border: 1px solid #148CD2; + color: #F0F0F0; +} + +QTextEdit:focus { + border: 1px solid #1464A0; +} + +QTextEdit:selected { + background: #1464A0; + color: #32414B; +} + +/* QPlainTextEdit --------------------------------------------------------- + +--------------------------------------------------------------------------- */ +QPlainTextEdit { + background-color: #19232D; + color: #F0F0F0; + border-radius: 4px; + border: 1px solid #32414B; +} + +QPlainTextEdit:hover { + border: 1px solid #148CD2; + color: #F0F0F0; +} + +QPlainTextEdit:focus { + border: 1px solid #1464A0; +} + +QPlainTextEdit:selected { + background: #1464A0; + color: #32414B; +} + +/* QSizeGrip -------------------------------------------------------------- + +https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qsizegrip + +--------------------------------------------------------------------------- */ +QSizeGrip { + background: transparent; + width: 12px; + height: 12px; + image: url(":/qss_icons/rc/window_grip.png"); +} + +/* QStackedWidget --------------------------------------------------------- + +--------------------------------------------------------------------------- */ +QStackedWidget { + padding: 2px; + border: 1px solid #32414B; + border: 1px solid #19232D; +} + +/* QToolBar --------------------------------------------------------------- + +https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qtoolbar + +--------------------------------------------------------------------------- */ +QToolBar { + background-color: #32414B; + border-bottom: 1px solid #19232D; + padding: 2px; + font-weight: bold; + spacing: 2px; +} + +QToolBar QToolButton { + background-color: #32414B; + border: 1px solid #32414B; +} + +QToolBar QToolButton:hover { + border: 1px solid #148CD2; +} + +QToolBar QToolButton:checked { + border: 1px solid #19232D; + background-color: #19232D; +} + +QToolBar QToolButton:checked:hover { + border: 1px solid #148CD2; +} + +QToolBar::handle:horizontal { + width: 16px; + image: url(":/qss_icons/rc/toolbar_move_horizontal.png"); +} + +QToolBar::handle:vertical { + height: 16px; + image: url(":/qss_icons/rc/toolbar_move_vertical.png"); +} + +QToolBar::separator:horizontal { + width: 16px; + image: url(":/qss_icons/rc/toolbar_separator_horizontal.png"); +} + +QToolBar::separator:vertical { + height: 16px; + image: url(":/qss_icons/rc/toolbar_separator_vertical.png"); +} + +QToolButton#qt_toolbar_ext_button { + background: #32414B; + border: 0px; + color: #F0F0F0; + image: url(":/qss_icons/rc/arrow_right.png"); +} + +/* QAbstractSpinBox ------------------------------------------------------- + +--------------------------------------------------------------------------- */ +QAbstractSpinBox { + background-color: #19232D; + border: 1px solid #32414B; + color: #F0F0F0; + /* This fixes 103, 111 */ + padding-top: 2px; + /* This fixes 103, 111 */ + padding-bottom: 2px; + padding-left: 4px; + padding-right: 4px; + border-radius: 4px; + /* min-width: 5px; removed to fix 109 */ +} + +QAbstractSpinBox:up-button { + background-color: transparent #19232D; + subcontrol-origin: border; + subcontrol-position: top right; + border-left: 1px solid #32414B; + border-bottom: 1px solid #32414B; + border-top-left-radius: 0; + border-bottom-left-radius: 0; + margin: 1px; + width: 12px; + margin-bottom: -1px; +} + +QAbstractSpinBox::up-arrow, QAbstractSpinBox::up-arrow:disabled, QAbstractSpinBox::up-arrow:off { + image: url(":/qss_icons/rc/arrow_up_disabled.png"); + height: 8px; + width: 8px; +} + +QAbstractSpinBox::up-arrow:hover { + image: url(":/qss_icons/rc/arrow_up.png"); +} + +QAbstractSpinBox:down-button { + background-color: transparent #19232D; + subcontrol-origin: border; + subcontrol-position: bottom right; + border-left: 1px solid #32414B; + border-top: 1px solid #32414B; + border-top-left-radius: 0; + border-bottom-left-radius: 0; + margin: 1px; + width: 12px; + margin-top: -1px; +} + +QAbstractSpinBox::down-arrow, QAbstractSpinBox::down-arrow:disabled, QAbstractSpinBox::down-arrow:off { + image: url(":/qss_icons/rc/arrow_down_disabled.png"); + height: 8px; + width: 8px; +} + +QAbstractSpinBox::down-arrow:hover { + image: url(":/qss_icons/rc/arrow_down.png"); +} + +QAbstractSpinBox:hover { + border: 1px solid #148CD2; + color: #F0F0F0; +} + +QAbstractSpinBox:focus { + border: 1px solid #1464A0; +} + +QAbstractSpinBox:selected { + background: #1464A0; + color: #32414B; +} + +/* ------------------------------------------------------------------------ */ +/* DISPLAYS --------------------------------------------------------------- */ +/* ------------------------------------------------------------------------ */ +/* QLabel ----------------------------------------------------------------- + +https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qframe + +--------------------------------------------------------------------------- */ +QLabel { + background-color: #19232D; + border: 0px solid #32414B; + padding: 2px; + margin: 0px; + color: #F0F0F0; +} + +QLabel:disabled { + background-color: #19232D; + border: 0px solid #32414B; + color: #787878; +} + +/* QTextBrowser ----------------------------------------------------------- + +https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qabstractscrollarea + +--------------------------------------------------------------------------- */ +QTextBrowser { + background-color: #19232D; + border: 1px solid #32414B; + color: #F0F0F0; + border-radius: 4px; +} + +QTextBrowser:disabled { + background-color: #19232D; + border: 1px solid #32414B; + color: #787878; + border-radius: 4px; +} + +QTextBrowser:hover, QTextBrowser:!hover, QTextBrowser:selected, QTextBrowser:pressed { + border: 1px solid #32414B; +} + +/* QGraphicsView ---------------------------------------------------------- + +--------------------------------------------------------------------------- */ +QGraphicsView { + background-color: #19232D; + border: 1px solid #32414B; + color: #F0F0F0; + border-radius: 4px; +} + +QGraphicsView:disabled { + background-color: #19232D; + border: 1px solid #32414B; + color: #787878; + border-radius: 4px; +} + +QGraphicsView:hover, QGraphicsView:!hover, QGraphicsView:selected, QGraphicsView:pressed { + border: 1px solid #32414B; +} + +/* QCalendarWidget -------------------------------------------------------- + +--------------------------------------------------------------------------- */ +QCalendarWidget { + border: 1px solid #32414B; + border-radius: 4px; +} + +QCalendarWidget:disabled { + background-color: #19232D; + color: #787878; +} + +/* QLCDNumber ------------------------------------------------------------- + +--------------------------------------------------------------------------- */ +QLCDNumber { + background-color: #19232D; + color: #F0F0F0; +} + +QLCDNumber:disabled { + background-color: #19232D; + color: #787878; +} + +/* QProgressBar ----------------------------------------------------------- + +https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qprogressbar + +--------------------------------------------------------------------------- */ +QProgressBar { + background-color: #19232D; + border: 1px solid #32414B; + color: #F0F0F0; + border-radius: 4px; + text-align: center; +} + +QProgressBar:disabled { + background-color: #19232D; + border: 1px solid #32414B; + color: #787878; + border-radius: 4px; + text-align: center; +} + +QProgressBar::chunk { + background-color: #1464A0; + color: #19232D; + border-radius: 4px; +} + +QProgressBar::chunk:disabled { + background-color: #14506E; + color: #787878; + border-radius: 4px; +} + +/* ------------------------------------------------------------------------ */ +/* BUTTONS ---------------------------------------------------------------- */ +/* ------------------------------------------------------------------------ */ +/* QPushButton ------------------------------------------------------------ + +https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qpushbutton + +--------------------------------------------------------------------------- */ +QPushButton { + background-color: #505F69; + border: 1px solid #32414B; + color: #F0F0F0; + border-radius: 4px; + padding: 3px; + outline: none; + /* Issue #194 - Special case of QPushButton inside dialogs, for better UI */ + min-width: 80px; +} + +QPushButton:disabled { + background-color: #32414B; + border: 1px solid #32414B; + color: #787878; + border-radius: 4px; + padding: 3px; +} + +QPushButton:checked { + background-color: #32414B; + border: 1px solid #32414B; + border-radius: 4px; + padding: 3px; + outline: none; +} + +QPushButton:checked:disabled { + background-color: #19232D; + border: 1px solid #32414B; + color: #787878; + border-radius: 4px; + padding: 3px; + outline: none; +} + +QPushButton:checked:selected { + background: #1464A0; + color: #32414B; +} + +QPushButton::menu-indicator { + subcontrol-origin: padding; + subcontrol-position: bottom right; + bottom: 4px; +} + +QPushButton:pressed { + background-color: #19232D; + border: 1px solid #19232D; +} + +QPushButton:pressed:hover { + border: 1px solid #148CD2; +} + +QPushButton:hover { + border: 1px solid #148CD2; + color: #F0F0F0; +} + +QPushButton:selected { + background: #1464A0; + color: #32414B; +} + +QPushButton:hover { + border: 1px solid #148CD2; + color: #F0F0F0; +} + +QPushButton:focus { + border: 1px solid #1464A0; +} + +/* QToolButton ------------------------------------------------------------ + +https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qtoolbutton + +--------------------------------------------------------------------------- */ +QToolButton { + background-color: transparent; + border: 1px solid transparent; + border-radius: 4px; + margin: 0px; + padding: 2px; + /* The subcontrols below are used only in the DelayedPopup mode */ + /* The subcontrols below are used only in the MenuButtonPopup mode */ + /* The subcontrol below is used only in the InstantPopup or DelayedPopup mode */ +} + +QToolButton:checked { + background-color: transparent; + border: 1px solid #1464A0; +} + +QToolButton:checked:disabled { + border: 1px solid #14506E; +} + +QToolButton:pressed { + margin: 1px; + background-color: transparent; + border: 1px solid #1464A0; +} + +QToolButton:disabled { + border: none; +} + +QToolButton:hover { + border: 1px solid #148CD2; +} + +QToolButton[popupMode="0"] { + /* Only for DelayedPopup */ + padding-right: 2px; +} + +QToolButton[popupMode="1"] { + /* Only for MenuButtonPopup */ + padding-right: 20px; +} + +QToolButton[popupMode="1"]::menu-button { + border: none; +} + +QToolButton[popupMode="1"]::menu-button:hover { + border: none; + border-left: 1px solid #148CD2; + border-radius: 0; +} + +QToolButton[popupMode="2"] { + /* Only for InstantPopup */ + padding-right: 2px; +} + +QToolButton::menu-button { + padding: 2px; + border-radius: 4px; + border: 1px solid #32414B; + width: 12px; + outline: none; +} + +QToolButton::menu-button:hover { + border: 1px solid #148CD2; +} + +QToolButton::menu-button:checked:hover { + border: 1px solid #148CD2; +} + +QToolButton::menu-indicator { + image: url(":/qss_icons/rc/arrow_down.png"); + height: 8px; + width: 8px; + top: 0; + /* Exclude a shift for better image */ + left: -2px; + /* Shift it a bit */ +} + +QToolButton::menu-arrow { + image: url(":/qss_icons/rc/arrow_down.png"); + height: 8px; + width: 8px; +} + +QToolButton::menu-arrow:hover { + image: url(":/qss_icons/rc/arrow_down_focus.png"); +} + +/* QCommandLinkButton ----------------------------------------------------- + +--------------------------------------------------------------------------- */ +QCommandLinkButton { + background-color: transparent; + border: 1px solid #32414B; + color: #F0F0F0; + border-radius: 4px; + padding: 0px; + margin: 0px; +} + +QCommandLinkButton:disabled { + background-color: transparent; + color: #787878; +} + +/* ------------------------------------------------------------------------ */ +/* INPUTS - NO FIELDS ----------------------------------------------------- */ +/* ------------------------------------------------------------------------ */ +/* QComboBox -------------------------------------------------------------- + +https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qcombobox + +--------------------------------------------------------------------------- */ +QComboBox { + border: 1px solid #32414B; + border-radius: 4px; + selection-background-color: #1464A0; + padding-left: 4px; + padding-right: 36px; + /* 4 + 16*2 See scrollbar size */ + /* Fixes #103, #111 */ + /* min-height: 1.5em; */ + /* padding-top: 2px; removed to fix #132 */ + /* padding-bottom: 2px; removed to fix #132 */ + /* min-width: 75px; removed to fix #109 */ + /* Needed to remove indicator - fix #132 */ +} + +QComboBox QAbstractItemView { + border: 1px solid #32414B; + border-radius: 0; + background-color: #19232D; + selection-background-color: #1464A0; +} + +QComboBox QAbstractItemView:hover { + background-color: #19232D; + color: #F0F0F0; +} + +QComboBox QAbstractItemView:selected { + background: #1464A0; + color: #32414B; +} + +QComboBox QAbstractItemView:alternate { + background: #19232D; +} + +QComboBox:disabled { + background-color: #19232D; + color: #787878; +} + +QComboBox:hover { + border: 1px solid #148CD2; +} + +QComboBox:focus { + border: 1px solid #1464A0; +} + +QComboBox:on { + selection-background-color: #1464A0; +} + +QComboBox::indicator { + border: none; + border-radius: 0; + background-color: transparent; + selection-background-color: transparent; + color: transparent; + selection-color: transparent; + /* Needed to remove indicator - fix #132 */ +} + +QComboBox::indicator:alternate { + background: #19232D; +} + +QComboBox::item:alternate { + background: #19232D; +} + +QComboBox::item:checked { + font-weight: bold; +} + +QComboBox::item:selected { + border: 0px solid transparent; +} + +QComboBox::drop-down { + width: 12px; + border-left: 1px solid #32414B; +} + +QComboBox::down-arrow { + image: url(":/qss_icons/rc/arrow_down_disabled.png"); + height: 8px; + width: 8px; +} + +QComboBox::down-arrow:on, QComboBox::down-arrow:hover, QComboBox::down-arrow:focus { + image: url(":/qss_icons/rc/arrow_down.png"); +} + +/* QSlider ---------------------------------------------------------------- + +https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qslider + +--------------------------------------------------------------------------- */ +QSlider:disabled { + background: #19232D; +} + +QSlider:focus { + border: none; +} + +QSlider::groove:horizontal { + background: #32414B; + border: 1px solid #32414B; + height: 4px; + margin: 0px; + border-radius: 4px; +} + +QSlider::groove:vertical { + background: #32414B; + border: 1px solid #32414B; + width: 4px; + margin: 0px; + border-radius: 4px; +} + +QSlider::add-page:vertical { + background: #1464A0; + border: 1px solid #32414B; + width: 4px; + margin: 0px; + border-radius: 4px; +} + +QSlider::add-page:vertical :disabled { + background: #14506E; +} + +QSlider::sub-page:horizontal { + background: #1464A0; + border: 1px solid #32414B; + height: 4px; + margin: 0px; + border-radius: 4px; +} + +QSlider::sub-page:horizontal:disabled { + background: #14506E; +} + +QSlider::handle:horizontal { + background: #787878; + border: 1px solid #32414B; + width: 8px; + height: 8px; + margin: -8px 0px; + border-radius: 4px; +} + +QSlider::handle:horizontal:hover { + background: #148CD2; + border: 1px solid #148CD2; +} + +QSlider::handle:horizontal:focus { + border: 1px solid #1464A0; +} + +QSlider::handle:vertical { + background: #787878; + border: 1px solid #32414B; + width: 8px; + height: 8px; + margin: 0 -8px; + border-radius: 4px; +} + +QSlider::handle:vertical:hover { + background: #148CD2; + border: 1px solid #148CD2; +} + +QSlider::handle:vertical:focus { + border: 1px solid #1464A0; +} + +/* QLineEdit -------------------------------------------------------------- + +https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qlineedit + +--------------------------------------------------------------------------- */ +QLineEdit { + background-color: #19232D; + padding-top: 2px; + /* This QLineEdit fix 103, 111 */ + padding-bottom: 2px; + /* This QLineEdit fix 103, 111 */ + padding-left: 4px; + padding-right: 4px; + border-style: solid; + border: 1px solid #32414B; + border-radius: 4px; + color: #F0F0F0; +} + +QLineEdit:disabled { + background-color: #19232D; + color: #787878; +} + +QLineEdit:hover { + border: 1px solid #148CD2; + color: #F0F0F0; +} + +QLineEdit:focus { + border: 1px solid #1464A0; +} + +QLineEdit:selected { + background-color: #1464A0; + color: #32414B; +} + +/* QTabWiget -------------------------------------------------------------- + +https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qtabwidget-and-qtabbar + +--------------------------------------------------------------------------- */ +QTabWidget { + padding: 2px; + selection-background-color: #32414B; +} + +QTabWidget QWidget { + /* Fixes #189 */ + border-radius: 4px; +} + +QTabWidget::pane { + border: 1px solid #32414B; + border-radius: 4px; + margin: 0px; + /* Fixes double border inside pane with pyqt5 */ + padding: 0px; +} + +QTabWidget::pane:selected { + background-color: #32414B; + border: 1px solid #1464A0; +} + +/* QTabBar ---------------------------------------------------------------- + +https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qtabwidget-and-qtabbar + +--------------------------------------------------------------------------- */ +QTabBar { + qproperty-drawBase: 0; + border-radius: 4px; + margin: 0px; + padding: 2px; + border: 0; + /* left: 5px; move to the right by 5px - removed for fix */ +} + +QTabBar::close-button { + border: 0; + margin: 2px; + padding: 2px; + image: url(":/qss_icons/rc/window_close.png"); +} + +QTabBar::close-button:hover { + image: url(":/qss_icons/rc/window_close_focus.png"); +} + +QTabBar::close-button:pressed { + image: url(":/qss_icons/rc/window_close_pressed.png"); +} + +/* QTabBar::tab - selected ------------------------------------------------ + +https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qtabwidget-and-qtabbar + +--------------------------------------------------------------------------- */ +QTabBar::tab { + /* !selected and disabled ----------------------------------------- */ + /* selected ------------------------------------------------------- */ +} + +QTabBar::tab:top:selected:disabled { + border-bottom: 3px solid #14506E; + color: #787878; + background-color: #32414B; +} + +QTabBar::tab:bottom:selected:disabled { + border-top: 3px solid #14506E; + color: #787878; + background-color: #32414B; +} + +QTabBar::tab:left:selected:disabled { + border-right: 3px solid #14506E; + color: #787878; + background-color: #32414B; +} + +QTabBar::tab:right:selected:disabled { + border-left: 3px solid #14506E; + color: #787878; + background-color: #32414B; +} + +QTabBar::tab:top:!selected:disabled { + border-bottom: 3px solid #19232D; + color: #787878; + background-color: #19232D; +} + +QTabBar::tab:bottom:!selected:disabled { + border-top: 3px solid #19232D; + color: #787878; + background-color: #19232D; +} + +QTabBar::tab:left:!selected:disabled { + border-right: 3px solid #19232D; + color: #787878; + background-color: #19232D; +} + +QTabBar::tab:right:!selected:disabled { + border-left: 3px solid #19232D; + color: #787878; + background-color: #19232D; +} + +QTabBar::tab:top:!selected { + border-bottom: 2px solid #19232D; + margin-top: 2px; +} + +QTabBar::tab:bottom:!selected { + border-top: 2px solid #19232D; + margin-bottom: 3px; +} + +QTabBar::tab:left:!selected { + border-left: 2px solid #19232D; + margin-right: 2px; +} + +QTabBar::tab:right:!selected { + border-right: 2px solid #19232D; + margin-left: 2px; +} + +QTabBar::tab:top { + background-color: #32414B; + color: #F0F0F0; + margin-left: 2px; + padding-left: 4px; + padding-right: 4px; + padding-top: 2px; + padding-bottom: 2px; + min-width: 5px; + border-bottom: 3px solid #32414B; + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} + +QTabBar::tab:top:selected { + background-color: #505F69; + color: #F0F0F0; + border-bottom: 3px solid #1464A0; + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} + +QTabBar::tab:top:!selected:hover { + border: 1px solid #148CD2; + border-bottom: 3px solid #148CD2; + /* Fixes spyder-ide/spyder#9766 */ + padding-left: 4px; + padding-right: 4px; +} + +QTabBar::tab:bottom { + color: #F0F0F0; + border-top: 3px solid #32414B; + background-color: #32414B; + margin-left: 2px; + padding-left: 4px; + padding-right: 4px; + padding-top: 2px; + padding-bottom: 2px; + border-bottom-left-radius: 3px; + border-bottom-right-radius: 3px; + min-width: 5px; +} + +QTabBar::tab:bottom:selected { + color: #F0F0F0; + background-color: #505F69; + border-top: 3px solid #1464A0; + border-bottom-left-radius: 3px; + border-bottom-right-radius: 3px; +} + +QTabBar::tab:bottom:!selected:hover { + border: 1px solid #148CD2; + border-top: 3px solid #148CD2; + /* Fixes spyder-ide/spyder#9766 */ + padding-left: 4px; + padding-right: 4px; +} + +QTabBar::tab:left { + color: #F0F0F0; + background-color: #32414B; + margin-top: 2px; + padding-left: 2px; + padding-right: 2px; + padding-top: 4px; + padding-bottom: 4px; + border-top-left-radius: 3px; + border-bottom-left-radius: 3px; + min-height: 5px; +} + +QTabBar::tab:left:selected { + color: #F0F0F0; + background-color: #505F69; + border-right: 3px solid #1464A0; +} + +QTabBar::tab:left:!selected:hover { + border: 1px solid #148CD2; + border-right: 3px solid #148CD2; + padding: 0px; +} + +QTabBar::tab:right { + color: #F0F0F0; + background-color: #32414B; + margin-top: 2px; + padding-left: 2px; + padding-right: 2px; + padding-top: 4px; + padding-bottom: 4px; + border-top-right-radius: 3px; + border-bottom-right-radius: 3px; + min-height: 5px; +} + +QTabBar::tab:right:selected { + color: #F0F0F0; + background-color: #505F69; + border-left: 3px solid #1464A0; +} + +QTabBar::tab:right:!selected:hover { + border: 1px solid #148CD2; + border-left: 3px solid #148CD2; + padding: 0px; +} + +QTabBar QToolButton { + /* Fixes #136 */ + background-color: #32414B; + height: 12px; + width: 12px; +} + +QTabBar QToolButton:pressed { + background-color: #32414B; +} + +QTabBar QToolButton:pressed:hover { + border: 1px solid #148CD2; +} + +QTabBar QToolButton::left-arrow:enabled { + image: url(":/qss_icons/rc/arrow_left.png"); +} + +QTabBar QToolButton::left-arrow:disabled { + image: url(":/qss_icons/rc/arrow_left_disabled.png"); +} + +QTabBar QToolButton::right-arrow:enabled { + image: url(":/qss_icons/rc/arrow_right.png"); +} + +QTabBar QToolButton::right-arrow:disabled { + image: url(":/qss_icons/rc/arrow_right_disabled.png"); +} + +/* QDockWiget ------------------------------------------------------------- + +--------------------------------------------------------------------------- */ +QDockWidget { + outline: 1px solid #32414B; + background-color: #19232D; + border: 1px solid #32414B; + border-radius: 4px; + titlebar-close-icon: url(":/qss_icons/rc/window_close.png"); + titlebar-normal-icon: url(":/qss_icons/rc/window_undock.png"); +} + +QDockWidget::title { + /* Better size for title bar */ + padding: 6px; + spacing: 4px; + border: none; + background-color: #32414B; +} + +QDockWidget::close-button { + background-color: #32414B; + border-radius: 4px; + border: none; +} + +QDockWidget::close-button:hover { + image: url(":/qss_icons/rc/window_close_focus.png"); +} + +QDockWidget::close-button:pressed { + image: url(":/qss_icons/rc/window_close_pressed.png"); +} + +QDockWidget::float-button { + background-color: #32414B; + border-radius: 4px; + border: none; +} + +QDockWidget::float-button:hover { + image: url(":/qss_icons/rc/window_undock_focus.png"); +} + +QDockWidget::float-button:pressed { + image: url(":/qss_icons/rc/window_undock_pressed.png"); +} + +/* QTreeView QListView QTableView ----------------------------------------- + +https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qtreeview +https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qlistview +https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qtableview + +--------------------------------------------------------------------------- */ +QTreeView:branch:selected, QTreeView:branch:hover { + background: url(":/qss_icons/rc/transparent.png"); +} + +QTreeView:branch:has-siblings:!adjoins-item { + border-image: url(":/qss_icons/rc/branch_line.png") 0; +} + +QTreeView:branch:has-siblings:adjoins-item { + border-image: url(":/qss_icons/rc/branch_more.png") 0; +} + +QTreeView:branch:!has-children:!has-siblings:adjoins-item { + border-image: url(":/qss_icons/rc/branch_end.png") 0; +} + +QTreeView:branch:has-children:!has-siblings:closed, QTreeView:branch:closed:has-children:has-siblings { + border-image: none; + image: url(":/qss_icons/rc/branch_closed.png"); +} + +QTreeView:branch:open:has-children:!has-siblings, QTreeView:branch:open:has-children:has-siblings { + border-image: none; + image: url(":/qss_icons/rc/branch_open.png"); +} + +QTreeView:branch:has-children:!has-siblings:closed:hover, QTreeView:branch:closed:has-children:has-siblings:hover { + image: url(":/qss_icons/rc/branch_closed_focus.png"); +} + +QTreeView:branch:open:has-children:!has-siblings:hover, QTreeView:branch:open:has-children:has-siblings:hover { + image: url(":/qss_icons/rc/branch_open_focus.png"); +} + +QTreeView::indicator:checked, +QListView::indicator:checked { + image: url(":/qss_icons/rc/checkbox_checked.png"); +} + +QTreeView::indicator:checked:hover, QTreeView::indicator:checked:focus, QTreeView::indicator:checked:pressed, +QListView::indicator:checked:hover, +QListView::indicator:checked:focus, +QListView::indicator:checked:pressed { + image: url(":/qss_icons/rc/checkbox_checked_focus.png"); +} + +QTreeView::indicator:unchecked, +QListView::indicator:unchecked { + image: url(":/qss_icons/rc/checkbox_unchecked.png"); +} + +QTreeView::indicator:unchecked:hover, QTreeView::indicator:unchecked:focus, QTreeView::indicator:unchecked:pressed, +QListView::indicator:unchecked:hover, +QListView::indicator:unchecked:focus, +QListView::indicator:unchecked:pressed { + image: url(":/qss_icons/rc/checkbox_unchecked_focus.png"); +} + +QTreeView::indicator:indeterminate, +QListView::indicator:indeterminate { + image: url(":/qss_icons/rc/checkbox_indeterminate.png"); +} + +QTreeView::indicator:indeterminate:hover, QTreeView::indicator:indeterminate:focus, QTreeView::indicator:indeterminate:pressed, +QListView::indicator:indeterminate:hover, +QListView::indicator:indeterminate:focus, +QListView::indicator:indeterminate:pressed { + image: url(":/qss_icons/rc/checkbox_indeterminate_focus.png"); +} + +QTreeView, +QListView, +QTableView, +QColumnView { + background-color: #19232D; + border: 1px solid #32414B; + color: #F0F0F0; + gridline-color: #32414B; + border-radius: 4px; +} + +QTreeView:disabled, +QListView:disabled, +QTableView:disabled, +QColumnView:disabled { + background-color: #19232D; + color: #787878; +} + +QTreeView:selected, +QListView:selected, +QTableView:selected, +QColumnView:selected { + background-color: #1464A0; + color: #32414B; +} + +QTreeView:hover, +QListView:hover, +QTableView:hover, +QColumnView:hover { + background-color: #19232D; + border: 1px solid #148CD2; +} + +QTreeView::item:pressed, +QListView::item:pressed, +QTableView::item:pressed, +QColumnView::item:pressed { + background-color: #1464A0; +} + +QTreeView::item:selected:hover, +QListView::item:selected:hover, +QTableView::item:selected:hover, +QColumnView::item:selected:hover { + background: #1464A0; + color: #19232D; +} + +QTreeView::item:selected:active, +QListView::item:selected:active, +QTableView::item:selected:active, +QColumnView::item:selected:active { + background-color: #1464A0; +} + +QTreeView::item:!selected:hover, +QListView::item:!selected:hover, +QTableView::item:!selected:hover, +QColumnView::item:!selected:hover { + outline: 0; + color: #148CD2; + background-color: #32414B; +} + +QTableCornerButton::section { + background-color: #19232D; + border: 1px transparent #32414B; + border-radius: 0px; +} + +/* QHeaderView ------------------------------------------------------------ + +https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qheaderview + +--------------------------------------------------------------------------- */ +QHeaderView { + background-color: #32414B; + border: 0px transparent #32414B; + padding: 0px; + margin: 0px; + border-radius: 0px; +} + +QHeaderView:disabled { + background-color: #32414B; + border: 1px transparent #32414B; + padding: 2px; +} + +QHeaderView::section { + background-color: #32414B; + color: #F0F0F0; + padding: 2px; + border-radius: 0px; + text-align: left; +} + +QHeaderView::section:checked { + color: #F0F0F0; + background-color: #1464A0; +} + +QHeaderView::section:checked:disabled { + color: #787878; + background-color: #14506E; +} + +QHeaderView::section::horizontal { + padding-left: 4px; + padding-right: 4px; + border-left: 1px solid #19232D; +} + +QHeaderView::section::horizontal::first, QHeaderView::section::horizontal::only-one { + border-left: 1px solid #32414B; +} + +QHeaderView::section::horizontal:disabled { + color: #787878; +} + +QHeaderView::section::vertical { + padding-left: 4px; + padding-right: 4px; + border-top: 1px solid #19232D; +} + +QHeaderView::section::vertical::first, QHeaderView::section::vertical::only-one { + border-top: 1px solid #32414B; +} + +QHeaderView::section::vertical:disabled { + color: #787878; +} + +QHeaderView::down-arrow { + /* Those settings (border/width/height/background-color) solve bug */ + /* transparent arrow background and size */ + background-color: #32414B; + border: none; + height: 12px; + width: 12px; + padding-left: 2px; + padding-right: 2px; + image: url(":/qss_icons/rc/arrow_down.png"); +} + +QHeaderView::up-arrow { + background-color: #32414B; + border: none; + height: 12px; + width: 12px; + padding-left: 2px; + padding-right: 2px; + image: url(":/qss_icons/rc/arrow_up.png"); +} + +/* QToolBox -------------------------------------------------------------- + +https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qtoolbox + +--------------------------------------------------------------------------- */ +QToolBox { + padding: 0px; + border: 0px; + border: 1px solid #32414B; +} + +QToolBox:selected { + padding: 0px; + border: 2px solid #1464A0; +} + +QToolBox::tab { + background-color: #19232D; + border: 1px solid #32414B; + color: #F0F0F0; + border-top-left-radius: 4px; + border-top-right-radius: 4px; +} + +QToolBox::tab:disabled { + color: #787878; +} + +QToolBox::tab:selected { + background-color: #505F69; + border-bottom: 2px solid #1464A0; +} + +QToolBox::tab:selected:disabled { + background-color: #32414B; + border-bottom: 2px solid #14506E; +} + +QToolBox::tab:!selected { + background-color: #32414B; + border-bottom: 2px solid #32414B; +} + +QToolBox::tab:!selected:disabled { + background-color: #19232D; +} + +QToolBox::tab:hover { + border-color: #148CD2; + border-bottom: 2px solid #148CD2; +} + +QToolBox QScrollArea QWidget QWidget { + padding: 0px; + border: 0px; + background-color: #19232D; +} + +/* QFrame ----------------------------------------------------------------- + +https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qframe +https://doc.qt.io/qt-5/qframe.html#-prop +https://doc.qt.io/qt-5/qframe.html#details +https://stackoverflow.com/questions/14581498/qt-stylesheet-for-hline-vline-color + +--------------------------------------------------------------------------- */ +/* (dot) .QFrame fix #141, #126, #123 */ +.QFrame { + border-radius: 4px; + border: 1px solid #32414B; + /* No frame */ + /* HLine */ + /* HLine */ +} + +.QFrame[frameShape="0"] { + border-radius: 4px; + border: 1px transparent #32414B; +} + +.QFrame[frameShape="4"] { + max-height: 2px; + border: none; + background-color: #32414B; +} + +.QFrame[frameShape="5"] { + max-width: 2px; + border: none; + background-color: #32414B; +} + +/* QSplitter -------------------------------------------------------------- + +https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qsplitter + +--------------------------------------------------------------------------- */ +QSplitter { + background-color: #32414B; + spacing: 0px; + padding: 0px; + margin: 0px; +} + +QSplitter::handle { + background-color: #32414B; + border: 0px solid #19232D; + spacing: 0px; + padding: 1px; + margin: 0px; +} + +QSplitter::handle:hover { + background-color: #787878; +} + +QSplitter::handle:horizontal { + width: 5px; + image: url(":/qss_icons/rc/line_vertical.png"); +} + +QSplitter::handle:vertical { + height: 5px; + image: url(":/qss_icons/rc/line_horizontal.png"); +} + +/* QDateEdit, QDateTimeEdit ----------------------------------------------- + +--------------------------------------------------------------------------- */ +QDateEdit, QDateTimeEdit { + selection-background-color: #1464A0; + border-style: solid; + border: 1px solid #32414B; + border-radius: 4px; + /* This fixes 103, 111 */ + padding-top: 2px; + /* This fixes 103, 111 */ + padding-bottom: 2px; + padding-left: 4px; + padding-right: 4px; + min-width: 10px; +} + +QDateEdit:on, QDateTimeEdit:on { + selection-background-color: #1464A0; +} + +QDateEdit::drop-down, QDateTimeEdit::drop-down { + subcontrol-origin: padding; + subcontrol-position: top right; + width: 12px; + border-left: 1px solid #32414B; +} + +QDateEdit::down-arrow, QDateTimeEdit::down-arrow { + image: url(":/qss_icons/rc/arrow_down_disabled.png"); + height: 8px; + width: 8px; +} + +QDateEdit::down-arrow:on, QDateEdit::down-arrow:hover, QDateEdit::down-arrow:focus, QDateTimeEdit::down-arrow:on, QDateTimeEdit::down-arrow:hover, QDateTimeEdit::down-arrow:focus { + image: url(":/qss_icons/rc/arrow_down.png"); +} + +QDateEdit QAbstractItemView, QDateTimeEdit QAbstractItemView { + background-color: #19232D; + border-radius: 4px; + border: 1px solid #32414B; + selection-background-color: #1464A0; +} + +/* QAbstractView ---------------------------------------------------------- + +--------------------------------------------------------------------------- */ +QAbstractView:hover { + border: 1px solid #148CD2; + color: #F0F0F0; +} + +QAbstractView:selected { + background: #1464A0; + color: #32414B; +} + +/* PlotWidget ------------------------------------------------------------- + +--------------------------------------------------------------------------- */ +PlotWidget { + /* Fix cut labels in plots #134 */ + padding: 0px; +} + diff --git a/Rare/__init__.py b/Rare/__init__.py new file mode 100644 index 00000000..3b4b7e54 --- /dev/null +++ b/Rare/__init__.py @@ -0,0 +1,4 @@ +import os +__version__ = "0.3.5" +style_path = os.path.join(os.path.dirname(__file__), "Styles/") +lang_path = os.path.join(os.path.dirname(__file__), "languages/") diff --git a/Rare/__pycache__/__init__.cpython-38.pyc b/Rare/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 00000000..eac0adec Binary files /dev/null and b/Rare/__pycache__/__init__.cpython-38.pyc differ diff --git a/Rare/__pycache__/__init__.cpython-39.pyc b/Rare/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 00000000..4f5677f1 Binary files /dev/null and b/Rare/__pycache__/__init__.cpython-39.pyc differ diff --git a/Rare/utils/Dialogs/InstallDialog.py b/Rare/utils/Dialogs/InstallDialog.py new file mode 100644 index 00000000..817e8a1f --- /dev/null +++ b/Rare/utils/Dialogs/InstallDialog.py @@ -0,0 +1,38 @@ +import os + +from PyQt5.QtWidgets import QDialog, QFormLayout, QVBoxLayout, QSpinBox, QFileDialog, QLabel, QPushButton, QHBoxLayout + +from Rare.utils.QtExtensions import PathEdit + + +class InstallDialog(QDialog): + def __init__(self): + super(InstallDialog, self).__init__() + self.layout = QVBoxLayout() + + self.form = QFormLayout() + default_path = os.path.expanduser("~/legendary") + #TODO read from config + self.install_path_field = PathEdit(text=default_path, type_of_file=QFileDialog.DirectoryOnly) + self.form.addRow(QLabel("Install directory"), self.install_path_field) + + self.max_workes = QSpinBox() + self.form.addRow(QLabel("Max workers"), self.max_workes) + + self.layout.addLayout(self.form) + + self.ok = QPushButton("Install") + self.cancel = QPushButton("Cancel") + + self.button_layout = QHBoxLayout() + self.button_layout.addStretch(1) + self.button_layout.addWidget(self.ok) + self.button_layout.addWidget(self.cancel) + + self.layout.addLayout(self.button_layout) + + self.setLayout(self.layout) + + def get_information(self): + self.exec_() + return self.install_path_field.text(), self.max_workes.value() diff --git a/Rare/utils/Dialogs/__pycache__/InstallDialog.cpython-39.pyc b/Rare/utils/Dialogs/__pycache__/InstallDialog.cpython-39.pyc new file mode 100644 index 00000000..572f985e Binary files /dev/null and b/Rare/utils/Dialogs/__pycache__/InstallDialog.cpython-39.pyc differ diff --git a/Rare/utils/LegendaryApi.py b/Rare/utils/LegendaryApi.py new file mode 100644 index 00000000..ec41862c --- /dev/null +++ b/Rare/utils/LegendaryApi.py @@ -0,0 +1,41 @@ +import os + +from PyQt5.QtCore import QProcess, QProcessEnvironment +from legendary.core import LegendaryCore + + + + +def launch_game(core, app_name: str, offline: bool = False, skip_version_check: bool = False): + game = core.get_installed_game(app_name) + if not game: + print("Game not found") + return None + if game.is_dlc: + print("Game is dlc") + return None + if not os.path.exists(game.install_path): + print("Game doesn't exist") + return None + + if not offline: + + if not skip_version_check and not core.is_noupdate_game(app_name): + # check updates + try: + latest = core.get_asset(app_name, update=True) + except ValueError: + print("Metadata doesn't exist") + return None + if latest.build_version != game.version: + print("Please update game") + return None + params, cwd, env = core.get_launch_parameters(app_name=app_name, offline=offline) + process = QProcess() + process.setWorkingDirectory(cwd) + environment = QProcessEnvironment() + for e in env: + environment.insert(e, env[e]) + process.setProcessEnvironment(environment) + process.start(params[0], params[1:]) + return process \ No newline at end of file diff --git a/Rare/utils/QtExtensions.py b/Rare/utils/QtExtensions.py new file mode 100644 index 00000000..7ce2eaa3 --- /dev/null +++ b/Rare/utils/QtExtensions.py @@ -0,0 +1,148 @@ +import os + +from PyQt5.QtCore import Qt, QRect, QSize, QPoint, pyqtSignal +from PyQt5.QtWidgets import QLayout, QStyle, QSizePolicy, QLabel, QFileDialog, QHBoxLayout, QWidget, QLineEdit, \ + QPushButton + + +class FlowLayout(QLayout): + def __init__(self, parent=None, margin=-1, hspacing=-1, vspacing=-1): + super(FlowLayout, self).__init__(parent) + self._hspacing = hspacing + self._vspacing = vspacing + self._items = [] + self.setContentsMargins(margin, margin, margin, margin) + + def __del__(self): + del self._items[:] + + def addItem(self, item): + self._items.append(item) + + def horizontalSpacing(self): + if self._hspacing >= 0: + return self._hspacing + else: + return self.smartSpacing( + QStyle.PM_LayoutHorizontalSpacing) + + def verticalSpacing(self): + if self._vspacing >= 0: + return self._vspacing + else: + return self.smartSpacing( + QStyle.PM_LayoutVerticalSpacing) + + def count(self): + return len(self._items) + + def itemAt(self, index): + if 0 <= index < len(self._items): + return self._items[index] + + def takeAt(self, index): + if 0 <= index < len(self._items): + return self._items.pop(index) + + def expandingDirections(self): + return Qt.Orientations(0) + + def hasHeightForWidth(self): + return True + + def heightForWidth(self, width): + return self.doLayout(QRect(0, 0, width, 0), True) + + def setGeometry(self, rect): + super(FlowLayout, self).setGeometry(rect) + self.doLayout(rect, False) + + def sizeHint(self): + return self.minimumSize() + + def minimumSize(self): + size = QSize() + for item in self._items: + size = size.expandedTo(item.minimumSize()) + left, top, right, bottom = self.getContentsMargins() + size += QSize(left + right, top + bottom) + return size + + def doLayout(self, rect, testonly): + left, top, right, bottom = self.getContentsMargins() + effective = rect.adjusted(+left, +top, -right, -bottom) + x = effective.x() + y = effective.y() + lineheight = 0 + for item in self._items: + widget = item.widget() + hspace = self.horizontalSpacing() + if hspace == -1: + hspace = widget.style().layoutSpacing( + QSizePolicy.PushButton, + QSizePolicy.PushButton, Qt.Horizontal) + vspace = self.verticalSpacing() + if vspace == -1: + vspace = widget.style().layoutSpacing( + QSizePolicy.PushButton, + QSizePolicy.PushButton, Qt.Vertical) + nextX = x + item.sizeHint().width() + hspace + if nextX - hspace > effective.right() and lineheight > 0: + x = effective.x() + y = y + lineheight + vspace + nextX = x + item.sizeHint().width() + hspace + lineheight = 0 + if not testonly: + item.setGeometry( + QRect(QPoint(x, y), item.sizeHint())) + x = nextX + lineheight = max(lineheight, item.sizeHint().height()) + return y + lineheight - rect.y() + bottom + + def smartSpacing(self, pm): + parent = self.parent() + if parent is None: + return -1 + elif parent.isWidgetType(): + return parent.style().pixelMetric(pm, None, parent) + else: + return parent.spacing() + + +class ClickableLabel(QLabel): + clicked = pyqtSignal() + + def __init__(self): + super(ClickableLabel, self).__init__() + + +class PathEdit(QWidget): + def __init__(self, text: str = "", type_of_file: QFileDialog.FileType = QFileDialog.AnyFile, infotext: str = "", + filter: str = None): + super(PathEdit, self).__init__() + self.filter = filter + self.type_of_file = type_of_file + self.info_text = infotext + self.layout = QHBoxLayout() + self.text_edit = QLineEdit(text) + self.path_select = QPushButton(self.tr("Select Path")) + self.path_select.clicked.connect(self.set_path) + self.layout.addWidget(self.text_edit) + self.layout.addWidget(self.path_select) + self.setLayout(self.layout) + + def setPlaceholderText(self, text: str): + self.text_edit.setPlaceholderText(text) + + def text(self): + return self.text_edit.text() + + def set_path(self): + dlg = QFileDialog(self, self.tr("Choose Path"), os.path.expanduser("~/")) + dlg.setFileMode(self.type_of_file) + if self.filter: + dlg.setFilter([self.filter]) + if dlg.exec_(): + names = dlg.selectedFiles() + self.text_edit.setText(names[0]) + diff --git a/Rare/utils/RareConfig.py b/Rare/utils/RareConfig.py new file mode 100644 index 00000000..6d36d244 --- /dev/null +++ b/Rare/utils/RareConfig.py @@ -0,0 +1,36 @@ +import configparser +import os +from logging import getLogger + +config_path = os.path.join(os.path.expanduser("~"), ".config/Rare/") +rare_config = configparser.ConfigParser() + +logger = getLogger("Config") +rare_config.read(config_path + "config.ini") + +if not os.path.exists(config_path): + os.makedirs(config_path) + rare_config["Rare"] = { + "IMAGE_DIR": os.path.expanduser(f"~/.cache/images"), + "theme": "default" + } + rare_config.write(open(config_path + "config.ini", "w")) +elif not rare_config.sections(): + rare_config["Rare"] = { + "IMAGE_DIR": os.path.expanduser("~/.cache/images"), + "theme": "default" + } + rare_config.write(open(config_path + "config.ini", "w")) + + +def get_config() -> {}: + return rare_config.__dict__["_sections"] + + +def set_config(new_config: {}): + rare_config.__dict__["_sections"] = new_config + rare_config.write(open(config_path + "config.ini", "w")) + + +IMAGE_DIR = rare_config["Rare"]["IMAGE_DIR"] +THEME = rare_config["Rare"]["theme"] diff --git a/Rare/utils/__init__.py b/Rare/utils/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/Rare/utils/__pycache__/LegendaryApi.cpython-39.pyc b/Rare/utils/__pycache__/LegendaryApi.cpython-39.pyc new file mode 100644 index 00000000..51a67440 Binary files /dev/null and b/Rare/utils/__pycache__/LegendaryApi.cpython-39.pyc differ diff --git a/Rare/utils/__pycache__/QtExtensions.cpython-38.pyc b/Rare/utils/__pycache__/QtExtensions.cpython-38.pyc new file mode 100644 index 00000000..9da2b837 Binary files /dev/null and b/Rare/utils/__pycache__/QtExtensions.cpython-38.pyc differ diff --git a/Rare/utils/__pycache__/QtExtensions.cpython-39.pyc b/Rare/utils/__pycache__/QtExtensions.cpython-39.pyc new file mode 100644 index 00000000..ca7ec4f6 Binary files /dev/null and b/Rare/utils/__pycache__/QtExtensions.cpython-39.pyc differ diff --git a/Rare/utils/__pycache__/RareConfig.cpython-38.pyc b/Rare/utils/__pycache__/RareConfig.cpython-38.pyc new file mode 100644 index 00000000..1ac5af23 Binary files /dev/null and b/Rare/utils/__pycache__/RareConfig.cpython-38.pyc differ diff --git a/Rare/utils/__pycache__/RareConfig.cpython-39.pyc b/Rare/utils/__pycache__/RareConfig.cpython-39.pyc new file mode 100644 index 00000000..651a84d1 Binary files /dev/null and b/Rare/utils/__pycache__/RareConfig.cpython-39.pyc differ diff --git a/Rare/utils/__pycache__/__init__.cpython-38.pyc b/Rare/utils/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 00000000..278ce053 Binary files /dev/null and b/Rare/utils/__pycache__/__init__.cpython-38.pyc differ diff --git a/Rare/utils/__pycache__/__init__.cpython-39.pyc b/Rare/utils/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 00000000..bcb49365 Binary files /dev/null and b/Rare/utils/__pycache__/__init__.cpython-39.pyc differ diff --git a/Rare/utils/legendaryConfig.py b/Rare/utils/legendaryConfig.py new file mode 100644 index 00000000..cc7e2a4f --- /dev/null +++ b/Rare/utils/legendaryConfig.py @@ -0,0 +1,14 @@ +import configparser +import os + +from legendary.lfs.lgndry import LGDLFS + +lgd = LGDLFS() + +def get_config() -> {}: + return lgd.config + +def set_config(new_config: {}): + lgd.config = new_config + with open(os.path.join(lgd.path, 'config.ini'), "w") as cf: + lgd.config.write(cf) diff --git a/Rare/utils/utils.py b/Rare/utils/utils.py new file mode 100644 index 00000000..5fdf81ab --- /dev/null +++ b/Rare/utils/utils.py @@ -0,0 +1,90 @@ +import json +import os +from logging import getLogger + +import requests +from PIL import Image +from PyQt5.QtCore import pyqtSignal, QLocale +from legendary.core import LegendaryCore + +from Rare.utils import legendaryConfig +from Rare.utils.RareConfig import IMAGE_DIR + +logger = getLogger("Utils") + + +def download_images(signal: pyqtSignal, core: LegendaryCore): + if not os.path.isdir(IMAGE_DIR): + os.makedirs(IMAGE_DIR) + logger.info("Create Image dir") + + # Download Images + for i, game in enumerate(sorted(core.get_game_list(), key=lambda x: x.app_title)): + + if not os.path.isdir(f"{IMAGE_DIR}/" + game.app_name): + os.mkdir(f"{IMAGE_DIR}/" + game.app_name) + + if not os.path.isfile(f"{IMAGE_DIR}/{game.app_name}/image.json"): + json_data = {"DieselGameBoxTall": None, "DieselGameBoxLogo": None} + else: + json_data = json.load(open(f"{IMAGE_DIR}/{game.app_name}/image.json", "r")) + + for image in game.metadata["keyImages"]: + if image["type"] == "DieselGameBoxTall" or image["type"] == "DieselGameBoxLogo": + + if json_data[image["type"]] != image["md5"] or not os.path.isfile( + f"{IMAGE_DIR}/{game.app_name}/{image['type']}.png"): + # Download + json_data[image["type"]] = image["md5"] + # os.remove(f"{IMAGE_DIR}/{game.app_name}/{image['type']}.png") + json.dump(json_data, open(f"{IMAGE_DIR}/{game.app_name}/image.json", "w")) + logger.info(f"Download Image for Game: {game.app_title}") + url = image["url"] + with open(f"{IMAGE_DIR}/{game.app_name}/{image['type']}.png", "wb") as f: + f.write(requests.get(url).content) + f.close() + + if not os.path.isfile(f'{IMAGE_DIR}/' + game.app_name + '/UninstalledArt.png'): + + if os.path.isfile(f'{IMAGE_DIR}/' + game.app_name + '/DieselGameBoxTall.png'): + # finalArt = Image.open(f'{IMAGE_DIR}/' + game.app_name + '/DieselGameBoxTall.png') + # finalArt.save(f'{IMAGE_DIR}/{game.app_name}/FinalArt.png') + # And same with the grayscale one + + bg = Image.open(f"{IMAGE_DIR}/{game.app_name}/DieselGameBoxTall.png") + uninstalledArt = bg.convert('L') + uninstalledArt.save(f'{IMAGE_DIR}/{game.app_name}/UninstalledArt.png') + elif os.path.isfile(f"{IMAGE_DIR}/{game.app_name}/DieselGameBoxLogo.png"): + bg: Image.Image = Image.open(f"{IMAGE_DIR}/{game.app_name}/DieselGameBoxLogo.png") + bg = bg.resize((int(bg.size[1] * 3 / 4), bg.size[1])) + logo = Image.open(f'{IMAGE_DIR}/{game.app_name}/DieselGameBoxLogo.png').convert('RGBA') + wpercent = ((bg.size[0] * (3 / 4)) / float(logo.size[0])) + hsize = int((float(logo.size[1]) * float(wpercent))) + logo = logo.resize((int(bg.size[0] * (3 / 4)), hsize), Image.ANTIALIAS) + # Calculate where the image has to be placed + pasteX = int((bg.size[0] - logo.size[0]) / 2) + pasteY = int((bg.size[1] - logo.size[1]) / 2) + # And finally copy the background and paste in the image + # finalArt = bg.copy() + # finalArt.paste(logo, (pasteX, pasteY), logo) + # Write out the file + # finalArt.save(f'{IMAGE_DIR}/' + game.app_name + '/FinalArt.png') + logoCopy = logo.copy() + logoCopy.putalpha(int(256 * 3 / 4)) + logo.paste(logoCopy, logo) + uninstalledArt = bg.copy() + uninstalledArt.paste(logo, (pasteX, pasteY), logo) + uninstalledArt = uninstalledArt.convert('L') + uninstalledArt.save(f'{IMAGE_DIR}/' + game.app_name + '/UninstalledArt.png') + else: + logger.warning(f"File {IMAGE_DIR}/{game.app_name}/DieselGameBoxTall.png dowsn't exist") + signal.emit(i) + + +def get_lang(): + if "Legendary" in legendaryConfig.get_config() and "locale" in legendaryConfig.get_config()["Legendary"]: + logger.info("Found locale in Legendary config: " + legendaryConfig.get_config()["Legendary"]["locale"]) + return legendaryConfig.get_config()["Legendary"]["locale"].split("-")[0] + else: + logger.info("Found locale in system config: " + QLocale.system().name().split("_")[0]) + return QLocale.system().name().split("_")[0]