GameWidget: Implement reactive and interactive labels
The `status_label` displays what is currently going on with the game. It reflects the current operation running on it or if it requires special attention (update, needs verification etc) The `tooltip_label` displays hover information such as what happens if a part of the widget is clicked or in the case of the launch button if the game can run (without version check, offline etc) The context menu on the widgets will be updated and populated according to the installation state of the game. Since the context menu was revised the shortcut creation code was revised too to make it more compact. the `create_desktop_link` and `get_rare_executable` functions are moved from `rare.utils.misc` to `rare.utils.paths` to avoid cyclical imports and better grouping. Two functions are added, `desktop_link_path` to uniformly calculate the path of the shortcut and `desktop_links_supported` which checks if Rare supports creating shortcuts on the current platform. `desktop_links_supported` should be used as safeguard before `desktop_link_path`. Desktop links are currently untested on Windows but if `shortcut.Description` works as expected, it should be good to go.
This commit is contained in:
parent
2903c19667
commit
1721677e33
13 changed files with 447 additions and 434 deletions
|
@ -65,13 +65,13 @@ def main():
|
|||
args = parser.parse_args()
|
||||
|
||||
if args.desktop_shortcut or args.startmenu_shortcut:
|
||||
from rare.utils.misc import create_desktop_link
|
||||
from rare.utils.paths import create_desktop_link
|
||||
|
||||
if args.desktop_shortcut:
|
||||
create_desktop_link(type_of_link="desktop", for_rare=True)
|
||||
create_desktop_link(app_name="rare_shortcut", link_type="desktop")
|
||||
|
||||
if args.startmenu_shortcut:
|
||||
create_desktop_link(type_of_link="start_menu", for_rare=True)
|
||||
create_desktop_link(app_name="rare_shortcut", link_type="start_menu")
|
||||
|
||||
print("Link created")
|
||||
return
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import datetime
|
||||
import platform
|
||||
from ctypes import c_uint64
|
||||
from logging import getLogger
|
||||
from typing import Union, Optional
|
||||
|
@ -18,7 +19,8 @@ from rare.models.install import InstallOptionsModel, InstallQueueItemModel, Unin
|
|||
from rare.shared import RareCore
|
||||
from rare.shared.workers.install_info import InstallInfoWorker
|
||||
from rare.shared.workers.uninstall import UninstallWorker
|
||||
from rare.utils.misc import get_size, create_desktop_link
|
||||
from rare.utils.misc import get_size
|
||||
from rare.utils.paths import create_desktop_link, desktop_links_supported
|
||||
from .download import DownloadWidget
|
||||
from .groups import UpdateGroup, QueueGroup
|
||||
from .thread import DlThread, DlResultModel, DlResultCode
|
||||
|
@ -220,15 +222,18 @@ class DownloadsTab(QWidget):
|
|||
@pyqtSlot(DlResultModel)
|
||||
def __on_download_result(self, result: DlResultModel):
|
||||
if result.code == DlResultCode.FINISHED:
|
||||
if result.shortcuts:
|
||||
if not create_desktop_link(result.options.app_name, self.core, "desktop"):
|
||||
logger.info(f"Download finished: {result.options.app_name}")
|
||||
if result.shortcut and desktop_links_supported():
|
||||
if not create_desktop_link(
|
||||
app_name=result.options.app_name,
|
||||
app_title=result.shortcut_title,
|
||||
link_name=result.shortcut_name,
|
||||
link_type="desktop",
|
||||
):
|
||||
# maybe add it to download summary, to show in finished downloads
|
||||
pass
|
||||
logger.error(f"Failed to create desktop link on {platform.system()}")
|
||||
else:
|
||||
logger.info("Desktop shortcut written")
|
||||
logger.info(
|
||||
f"Download finished: {result.options.app_name}"
|
||||
)
|
||||
logger.info(f"Created desktop link {result.shortcut_name} for {result.options.app_name}")
|
||||
|
||||
if result.options.overlay:
|
||||
self.signals.application.overlay_installed.emit()
|
||||
|
@ -239,8 +244,8 @@ class DownloadsTab(QWidget):
|
|||
self.updates_group.set_widget_enabled(result.options.app_name, True)
|
||||
|
||||
elif result.code == DlResultCode.ERROR:
|
||||
QMessageBox.warning(self, self.tr("Error"), self.tr("Download error: {}").format(result.message))
|
||||
logger.error(f"Download error: {result.options.app_name} ({result.message})")
|
||||
QMessageBox.warning(self, self.tr("Error"), self.tr("Download error: {}").format(result.message))
|
||||
|
||||
elif result.code == DlResultCode.STOPPED:
|
||||
logger.info(f"Download stopped: {result.options.app_name}")
|
||||
|
|
|
@ -34,7 +34,9 @@ class DlResultModel:
|
|||
dlcs: Optional[List[Dict]] = None
|
||||
sync_saves: bool = False
|
||||
tip_url: str = ""
|
||||
shortcuts: bool = False
|
||||
shortcut: bool = False
|
||||
shortcut_name: str = ""
|
||||
shortcut_title: str = ""
|
||||
|
||||
|
||||
class DlThread(QThread):
|
||||
|
@ -149,7 +151,9 @@ class DlThread(QThread):
|
|||
)
|
||||
|
||||
if not self.item.options.update and self.item.options.create_shortcut:
|
||||
result.shortcuts = True
|
||||
result.shortcut = True
|
||||
result.shortcut_name = self.rgame.folder_name
|
||||
result.shortcut_title = self.rgame.app_title
|
||||
|
||||
self.__result_emit(result)
|
||||
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import os
|
||||
import platform
|
||||
from abc import abstractmethod
|
||||
from abc import ABCMeta
|
||||
from logging import getLogger
|
||||
|
||||
from PyQt5.QtCore import pyqtSignal, QStandardPaths, Qt, pyqtSlot
|
||||
from PyQt5.QtCore import pyqtSignal, Qt, pyqtSlot, QObject, QEvent
|
||||
from PyQt5.QtGui import QMouseEvent
|
||||
from PyQt5.QtWidgets import QMessageBox, QAction, QLabel, QPushButton
|
||||
|
||||
|
@ -14,12 +13,19 @@ from rare.shared import (
|
|||
ArgumentsSingleton,
|
||||
ImageManagerSingleton,
|
||||
)
|
||||
from rare.utils.misc import create_desktop_link
|
||||
from rare.utils.paths import desktop_links_supported, desktop_link_path, create_desktop_link
|
||||
from .library_widget import LibraryWidget
|
||||
|
||||
logger = getLogger("GameWidget")
|
||||
|
||||
|
||||
class GameWidgetUi(metaclass=ABCMeta):
|
||||
status_label: QLabel
|
||||
install_btn: QPushButton
|
||||
launch_btn: QPushButton
|
||||
tooltip_label: QLabel
|
||||
|
||||
|
||||
class GameWidget(LibraryWidget):
|
||||
show_info = pyqtSignal(RareGame)
|
||||
|
||||
|
@ -33,86 +39,41 @@ class GameWidget(LibraryWidget):
|
|||
self.rgame: RareGame = rgame
|
||||
|
||||
self.setContextMenuPolicy(Qt.ActionsContextMenu)
|
||||
if self.rgame.is_installed or self.rgame.is_origin:
|
||||
|
||||
self.launch_action = QAction(self.tr("Launch"), self)
|
||||
self.launch_action.triggered.connect(self._launch)
|
||||
self.addAction(self.launch_action)
|
||||
else:
|
||||
|
||||
self.install_action = QAction(self.tr("Install"), self)
|
||||
self.install_action.triggered.connect(self._install)
|
||||
self.addAction(self.install_action)
|
||||
|
||||
# if self.rgame.game.supports_cloud_saves:
|
||||
# sync = QAction(self.tr("Sync with cloud"), self)
|
||||
# sync.triggered.connect(self.sync_game)
|
||||
# self.addAction(sync)
|
||||
# self.sync_action = QAction(self.tr("Sync with cloud"), self)
|
||||
# self.sync_action.triggered.connect(self.sync_saves)
|
||||
|
||||
desktop = QStandardPaths.writableLocation(QStandardPaths.DesktopLocation)
|
||||
if os.path.exists(
|
||||
os.path.join(desktop, f"{self.rgame.app_title}.desktop")
|
||||
) or os.path.exists(os.path.join(desktop, f"{self.rgame.app_title}.lnk")):
|
||||
self.create_desktop = QAction(self.tr("Remove Desktop link"))
|
||||
else:
|
||||
self.create_desktop = QAction(self.tr("Create Desktop link"))
|
||||
if self.rgame.is_installed:
|
||||
self.create_desktop.triggered.connect(
|
||||
lambda: self.create_desktop_link("desktop")
|
||||
self.desktop_link_action = QAction(self)
|
||||
self.desktop_link_action.triggered.connect(
|
||||
lambda: self._create_link(self.rgame.folder_name, "desktop")
|
||||
)
|
||||
self.addAction(self.create_desktop)
|
||||
|
||||
applications = QStandardPaths.writableLocation(QStandardPaths.ApplicationsLocation)
|
||||
if platform.system() == "Linux":
|
||||
start_menu_file = os.path.join(applications, f"{self.rgame.app_title}.desktop")
|
||||
elif platform.system() == "Windows":
|
||||
start_menu_file = os.path.join(applications, "..", f"{self.rgame.app_title}.lnk")
|
||||
else:
|
||||
start_menu_file = ""
|
||||
if platform.system() in ["Windows", "Linux"]:
|
||||
if os.path.exists(start_menu_file):
|
||||
self.create_start_menu = QAction(self.tr("Remove start menu link"))
|
||||
else:
|
||||
self.create_start_menu = QAction(self.tr("Create start menu link"))
|
||||
if self.rgame.is_installed:
|
||||
self.create_start_menu.triggered.connect(
|
||||
lambda: self.create_desktop_link("start_menu")
|
||||
self.menu_link_action = QAction(self)
|
||||
self.menu_link_action.triggered.connect(
|
||||
lambda: self._create_link(self.rgame.folder_name, "start_menu")
|
||||
)
|
||||
self.addAction(self.create_start_menu)
|
||||
|
||||
reload_image = QAction(self.tr("Reload Image"), self)
|
||||
reload_image.triggered.connect(self._on_reload_image)
|
||||
self.addAction(reload_image)
|
||||
self.reload_action = QAction(self.tr("Reload Image"), self)
|
||||
self.reload_action.triggered.connect(self._on_reload_image)
|
||||
|
||||
if self.rgame.is_installed and not self.rgame.is_origin:
|
||||
self.uninstall_action = QAction(self.tr("Uninstall"), self)
|
||||
self.uninstall_action.triggered.connect(self._uninstall)
|
||||
self.addAction(self.uninstall_action)
|
||||
|
||||
self.texts = {
|
||||
"hover": {
|
||||
"update_available": self.tr("Start without version check"),
|
||||
"launch": self.tr("Launch Game"),
|
||||
"launch_origin": self.tr("Launch/Link"),
|
||||
"running": self.tr("Game running"),
|
||||
"launch_offline": self.tr("Launch offline"),
|
||||
"no_launch": self.tr("Can't launch game")
|
||||
},
|
||||
"status": {
|
||||
"needs_verification": self.tr("Please verify game before playing"),
|
||||
"running": self.tr("Game running"),
|
||||
"syncing": self.tr("Syncing cloud saves"),
|
||||
"update_available": self.tr("Update available"),
|
||||
"no_meta": self.tr("Game is only offline available"),
|
||||
"no_launch": self.tr("Can't launch game"),
|
||||
},
|
||||
}
|
||||
self.update_actions()
|
||||
|
||||
# signals
|
||||
self.rgame.signals.widget.update.connect(
|
||||
lambda: self.setPixmap(self.rgame.pixmap)
|
||||
)
|
||||
self.rgame.signals.widget.update.connect(
|
||||
self.update_widget
|
||||
)
|
||||
self.rgame.signals.widget.update.connect(lambda: self.setPixmap(self.rgame.pixmap))
|
||||
self.rgame.signals.widget.update.connect(self.update_buttons)
|
||||
self.rgame.signals.widget.update.connect(self.update_state)
|
||||
self.rgame.signals.game.installed.connect(self.update_actions)
|
||||
self.rgame.signals.game.uninstalled.connect(self.update_actions)
|
||||
|
||||
self.rgame.signals.progress.start.connect(
|
||||
lambda: self.showProgress(
|
||||
self.image_manager.get_pixmap(self.rgame.app_name, True),
|
||||
|
@ -125,67 +86,113 @@ class GameWidget(LibraryWidget):
|
|||
self.rgame.signals.progress.finish.connect(
|
||||
lambda e: self.hideProgress(e)
|
||||
)
|
||||
self.rgame.signals.progress.finish.connect(self.set_status)
|
||||
|
||||
@abstractmethod
|
||||
def set_status(self, label: QLabel):
|
||||
if self.rgame.is_installed:
|
||||
self.state_strings = {
|
||||
RareGame.State.IDLE: "",
|
||||
RareGame.State.RUNNING: self.tr("Running..."),
|
||||
RareGame.State.DOWNLOADING: self.tr("Downloading..."),
|
||||
RareGame.State.VERIFYING: self.tr("Verifying..."),
|
||||
RareGame.State.MOVING: self.tr("Moving..."),
|
||||
RareGame.State.UNINSTALLING: self.tr("Uninstalling..."),
|
||||
"has_update": self.tr("Update available"),
|
||||
"needs_verification": self.tr("Needs verification"),
|
||||
"not_can_launch": self.tr("Can't launch"),
|
||||
}
|
||||
|
||||
self.hover_strings = {
|
||||
"info": self.tr("Show infromation"),
|
||||
"install": self.tr("Install game"),
|
||||
"can_launch": self.tr("Launch game"),
|
||||
"is_foreign": self.tr("Launch offline"),
|
||||
"has_update": self.tr("Launch without version check"),
|
||||
"is_origin": self.tr("Launch/Link"),
|
||||
"not_can_launch": self.tr("Can't launch"),
|
||||
}
|
||||
|
||||
# lk: abstract class for typing, the `self.ui` attribute should be used
|
||||
# lk: by the Ui class in the children. It must contain at least the same
|
||||
# lk: attributes as `GameWidgetUi` class
|
||||
self.ui = GameWidgetUi()
|
||||
|
||||
@pyqtSlot()
|
||||
def update_state(self):
|
||||
if self.rgame.is_idle:
|
||||
if self.rgame.has_update:
|
||||
label.setText(self.texts["status"]["update_available"])
|
||||
return
|
||||
if self.rgame.needs_verification:
|
||||
label.setText(self.texts["status"]["needs_verification"])
|
||||
return
|
||||
label.setText("")
|
||||
label.setVisible(False)
|
||||
self.ui.status_label.setText(self.state_strings["has_update"])
|
||||
elif self.rgame.needs_verification:
|
||||
self.ui.status_label.setText(self.state_strings["needs_verification"])
|
||||
elif not self.rgame.can_launch and self.rgame.is_installed:
|
||||
self.ui.status_label.setText(self.state_strings["not_can_launch"])
|
||||
else:
|
||||
self.ui.status_label.setText(self.state_strings[self.rgame.state])
|
||||
else:
|
||||
self.ui.status_label.setText(self.state_strings[self.rgame.state])
|
||||
self.ui.status_label.setVisible(bool(self.ui.status_label.text()))
|
||||
|
||||
@abstractmethod
|
||||
def update_widget(self, install_btn: QPushButton, launch_btn: QPushButton):
|
||||
install_btn.setVisible(not self.rgame.is_installed)
|
||||
install_btn.setEnabled(not self.rgame.is_installed)
|
||||
launch_btn.setVisible(self.rgame.is_installed)
|
||||
launch_btn.setEnabled(self.rgame.can_launch)
|
||||
@pyqtSlot()
|
||||
def update_buttons(self):
|
||||
self.ui.install_btn.setVisible(not self.rgame.is_installed)
|
||||
self.ui.install_btn.setEnabled(not self.rgame.is_installed)
|
||||
self.ui.launch_btn.setVisible(self.rgame.is_installed)
|
||||
self.ui.launch_btn.setEnabled(self.rgame.can_launch)
|
||||
|
||||
@property
|
||||
def enterEventText(self) -> str:
|
||||
if self.rgame.is_installed:
|
||||
if self.rgame.state == RareGame.State.RUNNING:
|
||||
return self.texts["status"]["running"]
|
||||
elif (not self.rgame.is_origin) and self.rgame.needs_verification:
|
||||
return self.texts["status"]["needs_verification"]
|
||||
elif self.rgame.is_foreign:
|
||||
return self.texts["hover"]["launch_offline"]
|
||||
@pyqtSlot()
|
||||
def update_actions(self):
|
||||
for action in self.actions():
|
||||
self.removeAction(action)
|
||||
|
||||
if self.rgame.is_installed or self.rgame.is_origin:
|
||||
self.addAction(self.launch_action)
|
||||
else:
|
||||
self.addAction(self.install_action)
|
||||
|
||||
# if self.rgame.game.supports_cloud_saves:
|
||||
# self.addAction(self.sync_action)
|
||||
|
||||
if desktop_links_supported() and self.rgame.is_installed:
|
||||
if desktop_link_path(self.rgame.folder_name, "desktop").exists():
|
||||
self.desktop_link_action.setText(self.tr("Remove Desktop link"))
|
||||
else:
|
||||
self.desktop_link_action.setText(self.tr("Create Desktop link"))
|
||||
self.addAction(self.desktop_link_action)
|
||||
if desktop_link_path(self.rgame.folder_name, "start_menu").exists():
|
||||
self.menu_link_action.setText(self.tr("Remove Menu link"))
|
||||
else:
|
||||
self.menu_link_action.setText(self.tr("Create Menu link"))
|
||||
self.addAction(self.menu_link_action)
|
||||
|
||||
self.addAction(self.reload_action)
|
||||
if self.rgame.is_installed and not self.rgame.is_origin:
|
||||
self.addAction(self.uninstall_action)
|
||||
|
||||
def eventFilter(self, a0: QObject, a1: QEvent) -> bool:
|
||||
if a0 is self.ui.launch_btn:
|
||||
if a1.type() == QEvent.Enter:
|
||||
if not self.rgame.can_launch:
|
||||
self.ui.tooltip_label.setText(self.hover_strings["not_can_launch"])
|
||||
elif self.rgame.is_origin:
|
||||
self.ui.tooltip_label.setText(self.hover_strings["is_origin"])
|
||||
elif self.rgame.has_update:
|
||||
return self.texts["hover"]["update_available"]
|
||||
else:
|
||||
return self.tr("Game Info")
|
||||
# return self.texts["hover"]["launch" if self.igame else "launch_origin"]
|
||||
else:
|
||||
if not self.rgame.state == RareGame.State.DOWNLOADING:
|
||||
return self.tr("Game Info")
|
||||
else:
|
||||
return self.tr("Installation running")
|
||||
|
||||
@property
|
||||
def leaveEventText(self) -> str:
|
||||
if self.rgame.is_installed:
|
||||
if self.rgame.state == RareGame.State.RUNNING:
|
||||
return self.texts["status"]["running"]
|
||||
# elif self.syncing_cloud_saves:
|
||||
# return self.texts["status"]["syncing"]
|
||||
elif self.rgame.is_foreign:
|
||||
return self.texts["status"]["no_meta"]
|
||||
elif self.rgame.has_update:
|
||||
return self.texts["status"]["update_available"]
|
||||
elif (not self.rgame.is_origin) and self.rgame.needs_verification:
|
||||
return self.texts["status"]["needs_verification"]
|
||||
else:
|
||||
return ""
|
||||
else:
|
||||
if self.rgame.state == RareGame.State.DOWNLOADING:
|
||||
return "Installation..."
|
||||
else:
|
||||
return ""
|
||||
self.ui.tooltip_label.setText(self.hover_strings["has_update"])
|
||||
elif self.rgame.is_foreign and self.rgame.can_run_offline:
|
||||
self.ui.tooltip_label.setText(self.hover_strings["is_foreign"])
|
||||
elif self.rgame.can_launch:
|
||||
self.ui.tooltip_label.setText(self.hover_strings["can_launch"])
|
||||
return True
|
||||
if a1.type() == QEvent.Leave:
|
||||
self.ui.tooltip_label.setText(self.hover_strings["info"])
|
||||
# return True
|
||||
if a0 is self.ui.install_btn:
|
||||
if a1.type() == QEvent.Enter:
|
||||
self.ui.tooltip_label.setText(self.hover_strings["install"])
|
||||
return True
|
||||
if a1.type() == QEvent.Leave:
|
||||
self.ui.tooltip_label.setText(self.hover_strings["info"])
|
||||
# return True
|
||||
if a0 is self:
|
||||
if a1.type() == QEvent.Enter:
|
||||
self.ui.tooltip_label.setText(self.hover_strings["info"])
|
||||
return super().eventFilter(a0, a1)
|
||||
|
||||
def mousePressEvent(self, e: QMouseEvent) -> None:
|
||||
# left button
|
||||
|
@ -220,46 +227,42 @@ class GameWidget(LibraryWidget):
|
|||
def _uninstall(self):
|
||||
self.show_info.emit(self.rgame)
|
||||
|
||||
def create_desktop_link(self, type_of_link):
|
||||
if type_of_link == "desktop":
|
||||
shortcut_path = QStandardPaths.writableLocation(QStandardPaths.DesktopLocation)
|
||||
elif type_of_link == "start_menu":
|
||||
shortcut_path = QStandardPaths.writableLocation(QStandardPaths.ApplicationsLocation)
|
||||
else:
|
||||
return
|
||||
|
||||
if platform.system() == "Windows":
|
||||
shortcut_path = os.path.join(shortcut_path, f"{self.rgame.app_title}.lnk")
|
||||
elif platform.system() == "Linux":
|
||||
shortcut_path = os.path.join(shortcut_path, f"{self.rgame.app_title}.desktop")
|
||||
else:
|
||||
def _create_link(self, name, link_type):
|
||||
if not desktop_links_supported():
|
||||
QMessageBox.warning(
|
||||
self,
|
||||
"Warning",
|
||||
f"Create a Desktop link is currently not supported on {platform.system()}",
|
||||
self.tr("Warning"),
|
||||
self.tr("Creating shortcuts is currently unsupported on {}").format(platform.system()),
|
||||
)
|
||||
return
|
||||
|
||||
if not os.path.exists(shortcut_path):
|
||||
shortcut_path = desktop_link_path(name, link_type)
|
||||
|
||||
if not shortcut_path.exists():
|
||||
try:
|
||||
if not create_desktop_link(self.rgame.app_name, self.core, type_of_link):
|
||||
return
|
||||
if not create_desktop_link(
|
||||
app_name=self.rgame.app_name,
|
||||
app_title=self.rgame.app_title,
|
||||
link_name=self.rgame.folder_name,
|
||||
link_type=link_type,
|
||||
):
|
||||
raise PermissionError
|
||||
except PermissionError:
|
||||
QMessageBox.warning(
|
||||
self, "Error", "Permission error. Cannot create Desktop Link"
|
||||
)
|
||||
if type_of_link == "desktop":
|
||||
self.create_desktop.setText(self.tr("Remove Desktop link"))
|
||||
elif type_of_link == "start_menu":
|
||||
self.create_start_menu.setText(self.tr("Remove Start menu link"))
|
||||
else:
|
||||
if os.path.exists(shortcut_path):
|
||||
os.remove(shortcut_path)
|
||||
QMessageBox.warning(self, "Error", "Could not create shortcut.")
|
||||
return
|
||||
|
||||
if type_of_link == "desktop":
|
||||
self.create_desktop.setText(self.tr("Create Desktop link"))
|
||||
elif type_of_link == "start_menu":
|
||||
self.create_start_menu.setText(self.tr("Create Start menu link"))
|
||||
if link_type == "desktop":
|
||||
self.desktop_link_action.setText(self.tr("Remove Desktop link"))
|
||||
elif link_type == "start_menu":
|
||||
self.menu_link_action.setText(self.tr("Remove Start Menu link"))
|
||||
else:
|
||||
if shortcut_path.exists():
|
||||
shortcut_path.unlink(missing_ok=True)
|
||||
|
||||
if link_type == "desktop":
|
||||
self.desktop_link_action.setText(self.tr("Create Desktop link"))
|
||||
elif link_type == "start_menu":
|
||||
self.menu_link_action.setText(self.tr("Create Start Menu link"))
|
||||
|
||||
# def sync_finished(self, app_name):
|
||||
# self.syncing_cloud_saves = False
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from logging import getLogger
|
||||
from typing import Optional
|
||||
|
||||
from PyQt5.QtCore import QEvent, pyqtSlot
|
||||
from PyQt5.QtCore import QEvent
|
||||
|
||||
from rare.models.game import RareGame
|
||||
from rare.shared.image_manager import ImageSize
|
||||
|
@ -13,13 +13,13 @@ logger = getLogger("IconGameWidget")
|
|||
|
||||
class IconGameWidget(GameWidget):
|
||||
def __init__(self, rgame: RareGame, parent=None):
|
||||
super(IconGameWidget, self).__init__(rgame, parent)
|
||||
super().__init__(rgame, parent)
|
||||
self.setObjectName(f"{rgame.app_name}")
|
||||
self.setFixedSize(ImageSize.Display)
|
||||
self.ui = IconWidget()
|
||||
self.ui.setupUi(self)
|
||||
|
||||
self.ui.title_label.setText(f"<h4>{self.rgame.app_title}</h4>")
|
||||
self.ui.title_label.setText(self.rgame.app_title)
|
||||
self.ui.launch_btn.clicked.connect(self._launch)
|
||||
self.ui.launch_btn.setVisible(self.rgame.is_installed)
|
||||
self.ui.install_btn.clicked.connect(self._install)
|
||||
|
@ -27,27 +27,22 @@ class IconGameWidget(GameWidget):
|
|||
|
||||
self.ui.launch_btn.setEnabled(self.rgame.can_launch)
|
||||
|
||||
self.set_status()
|
||||
self.update_state()
|
||||
|
||||
@pyqtSlot()
|
||||
def set_status(self):
|
||||
super(IconGameWidget, self).set_status(self.ui.status_label)
|
||||
|
||||
@pyqtSlot()
|
||||
def update_widget(self):
|
||||
super(IconGameWidget, self).update_widget(self.ui.install_btn, self.ui.launch_btn)
|
||||
# lk: "connect" the buttons' enter/leave events to this widget
|
||||
self.installEventFilter(self)
|
||||
self.ui.launch_btn.installEventFilter(self)
|
||||
self.ui.install_btn.installEventFilter(self)
|
||||
|
||||
def enterEvent(self, a0: Optional[QEvent] = None) -> None:
|
||||
if a0 is not None:
|
||||
a0.accept()
|
||||
self.ui.tooltip_label.setText(self.enterEventText)
|
||||
self.ui.enterAnimation(self)
|
||||
|
||||
def leaveEvent(self, a0: Optional[QEvent] = None) -> None:
|
||||
if a0 is not None:
|
||||
a0.accept()
|
||||
self.ui.leaveAnimation(self)
|
||||
self.ui.tooltip_label.setText(self.leaveEventText)
|
||||
|
||||
# def sync_finished(self, app_name):
|
||||
# if not app_name == self.rgame.app_name:
|
||||
|
|
|
@ -17,15 +17,15 @@ from rare.widgets.elide_label import ElideLabel
|
|||
class IconWidget(object):
|
||||
def __init__(self):
|
||||
self._effect = None
|
||||
self._animation = None
|
||||
self._animation: QPropertyAnimation = None
|
||||
|
||||
self.status_label = None
|
||||
self.mini_widget = None
|
||||
self.mini_effect = None
|
||||
self.title_label = None
|
||||
self.tooltip_label = None
|
||||
self.launch_btn = None
|
||||
self.install_btn = None
|
||||
self.status_label: ElideLabel = None
|
||||
self.mini_widget: QWidget = None
|
||||
self.mini_effect: QGraphicsOpacityEffect = None
|
||||
self.title_label: QLabel = None
|
||||
self.tooltip_label: ElideLabel = None
|
||||
self.launch_btn: QPushButton = None
|
||||
self.install_btn: QPushButton = None
|
||||
|
||||
def setupUi(self, widget: QWidget):
|
||||
# information at top
|
||||
|
@ -34,6 +34,7 @@ class IconWidget(object):
|
|||
self.status_label.setStyleSheet(
|
||||
f"QLabel#{self.status_label.objectName()}"
|
||||
"{"
|
||||
"font-weight: bold;"
|
||||
"color: white;"
|
||||
"background-color: rgba(0, 0, 0, 65%);"
|
||||
"border-radius: 5%;"
|
||||
|
@ -68,11 +69,12 @@ class IconWidget(object):
|
|||
self.title_label.setStyleSheet(
|
||||
f"QLabel#{self.title_label.objectName()}"
|
||||
"{"
|
||||
"font-weight: bold;"
|
||||
"background-color: rgba(0, 0, 0, 0%); color: white;"
|
||||
"}"
|
||||
)
|
||||
self.title_label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
||||
self.title_label.setAlignment(Qt.AlignTop)
|
||||
self.title_label.setAlignment(Qt.AlignVCenter)
|
||||
self.title_label.setAutoFillBackground(False)
|
||||
self.title_label.setWordWrap(True)
|
||||
|
||||
|
@ -125,12 +127,6 @@ class IconWidget(object):
|
|||
self.install_btn.setFixedSize(QSize(widget.width() // 4, widget.width() // 4))
|
||||
|
||||
# Create layouts
|
||||
# layout for the whole widget, holds the image
|
||||
layout = QVBoxLayout()
|
||||
layout.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.setSizeConstraint(QVBoxLayout.SetFixedSize)
|
||||
|
||||
# layout for the image, holds the mini widget and a spacer item
|
||||
image_layout = QVBoxLayout()
|
||||
image_layout.setContentsMargins(2, 2, 2, 2)
|
||||
|
|
|
@ -37,6 +37,7 @@ class ProgressLabel(QLabel):
|
|||
|
||||
def setStyleSheetColors(self, bg: QColor, fg: QColor, brd: QColor):
|
||||
sheet = (
|
||||
f"QLabel#{type(self).__name__} {{"
|
||||
f"background-color: rgba({bg.red()}, {bg.green()}, {bg.blue()}, 65%);"
|
||||
f"color: rgb({fg.red()}, {fg.green()}, {fg.blue()});"
|
||||
f"border-width: 1px;"
|
||||
|
@ -44,6 +45,7 @@ class ProgressLabel(QLabel):
|
|||
f"border-color: rgb({brd.red()}, {brd.green()}, {brd.blue()});"
|
||||
f"font-weight: bold;"
|
||||
f"font-size: 16pt;"
|
||||
f"}}"
|
||||
)
|
||||
self.setStyleSheet(sheet)
|
||||
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
from logging import getLogger
|
||||
|
||||
from PyQt5.QtCore import Qt, QEvent, QRect, pyqtSlot, pyqtSignal
|
||||
from PyQt5.QtCore import Qt, QEvent, QRect
|
||||
from PyQt5.QtGui import (
|
||||
QPalette,
|
||||
QBrush,
|
||||
QPaintEvent,
|
||||
QPainter,
|
||||
QLinearGradient,
|
||||
QPixmap, QImage, QResizeEvent,
|
||||
QPixmap,
|
||||
QImage,
|
||||
QResizeEvent,
|
||||
)
|
||||
|
||||
from rare.models.game import RareGame
|
||||
|
@ -20,72 +22,45 @@ logger = getLogger("ListGameWidget")
|
|||
|
||||
|
||||
class ListGameWidget(GameWidget):
|
||||
|
||||
def __init__(self, rgame: RareGame, parent=None):
|
||||
super(ListGameWidget, self).__init__(rgame, parent)
|
||||
super().__init__(rgame, parent)
|
||||
self.setObjectName(f"{rgame.app_name}")
|
||||
self.ui = ListWidget()
|
||||
self.ui.setupUi(self)
|
||||
|
||||
self.ui.title_label.setText(f"<h3>{self.rgame.app_title}</h3>")
|
||||
|
||||
self.ui.install_btn.setVisible(not self.rgame.is_installed)
|
||||
self.ui.title_label.setText(self.rgame.app_title)
|
||||
self.ui.launch_btn.clicked.connect(self._launch)
|
||||
self.ui.launch_btn.setVisible(self.rgame.is_installed)
|
||||
self.ui.install_btn.clicked.connect(self._install)
|
||||
self.ui.install_btn.setVisible(not self.rgame.is_installed)
|
||||
|
||||
self.ui.launch_btn.setEnabled(self.rgame.can_launch)
|
||||
|
||||
self.ui.launch_btn.setText(
|
||||
self.tr("Launch") if not self.rgame.is_origin else self.tr("Link/Play")
|
||||
)
|
||||
self.ui.launch_btn.clicked.connect(self._launch)
|
||||
self.ui.launch_btn.setVisible(self.rgame.is_installed)
|
||||
|
||||
self.ui.developer_text.setText(self.rgame.developer)
|
||||
# self.version_label.setVisible(self.is_installed)
|
||||
if self.rgame.igame:
|
||||
self.ui.version_text.setText(self.rgame.version)
|
||||
|
||||
self.ui.size_text.setText(get_size(self.rgame.install_size) if self.rgame.install_size else "")
|
||||
|
||||
self.ui.launch_btn.setEnabled(self.rgame.can_launch)
|
||||
self.update_state()
|
||||
|
||||
self.set_status()
|
||||
|
||||
@pyqtSlot()
|
||||
def set_status(self):
|
||||
super(ListGameWidget, self).set_status(self.ui.status_label)
|
||||
|
||||
@pyqtSlot()
|
||||
def update_widget(self):
|
||||
super(ListGameWidget, self).update_widget(self.ui.install_btn, self.ui.launch_btn)
|
||||
|
||||
def update_text(self, e=None):
|
||||
if self.rgame.is_installed:
|
||||
if self.rgame.has_update:
|
||||
self.ui.status_label.setText(self.texts["status"]["update_available"])
|
||||
elif self.rgame.is_foreign:
|
||||
self.ui.status_label.setText(self.texts["status"]["no_meta"])
|
||||
elif self.rgame.igame and self.rgame.needs_verification:
|
||||
self.ui.status_label.setText(self.texts["status"]["needs_verification"])
|
||||
# elif self.syncing_cloud_saves:
|
||||
# self.ui.status_label.setText(self.texts["status"]["syncing"])
|
||||
else:
|
||||
self.ui.status_label.setText("")
|
||||
self.ui.status_label.setVisible(False)
|
||||
else:
|
||||
self.ui.status_label.setVisible(False)
|
||||
# lk: "connect" the buttons' enter/leave events to this widget
|
||||
self.installEventFilter(self)
|
||||
self.ui.launch_btn.installEventFilter(self)
|
||||
self.ui.install_btn.installEventFilter(self)
|
||||
|
||||
def enterEvent(self, a0: QEvent = None) -> None:
|
||||
if a0 is not None:
|
||||
a0.accept()
|
||||
status = self.enterEventText
|
||||
self.ui.tooltip_label.setText(status)
|
||||
self.ui.tooltip_label.setVisible(bool(status))
|
||||
self.ui.tooltip_label.setVisible(True)
|
||||
|
||||
def leaveEvent(self, a0: QEvent = None) -> None:
|
||||
if a0 is not None:
|
||||
a0.accept()
|
||||
status = self.leaveEventText
|
||||
self.ui.tooltip_label.setText(status)
|
||||
self.ui.tooltip_label.setVisible(bool(status))
|
||||
self.ui.tooltip_label.setVisible(False)
|
||||
|
||||
# def game_started(self, app_name):
|
||||
# if app_name == self.rgame.app_name:
|
||||
|
|
|
@ -29,10 +29,14 @@ class ListWidget(object):
|
|||
def setupUi(self, widget: QWidget):
|
||||
self.title_label = QLabel(parent=widget)
|
||||
self.title_label.setWordWrap(False)
|
||||
self.title_label.setStyleSheet(
|
||||
"font-weight: bold;"
|
||||
)
|
||||
|
||||
self.status_label = QLabel(parent=widget)
|
||||
self.status_label.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
|
||||
self.status_label.setStyleSheet(
|
||||
"font-weight: bold;"
|
||||
"background-color: rgba(0,0,0,75%);"
|
||||
"border: 1px solid black;"
|
||||
"border-radius: 5px;"
|
||||
|
|
|
@ -4,13 +4,12 @@ import subprocess
|
|||
import sys
|
||||
from logging import getLogger
|
||||
|
||||
from PyQt5.QtCore import QSettings, QStandardPaths, Qt
|
||||
from PyQt5.QtCore import QSettings, Qt
|
||||
from PyQt5.QtWidgets import QWidget, QMessageBox
|
||||
|
||||
from rare.shared import LegendaryCoreSingleton
|
||||
from rare.components.tabs.settings.widgets.rpc import RPCSettings
|
||||
from rare.shared import LegendaryCoreSingleton
|
||||
from rare.ui.components.tabs.settings.rare import Ui_RareSettings
|
||||
from rare.utils.paths import log_dir
|
||||
from rare.utils.misc import (
|
||||
get_translations,
|
||||
get_color_schemes,
|
||||
|
@ -18,8 +17,8 @@ from rare.utils.misc import (
|
|||
get_style_sheets,
|
||||
set_style_sheet,
|
||||
get_size,
|
||||
create_desktop_link,
|
||||
)
|
||||
from rare.utils.paths import create_desktop_link, desktop_link_path, log_dir, desktop_links_supported
|
||||
|
||||
logger = getLogger("RareSettings")
|
||||
|
||||
|
@ -106,36 +105,28 @@ class RareSettings(QWidget, Ui_RareSettings):
|
|||
)
|
||||
)
|
||||
self.notification.stateChanged.connect(
|
||||
lambda: self.settings.setValue(
|
||||
"notification", self.notification.isChecked()
|
||||
)
|
||||
lambda: self.settings.setValue("notification", self.notification.isChecked())
|
||||
)
|
||||
self.save_size.stateChanged.connect(self.save_window_size)
|
||||
self.log_games.stateChanged.connect(
|
||||
lambda: self.settings.setValue("show_console", self.log_games.isChecked())
|
||||
)
|
||||
|
||||
desktop = QStandardPaths.writableLocation(QStandardPaths.DesktopLocation)
|
||||
applications = QStandardPaths.writableLocation(QStandardPaths.ApplicationsLocation)
|
||||
if platform.system() == "Linux":
|
||||
self.desktop_file = os.path.join(desktop, "Rare.desktop")
|
||||
self.start_menu_link = os.path.join(applications, "Rare.desktop")
|
||||
elif platform.system() == "Windows":
|
||||
self.desktop_file = os.path.join(desktop, "Rare.lnk")
|
||||
self.start_menu_link = os.path.join(applications, "..", "Rare.lnk")
|
||||
if desktop_links_supported():
|
||||
self.desktop_link = desktop_link_path("Rare", "desktop")
|
||||
self.start_menu_link = desktop_link_path("Rare", "start_menu")
|
||||
else:
|
||||
self.desktop_link_btn.setText(self.tr("Not supported"))
|
||||
self.desktop_link_btn.setToolTip(self.tr("Not supported"))
|
||||
self.desktop_link_btn.setDisabled(True)
|
||||
self.startmenu_link_btn.setText(self.tr("Not supported"))
|
||||
self.startmenu_link_btn.setToolTip(self.tr("Not supported"))
|
||||
self.startmenu_link_btn.setDisabled(True)
|
||||
|
||||
self.desktop_file = ""
|
||||
self.desktop_link = ""
|
||||
self.start_menu_link = ""
|
||||
|
||||
if self.desktop_file and os.path.exists(self.desktop_file):
|
||||
if self.desktop_link and self.desktop_link.exists():
|
||||
self.desktop_link_btn.setText(self.tr("Remove desktop link"))
|
||||
|
||||
if self.start_menu_link and os.path.exists(self.start_menu_link):
|
||||
if self.start_menu_link and self.start_menu_link.exists():
|
||||
self.startmenu_link_btn.setText(self.tr("Remove start menu link"))
|
||||
|
||||
self.desktop_link_btn.clicked.connect(self.create_desktop_link)
|
||||
|
@ -171,7 +162,8 @@ class RareSettings(QWidget, Ui_RareSettings):
|
|||
def create_start_menu_link(self):
|
||||
try:
|
||||
if not os.path.exists(self.start_menu_link):
|
||||
create_desktop_link(type_of_link="start_menu", for_rare=True)
|
||||
if not create_desktop_link(app_name="rare_shortcut", link_type="start_menu"):
|
||||
return
|
||||
self.startmenu_link_btn.setText(self.tr("Remove start menu link"))
|
||||
else:
|
||||
os.remove(self.start_menu_link)
|
||||
|
@ -186,11 +178,12 @@ class RareSettings(QWidget, Ui_RareSettings):
|
|||
|
||||
def create_desktop_link(self):
|
||||
try:
|
||||
if not os.path.exists(self.desktop_file):
|
||||
create_desktop_link(type_of_link="desktop", for_rare=True)
|
||||
if not os.path.exists(self.desktop_link):
|
||||
if not create_desktop_link(app_name="rare_shortcut", link_type="desktop"):
|
||||
return
|
||||
self.desktop_link_btn.setText(self.tr("Remove Desktop link"))
|
||||
else:
|
||||
os.remove(self.desktop_file)
|
||||
os.remove(self.desktop_link)
|
||||
self.desktop_link_btn.setText(self.tr("Create desktop link"))
|
||||
except PermissionError as e:
|
||||
logger.error(str(e))
|
||||
|
|
|
@ -14,8 +14,7 @@ from rare.lgndr.core import LegendaryCore
|
|||
from rare.models.install import InstallOptionsModel, UninstallOptionsModel
|
||||
from rare.shared.game_process import GameProcess
|
||||
from rare.shared.image_manager import ImageManager
|
||||
from rare.utils.misc import get_rare_executable
|
||||
from rare.utils.paths import data_dir
|
||||
from rare.utils.paths import data_dir, get_rare_executable
|
||||
|
||||
logger = getLogger("RareGame")
|
||||
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
import os
|
||||
import platform
|
||||
import shlex
|
||||
import sys
|
||||
from logging import getLogger
|
||||
from typing import List, Union, Type
|
||||
|
||||
|
@ -12,23 +10,19 @@ from PyQt5.QtCore import (
|
|||
QObject,
|
||||
QRunnable,
|
||||
QSettings,
|
||||
QStandardPaths,
|
||||
QFile,
|
||||
QDir, Qt,
|
||||
QDir,
|
||||
Qt,
|
||||
)
|
||||
from PyQt5.QtGui import QPalette, QColor, QImage, QFontMetrics
|
||||
from PyQt5.QtWidgets import qApp, QStyleFactory, QWidget, QLabel
|
||||
from PyQt5.QtGui import QPalette, QColor, QFontMetrics
|
||||
from PyQt5.QtWidgets import qApp, QStyleFactory, QLabel
|
||||
from PyQt5.sip import wrappertype
|
||||
from legendary.core import LegendaryCore
|
||||
from legendary.models.game import Game
|
||||
from requests.exceptions import HTTPError
|
||||
|
||||
from rare.models.apiresults import ApiResults
|
||||
from rare.utils.paths import image_dir, resources_path
|
||||
|
||||
if platform.system() == "Windows":
|
||||
# noinspection PyUnresolvedReferences
|
||||
from win32com.client import Dispatch # pylint: disable=E0401
|
||||
from rare.utils.paths import resources_path
|
||||
|
||||
logger = getLogger("Utils")
|
||||
settings = QSettings("Rare", "Rare")
|
||||
|
@ -156,152 +150,6 @@ def get_size(b: Union[int, float]) -> str:
|
|||
b /= 1024
|
||||
|
||||
|
||||
def get_rare_executable() -> List[str]:
|
||||
# lk: detect if nuitka
|
||||
if "__compiled__" in globals():
|
||||
executable = [sys.executable]
|
||||
elif platform.system() == "Linux" or platform.system() == "Darwin":
|
||||
if p := os.environ.get("APPIMAGE"):
|
||||
executable = [p]
|
||||
else:
|
||||
if sys.executable == os.path.abspath(sys.argv[0]):
|
||||
executable = [sys.executable]
|
||||
else:
|
||||
executable = [sys.executable, os.path.abspath(sys.argv[0])]
|
||||
elif platform.system() == "Windows":
|
||||
executable = [sys.executable]
|
||||
|
||||
if sys.executable != os.path.abspath(sys.argv[0]):
|
||||
executable.append(os.path.abspath(sys.argv[0]))
|
||||
|
||||
if executable[0].endswith("python.exe"):
|
||||
# be sure to start consoleless then
|
||||
executable[0] = executable[0].replace("python.exe", "pythonw.exe")
|
||||
if executable[1].endswith("rare"):
|
||||
executable[1] = executable[1] + ".exe"
|
||||
else:
|
||||
executable = [sys.executable]
|
||||
|
||||
executable[0] = os.path.abspath(executable[0])
|
||||
return executable
|
||||
|
||||
|
||||
def create_desktop_link(app_name=None, core: LegendaryCore = None, type_of_link="desktop",
|
||||
for_rare: bool = False) -> bool:
|
||||
if not for_rare:
|
||||
igame = core.get_installed_game(app_name)
|
||||
|
||||
icon = os.path.join(os.path.join(image_dir(), igame.app_name, "installed.png"))
|
||||
icon = icon.replace(".png", "")
|
||||
|
||||
if platform.system() == "Linux":
|
||||
if type_of_link == "desktop":
|
||||
path = QStandardPaths.writableLocation(QStandardPaths.DesktopLocation)
|
||||
elif type_of_link == "start_menu":
|
||||
path = QStandardPaths.writableLocation(QStandardPaths.ApplicationsLocation)
|
||||
else:
|
||||
return False
|
||||
if not os.path.exists(path):
|
||||
return False
|
||||
executable = get_rare_executable()
|
||||
executable = shlex.join(executable)
|
||||
|
||||
if for_rare:
|
||||
with open(os.path.join(path, "Rare.desktop"), "w") as desktop_file:
|
||||
desktop_file.write(
|
||||
"[Desktop Entry]\n"
|
||||
f"Name=Rare\n"
|
||||
f"Type=Application\n"
|
||||
f"Categories=Game;\n"
|
||||
f"Icon={os.path.join(resources_path, 'images', 'Rare.png')}\n"
|
||||
f"Exec={executable}\n"
|
||||
"Terminal=false\n"
|
||||
"StartupWMClass=Rare\n"
|
||||
)
|
||||
else:
|
||||
with open(os.path.join(path, f"{igame.title}.desktop"), "w") as desktop_file:
|
||||
desktop_file.write(
|
||||
"[Desktop Entry]\n"
|
||||
f"Name={igame.title}\n"
|
||||
f"Type=Application\n"
|
||||
f"Categories=Game;\n"
|
||||
f"Icon={icon}.png\n"
|
||||
f"Exec={executable} launch {app_name}\n"
|
||||
"Terminal=false\n"
|
||||
"StartupWMClass=Rare\n"
|
||||
)
|
||||
os.chmod(os.path.join(path, f"{igame.title}.desktop"), 0o755)
|
||||
|
||||
return True
|
||||
|
||||
elif platform.system() == "Windows":
|
||||
# Target of shortcut
|
||||
if type_of_link == "desktop":
|
||||
target_folder = QStandardPaths.writableLocation(QStandardPaths.DesktopLocation)
|
||||
elif type_of_link == "start_menu":
|
||||
target_folder = os.path.join(
|
||||
QStandardPaths.writableLocation(QStandardPaths.ApplicationsLocation),
|
||||
".."
|
||||
)
|
||||
else:
|
||||
logger.warning("No valid type of link")
|
||||
return False
|
||||
if not os.path.exists(target_folder):
|
||||
return False
|
||||
|
||||
if for_rare:
|
||||
linkName = "Rare.lnk"
|
||||
else:
|
||||
linkName = igame.title
|
||||
# TODO: this conversion is not applied everywhere (see base_installed_widget), should it?
|
||||
for c in r'<>?":|\/*':
|
||||
linkName = linkName.replace(c, "")
|
||||
|
||||
linkName = f"{linkName.strip()}.lnk"
|
||||
|
||||
# Path to location of link file
|
||||
pathLink = os.path.join(target_folder, linkName)
|
||||
|
||||
# Add shortcut
|
||||
shell = Dispatch("WScript.Shell")
|
||||
shortcut = shell.CreateShortCut(pathLink)
|
||||
|
||||
executable = get_rare_executable()
|
||||
arguments = []
|
||||
|
||||
if len(executable) > 1:
|
||||
arguments.extend(executable[1:])
|
||||
executable = executable[0]
|
||||
|
||||
if not for_rare:
|
||||
arguments.extend(["launch", app_name])
|
||||
|
||||
shortcut.Targetpath = executable
|
||||
# Maybe there is a better solution, but windows does not accept single quotes (Windows is weird)
|
||||
shortcut.Arguments = shlex.join(arguments).replace("'", '"')
|
||||
if for_rare:
|
||||
shortcut.WorkingDirectory = QStandardPaths.writableLocation(QStandardPaths.HomeLocation)
|
||||
|
||||
# Icon
|
||||
if for_rare:
|
||||
icon_location = os.path.join(resources_path, "images", "Rare.ico")
|
||||
else:
|
||||
if not os.path.exists(f"{icon}.ico"):
|
||||
img = QImage()
|
||||
img.load(f"{icon}.png")
|
||||
img.save(f"{icon}.ico")
|
||||
logger.info("Created ico file")
|
||||
icon_location = f"{icon}.ico"
|
||||
shortcut.IconLocation = os.path.abspath(icon_location)
|
||||
|
||||
shortcut.save()
|
||||
return True
|
||||
|
||||
# mac OS is based on Darwin
|
||||
elif platform.system() == "Darwin":
|
||||
return False
|
||||
|
||||
|
||||
class CloudWorker(QRunnable):
|
||||
class Signals(QObject):
|
||||
# List[SaveGameFile]
|
||||
|
|
|
@ -1,8 +1,20 @@
|
|||
import os
|
||||
import platform
|
||||
import shlex
|
||||
import shutil
|
||||
import sys
|
||||
from logging import getLogger
|
||||
from pathlib import Path
|
||||
from typing import List
|
||||
|
||||
from PyQt5.QtCore import QStandardPaths
|
||||
from PyQt5.QtGui import QImage
|
||||
|
||||
if platform.system() == "Windows":
|
||||
# noinspection PyUnresolvedReferences
|
||||
from win32com.client import Dispatch # pylint: disable=E0401
|
||||
|
||||
logger = getLogger("Paths")
|
||||
|
||||
resources_path = Path(__file__).absolute().parent.parent.joinpath("resources")
|
||||
|
||||
|
@ -15,7 +27,7 @@ for old_dir in [
|
|||
]:
|
||||
if old_dir.exists():
|
||||
# lk: case-sensitive matching on Winblows
|
||||
if old_dir.stem in os.listdir(old_dir.parent):
|
||||
if old_dir.stem in old_dir.parent.iterdir():
|
||||
shutil.rmtree(old_dir, ignore_errors=True)
|
||||
|
||||
|
||||
|
@ -49,3 +61,180 @@ def create_dirs() -> None:
|
|||
for path in (data_dir(), cache_dir(), image_dir(), log_dir(), tmp_dir()):
|
||||
if not path.exists():
|
||||
path.mkdir(parents=True)
|
||||
|
||||
|
||||
def home_dir() -> Path:
|
||||
return Path(QStandardPaths.writableLocation(QStandardPaths.HomeLocation))
|
||||
|
||||
|
||||
def desktop_dir() -> Path:
|
||||
return Path(QStandardPaths.writableLocation(QStandardPaths.DesktopLocation))
|
||||
|
||||
|
||||
def applications_dir() -> Path:
|
||||
return Path(QStandardPaths.writableLocation(QStandardPaths.ApplicationsLocation))
|
||||
|
||||
|
||||
__link_suffix = {
|
||||
"Windows": ".lnk",
|
||||
"Linux": ".desktop",
|
||||
}
|
||||
|
||||
|
||||
def desktop_links_supported() -> bool:
|
||||
return platform.system() in __link_suffix.keys()
|
||||
|
||||
|
||||
__link_type = {
|
||||
"desktop": desktop_dir(),
|
||||
# lk: for some undocumented reason, on Windows we used the parent directory
|
||||
# lk: for start menu items. Mirror it here for backwards compatibility
|
||||
"start_menu": applications_dir().parent if platform.system() == "Windows" else applications_dir(),
|
||||
}
|
||||
|
||||
|
||||
def desktop_link_path(link_name: str, link_type: str) -> Path:
|
||||
"""
|
||||
Creates the path of a shortcut link
|
||||
|
||||
:param link_name:
|
||||
Name of the shortcut file
|
||||
:param link_type:
|
||||
"desktop" or "start_menu"
|
||||
:return Path:
|
||||
shortcut path
|
||||
"""
|
||||
return __link_type[link_type].joinpath(f"{link_name}{__link_suffix[platform.system()]}")
|
||||
|
||||
|
||||
def get_rare_executable() -> List[str]:
|
||||
# lk: detect if nuitka
|
||||
if "__compiled__" in globals():
|
||||
executable = [sys.executable]
|
||||
elif platform.system() == "Linux" or platform.system() == "Darwin":
|
||||
if p := os.environ.get("APPIMAGE"):
|
||||
executable = [p]
|
||||
else:
|
||||
if sys.executable == os.path.abspath(sys.argv[0]):
|
||||
executable = [sys.executable]
|
||||
else:
|
||||
executable = [sys.executable, os.path.abspath(sys.argv[0])]
|
||||
elif platform.system() == "Windows":
|
||||
executable = [sys.executable]
|
||||
|
||||
if sys.executable != os.path.abspath(sys.argv[0]):
|
||||
executable.append(os.path.abspath(sys.argv[0]))
|
||||
|
||||
if executable[0].endswith("python.exe"):
|
||||
# be sure to start consoleless then
|
||||
executable[0] = executable[0].replace("python.exe", "pythonw.exe")
|
||||
if executable[1].endswith("rare"):
|
||||
executable[1] = executable[1] + ".exe"
|
||||
else:
|
||||
executable = [sys.executable]
|
||||
|
||||
executable[0] = os.path.abspath(executable[0])
|
||||
return executable
|
||||
|
||||
|
||||
def create_desktop_link(app_name: str, app_title: str = "", link_name: str = "", link_type="desktop") -> bool:
|
||||
"""
|
||||
Creates a desktop or start menu shortcut link
|
||||
|
||||
:param app_name:
|
||||
app_name or "rare_shortcut" for Rare itself
|
||||
:param app_title:
|
||||
the title shown in the shortcut
|
||||
(overrides to "Rare" for "rare_shortcut")
|
||||
:param link_name:
|
||||
the sanitized filename of the shortcut (use the folder_name attribute of RareGame)
|
||||
(overrides to "Rare" for "rare_shortcut")
|
||||
:param link_type:
|
||||
"desktop" or "start_menu"
|
||||
:return bool:
|
||||
True if successful else False
|
||||
"""
|
||||
# macOS is based on Darwin
|
||||
if not desktop_links_supported():
|
||||
logger.error(f"Shortcut creation is not available on {platform.system()}")
|
||||
return False
|
||||
|
||||
if link_type not in ["desktop", "start_menu"]:
|
||||
logger.error(f"Invalid link type {link_type}")
|
||||
return False
|
||||
|
||||
for_rare = app_name == "rare_shortcut"
|
||||
|
||||
if for_rare:
|
||||
icon_path = resources_path.joinpath("images", "Rare.png")
|
||||
if platform.system() == "Windows":
|
||||
icon_path = resources_path.joinpath("images", "Rare.ico")
|
||||
app_title = "Rare"
|
||||
link_name = "Rare"
|
||||
else:
|
||||
icon_path = image_dir().joinpath(app_name, "installed.png")
|
||||
if platform.system() == "Windows":
|
||||
icon_path = image_dir().joinpath(app_name, "installed.ico")
|
||||
if not icon_path.exists():
|
||||
img = QImage()
|
||||
img.load(str(icon_path.with_suffix(".png")))
|
||||
img.save(str(icon_path))
|
||||
logger.info("Created ico file")
|
||||
if not app_title or not link_name:
|
||||
logger.error("Missing app_title or link_name")
|
||||
return False
|
||||
|
||||
shortcut_path = desktop_link_path(link_name, link_type)
|
||||
if not shortcut_path.parent.exists():
|
||||
logger.error(f"Parent directory {shortcut_path.parent} does not exist")
|
||||
return False
|
||||
else:
|
||||
logger.info(f"Creating shortcut for {app_title} at {shortcut_path}")
|
||||
|
||||
if platform.system() == "Linux":
|
||||
executable = get_rare_executable()
|
||||
executable = shlex.join(executable)
|
||||
if not for_rare:
|
||||
executable = f"{executable} launch {app_name}"
|
||||
|
||||
with shortcut_path.open(mode="w") as desktop_file:
|
||||
desktop_file.write(
|
||||
"[Desktop Entry]\n"
|
||||
f"Name={app_title}\n"
|
||||
"Type=Application\n"
|
||||
"Categories=Game;\n"
|
||||
f"Icon={icon_path}\n"
|
||||
f"Exec={executable}\n"
|
||||
"Terminal=false\n"
|
||||
"StartupWMClass=Rare\n"
|
||||
)
|
||||
# shortcut_path.chmod(0o755)
|
||||
return True
|
||||
|
||||
elif platform.system() == "Windows":
|
||||
# Add shortcut
|
||||
shell = Dispatch("WScript.Shell")
|
||||
shortcut = shell.CreateShortCut(str(shortcut_path))
|
||||
|
||||
executable = get_rare_executable()
|
||||
arguments = []
|
||||
|
||||
if len(executable) > 1:
|
||||
arguments.extend(executable[1:])
|
||||
executable = executable[0]
|
||||
|
||||
if not for_rare:
|
||||
arguments.extend(["launch", app_name])
|
||||
shortcut.Targetpath = executable
|
||||
# Maybe there is a better solution, but windows does not accept single quotes (Windows is weird)
|
||||
shortcut.Arguments = shlex.join(arguments).replace("'", '"')
|
||||
|
||||
shortcut.Description = app_title
|
||||
if for_rare:
|
||||
shortcut.WorkingDirectory = str(home_dir())
|
||||
|
||||
# Icon
|
||||
shortcut.IconLocation = icon_path.absolute()
|
||||
|
||||
shortcut.save()
|
||||
return True
|
||||
|
|
Loading…
Reference in a new issue