diff --git a/rare/components/tabs/store/__init__.py b/rare/components/tabs/store/__init__.py index 35b5b6a2..273d2293 100644 --- a/rare/components/tabs/store/__init__.py +++ b/rare/components/tabs/store/__init__.py @@ -1,14 +1,13 @@ +from PyQt5.QtGui import QShowEvent, QHideEvent from legendary.core import LegendaryCore -from rare.shared import RareCore -from rare.utils.paths import cache_dir from rare.widgets.side_tab import SideTabWidget -from .game_info import ShopGameInfo -from .search_results import SearchResults -from .shop_api_core import ShopApiCore from .api.models.response import CatalogOfferModel -from .shop_widget import ShopWidget -from .wishlist import WishlistWidget, Wishlist +from .landing import LandingWidget, LandingPage +from .search import SearchPage +from .store_api import StoreAPI +from .widgets.details import DetailsWidget +from .wishlist import WishlistPage class StoreTab(SideTabWidget): @@ -19,45 +18,31 @@ class StoreTab(SideTabWidget): self.core = core # self.rcore = RareCore.instance() - self.api_core = ShopApiCore( + self.api = StoreAPI( self.core.egs.session.headers["Authorization"], self.core.language_code, self.core.country_code, + [] # [i.asset_infos["Windows"].namespace for i in self.rcore.game_list if bool(i.asset_infos)] ) - self.shop = ShopWidget(cache_dir(), self.core, self.api_core, parent=self) - self.shop_index = self.addTab(self.shop, self.tr("Store")) - self.shop.show_game.connect(self.show_game) - self.shop.show_info.connect(self.show_search) + self.landing = LandingPage(self.api, parent=self) + self.landing_index = self.addTab(self.landing, self.tr("Store")) - self.search = SearchResults(self.api_core, parent=self) - self.search_index = self.addTab(self.search, self.tr("Search"), self.tr("Results")) - self.search.show_info.connect(self.show_game) - # self.search.back_button.clicked.connect(lambda: self.setCurrentIndex(self.shop_index)) + self.search = SearchPage(self.core, self.api, parent=self) + self.search_index = self.addTab(self.search, self.tr("Search")) - self.info = ShopGameInfo( - # [i.asset_infos["Windows"].namespace for i in self.rcore.game_list if bool(i.asset_infos)], - [], - self.api_core, - parent=self - ) - self.info_index = self.addTab(self.info, self.tr("Information"), self.tr("Information")) - # self.info.back_button.clicked.connect(lambda: self.setCurrentIndex(self.previous_index)) + self.wishlist = WishlistPage(self.api, parent=self) + self.wishlist_index = self.addTab(self.wishlist, self.tr("Wishlist")) - self.wishlist = Wishlist(self.api_core, parent=self) - self.wishlist_index = self.addTab(self.wishlist, self.tr("Wishlist"), self.tr("Wishlist")) - self.wishlist.update_wishlist_signal.connect(self.update_wishlist) - self.wishlist.show_game_info.connect(self.show_game) + self.api.update_wishlist.connect(self.update_wishlist) - self.api_core.update_wishlist.connect(self.update_wishlist) - - self.previous_index = self.shop_index + self.previous_index = self.landing_index def showEvent(self, a0: QShowEvent) -> None: if a0.spontaneous() or self.init: return super().showEvent(a0) - self.shop.load() - self.wishlist_widget.update_wishlist() + # self.landing.load() + # self.wishlist.update_wishlist() self.init = True return super().showEvent(a0) @@ -68,13 +53,4 @@ class StoreTab(SideTabWidget): return super().hideEvent(a0) def update_wishlist(self): - self.shop.update_wishlist() - - def show_game(self, data: CatalogOfferModel): - self.previous_index = self.currentIndex() - self.info.update_game(data) - self.setCurrentIndex(self.info_index) - - def show_search(self, text: str): - self.search.load_results(text) - self.setCurrentIndex(self.search_index) + self.landing.update_wishlist() diff --git a/rare/components/tabs/store/__main__.py b/rare/components/tabs/store/__main__.py index a7366a13..ad7c13f4 100644 --- a/rare/components/tabs/store/__main__.py +++ b/rare/components/tabs/store/__main__.py @@ -18,13 +18,13 @@ class StoreWindow(QDialog): layout = QVBoxLayout(self) layout.addWidget(self.store_tab) - self.store_tab.load() + self.store_tab.show() if __name__ == "__main__": - from rare.utils.misc import set_style_sheet import rare.resources.static_css import rare.resources.stylesheets.RareStyle + from rare.utils.misc import set_style_sheet app = QApplication(sys.argv) app.setApplicationName("Rare") diff --git a/rare/components/tabs/store/api/debug.py b/rare/components/tabs/store/api/debug.py index 083fcca2..24a20016 100644 --- a/rare/components/tabs/store/api/debug.py +++ b/rare/components/tabs/store/api/debug.py @@ -1,7 +1,7 @@ from PyQt5.QtCore import Qt from PyQt5.QtWidgets import QTreeView, QDialog, QVBoxLayout -from utils.json_formatter import QJsonModel +from rare.utils.json_formatter import QJsonModel class DebugView(QTreeView): diff --git a/rare/components/tabs/store/api/graphql/schema.graphql b/rare/components/tabs/store/api/graphql/schema.graphql index 6f3edfda..676fcfe5 100644 --- a/rare/components/tabs/store/api/graphql/schema.graphql +++ b/rare/components/tabs/store/api/graphql/schema.graphql @@ -38,4 +38,39 @@ type LineOfferRes { type GetPriceRes { totalPrice: TotalPrice lineOffers: [LineOfferRes] +} + +type Image { + type: String + url: String + alt: String +} + +type StorePageMapping { + cmsSlug: String + offerId: ID + prePurchaseOfferId: ID +} + +type PageSandboxModel { + pageSlug: String + pageType: String + productId: ID + sandboxId: ID + createdDate: Date + updatedDate: Date + deletedDate: Date + mappings: [StorePageMapping] +} + +type CatalogNamespace { + parent: ID + displayName: String + store: String + mappings: [PageSandboxModel] +} + +type CatalogItem { + id: ID + namespace: ID } \ No newline at end of file diff --git a/rare/components/tabs/store/api/models/query.py b/rare/components/tabs/store/api/models/query.py index 925cc500..f3f21ca8 100644 --- a/rare/components/tabs/store/api/models/query.py +++ b/rare/components/tabs/store/api/models/query.py @@ -6,11 +6,11 @@ from typing import List @dataclass class SearchDateRange: start_date: datetime = datetime(year=1990, month=1, day=1, tzinfo=timezone.utc) - end_date: datetime = datetime.utcnow() + end_date: datetime = datetime.utcnow().replace(tzinfo=timezone.utc) def __str__(self): def fmt_date(date: datetime) -> str: - # lk: The formatting accepted by the GraphQL API is either '%Y-%m-%dT%H:%M:%S.000Z' or '%Y-%m-%dT' + # lk: The formatting accepted by the GraphQL API is either '%Y-%m-%dT%H:%M:%S.000Z' or '%Y-%m-%d' return datetime.strftime(date, '%Y-%m-%dT%H:%M:%S.000Z') return f"[{fmt_date(self.start_date)},{fmt_date(self.end_date)}]" diff --git a/rare/components/tabs/store/api/models/response.py b/rare/components/tabs/store/api/models/response.py index 182985a1..6a7672f2 100644 --- a/rare/components/tabs/store/api/models/response.py +++ b/rare/components/tabs/store/api/models/response.py @@ -1,8 +1,10 @@ import logging from dataclasses import dataclass, field -from datetime import datetime, timezone +from datetime import datetime from typing import List, Dict, Any, Type, Optional +from .utils import parse_date + logger = logging.getLogger("StoreApiModels") # lk: Typing overloads for unimplemented types @@ -13,171 +15,11 @@ CategoryModel = Dict CustomAttributeModel = Dict ItemModel = Dict SellerModel = Dict -OfferMappingModel = Dict +PageSandboxModel = Dict TagModel = Dict PromotionsModel = Dict -def parse_date(date: str): - return datetime.fromisoformat(date[:-1]).replace(tzinfo=timezone.utc) - - -@dataclass -class DieselSystemDetailItem: - p_type: Optional[str] = None - minimum: Optional[str] = None - recommended: Optional[str] = None - title: Optional[str] = None - unmapped: Dict[str, Any] = field(default_factory=dict) - - @classmethod - def from_dict(cls: Type["DieselSystemDetailItem"], src: Dict[str, Any]) -> "DieselSystemDetailItem": - d = src.copy() - tmp = cls( - p_type=d.pop("_type", ""), - minimum=d.pop("minimum", ""), - recommended=d.pop("recommended", ""), - title=d.pop("title", ""), - ) - tmp.unmapped = d - return tmp - - -@dataclass -class DieselSystemDetail: - p_type: Optional[str] = None - details: Optional[List[DieselSystemDetailItem]] = None - system_type: Optional[str] = None - unmapped: Dict[str, Any] = field(default_factory=dict) - - @classmethod - def from_dict(cls: Type["DieselSystemDetail"], src: Dict[str, Any]) -> "DieselSystemDetail": - d = src.copy() - _details = d.pop("details", []) - details = [] if _details else None - for item in _details: - detail = DieselSystemDetailItem.from_dict(item) - details.append(detail) - tmp = cls( - p_type=d.pop("_type", ""), - details=details, - system_type=d.pop("systemType", ""), - ) - tmp.unmapped = d - return tmp - - -@dataclass -class DieselSystemDetails: - p_type: Optional[str] = None - languages: Optional[List[str]] = None - rating: Optional[Dict] = None - systems: Optional[List[DieselSystemDetail]] = None - unmapped: Dict[str, Any] = field(default_factory=dict) - - @classmethod - def from_dict(cls: Type["DieselSystemDetails"], src: Dict[str, Any]) -> "DieselSystemDetails": - d = src.copy() - _systems = d.pop("systems", []) - systems = [] if _systems else None - for item in _systems: - system = DieselSystemDetail.from_dict(item) - systems.append(system) - tmp = cls( - p_type=d.pop("_type", ""), - languages=d.pop("languages", []), - rating=d.pop("rating", {}), - systems=systems, - ) - tmp.unmapped = d - return tmp - - -@dataclass -class DieselProductAbout: - p_type: Optional[str] = None - desciption: Optional[str] = None - developer_attribution: Optional[str] = None - publisher_attribution: Optional[str] = None - short_description: Optional[str] = None - unmapped: Dict[str, Any] = field(default_factory=dict) - - @classmethod - def from_dict(cls: Type["DieselProductAbout"], src: Dict[str, Any]) -> "DieselProductAbout": - d = src.copy() - tmp = cls( - p_type=d.pop("_type", ""), - desciption=d.pop("description", ""), - developer_attribution=d.pop("developerAttribution", ""), - publisher_attribution=d.pop("publisherAttribution", ""), - short_description=d.pop("shortDescription", ""), - ) - tmp.unmapped = d - return tmp - - -@dataclass -class DieselProductDetail: - p_type: Optional[str] = None - about: Optional[DieselProductAbout] = None - requirements: Optional[DieselSystemDetails] = None - social_links: Optional[DieselSocialLinks] = None - unmapped: Dict[str, Any] = field(default_factory=dict) - - @classmethod - def from_dict(cls: Type["DieselProductDetail"], src: Dict[str, Any]) -> "DieselProductDetail": - d = src.copy() - about = DieselProductAbout.from_dict(x) if (x := d.pop("about"), {}) else None - requirements = DieselSystemDetails.from_dict(x) if (x := d.pop("requirements", {})) else None - tmp = cls( - p_type=d.pop("_type", ""), - about=about, - requirements=requirements, - social_links=d.pop("socialLinks", {}), - ) - tmp.unmapped = d - return tmp - - -@dataclass -class DieselProduct: - p_id: Optional[str] = None - p_images_: Optional[List[str]] = None - p_locale: Optional[str] = None - p_slug: Optional[str] = None - p_title: Optional[str] = None - p_url_pattern: Optional[str] = None - namespace: Optional[str] = None - pages: Optional[List["DieselProduct"]] = None - data: Optional[DieselProductDetail] = None - product_name: Optional[str] = None - unmapped: Dict[str, Any] = field(default_factory=dict) - - @classmethod - def from_dict(cls: Type["DieselProduct"], src: Dict[str, Any]) -> "DieselProduct": - d = src.copy() - _pages = d.pop("pages", []) - pages = [] if _pages else None - for item in _pages: - page = DieselProduct.from_dict(item) - pages.append(page) - data = DieselProductDetail.from_dict(x) if (x := d.pop("data", {})) else None - tmp = cls( - p_id=d.pop("_id", ""), - p_images_=d.pop("_images_", []), - p_locale=d.pop("_locale", ""), - p_slug=d.pop("_slug", ""), - p_title=d.pop("_title", ""), - p_url_pattern=d.pop("_urlPattern", ""), - namespace=d.pop("namespace", ""), - pages=pages, - data=data, - product_name=d.pop("productName", ""), - ) - tmp.unmapped = d - return tmp - - @dataclass class ImageUrlModel: type: Optional[str] = None @@ -271,14 +113,14 @@ LineOffersModel = Dict @dataclass -class PriceModel: +class GetPriceResModel: total_price: Optional[TotalPriceModel] = None fmt_price: Optional[FmtPriceModel] = None line_offers: Optional[LineOffersModel] = None unmapped: Dict[str, Any] = field(default_factory=dict) @classmethod - def from_dict(cls: Type["PriceModel"], src: Dict[str, Any]) -> "PriceModel": + def from_dict(cls: Type["GetPriceResModel"], src: Dict[str, Any]) -> "GetPriceResModel": d = src.copy() tmp = cls( total_price=d.pop("totalPrice", {}), @@ -302,9 +144,9 @@ class CatalogOfferModel: items: Optional[List[ItemModel]] = None key_images: Optional[KeyImagesModel] = None namespace: Optional[str] = None - offer_mappings: Optional[List[OfferMappingModel]] = None + offer_mappings: Optional[List[PageSandboxModel]] = None offer_type: Optional[str] = None - price: Optional[PriceModel] = None + price: Optional[GetPriceResModel] = None product_slug: Optional[str] = None promotions: Optional[PromotionsModel] = None seller: Optional[SellerModel] = None @@ -322,7 +164,7 @@ class CatalogOfferModel: effective_date = parse_date(x) if (x := d.pop("effectiveDate", "")) else None expiry_date = parse_date(x) if (x := d.pop("expiryDate", "")) else None key_images = KeyImagesModel.from_list(d.pop("keyImages", [])) - price = PriceModel.from_dict(x) if (x := d.pop("price", {})) else None + price = GetPriceResModel.from_dict(x) if (x := d.pop("price", {})) else None viewable_date = parse_date(x) if (x := d.pop("viewableDate", "")) else None tmp = cls( catalog_ns=d.pop("catalogNs", {}), diff --git a/rare/components/tabs/store/landing.py b/rare/components/tabs/store/landing.py new file mode 100644 index 00000000..6f570a75 --- /dev/null +++ b/rare/components/tabs/store/landing.py @@ -0,0 +1,212 @@ +import datetime +import logging +from typing import List + +from PyQt5.QtCore import Qt, pyqtSlot, pyqtSignal +from PyQt5.QtGui import QShowEvent, QHideEvent +from PyQt5.QtWidgets import ( + QHBoxLayout, + QWidget, + QSizePolicy, + QVBoxLayout, + QSpacerItem, + QScrollArea, + QFrame, +) + +from rare.widgets.flow_layout import FlowLayout +from rare.widgets.side_tab import SideTabContents +from rare.widgets.sliding_stack import SlidingStackedWidget +from rare.components.tabs.store.api.models.response import CatalogOfferModel, WishlistItemModel +from .api.models.utils import parse_date +from .store_api import StoreAPI +from .widgets.details import DetailsWidget +from .widgets.items import StoreItemWidget +from .widgets.groups import StoreGroup + +logger = logging.getLogger("StoreLanding") + + +class LandingPage(SlidingStackedWidget, SideTabContents): + + def __init__(self, api: StoreAPI, parent=None): + super(LandingPage, self).__init__(parent=parent) + self.implements_scrollarea = True + + self.landing_widget = LandingWidget(api, parent=self) + self.landing_widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) + self.landing_widget.set_title.connect(self.set_title) + self.landing_widget.show_details.connect(self.show_details) + + self.landing_scroll = QScrollArea(self) + self.landing_scroll.setWidgetResizable(True) + self.landing_scroll.setFrameStyle(QFrame.NoFrame | QFrame.Plain) + self.landing_scroll.setWidget(self.landing_widget) + + self.details_widget = DetailsWidget([], api, parent=self) + self.details_widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) + self.details_widget.set_title.connect(self.set_title) + self.details_widget.back_clicked.connect(self.show_main) + + self.details_scroll = QScrollArea(self) + self.details_scroll.setWidgetResizable(True) + self.details_scroll.setFrameStyle(QFrame.NoFrame | QFrame.Plain) + self.details_scroll.setWidget(self.details_widget) + + self.setDirection(Qt.Horizontal) + self.addWidget(self.landing_scroll) + self.addWidget(self.details_scroll) + + @pyqtSlot() + def show_main(self): + self.slideInWidget(self.landing_scroll) + + @pyqtSlot(object) + def show_details(self, game: CatalogOfferModel): + self.details_widget.update_game(game) + self.slideInWidget(self.details_scroll) + + +class LandingWidget(QWidget, SideTabContents): + show_details = pyqtSignal(CatalogOfferModel) + + def __init__(self, api: StoreAPI, parent=None): + super(LandingWidget, self).__init__(parent=parent) + self.api = api + + layout = QVBoxLayout(self) + layout.setContentsMargins(0, 0, 3, 0) + self.setLayout(layout) + self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) + + self.free_games_now = StoreGroup(self.tr("Free now"), layout=QHBoxLayout, parent=self) + self.free_games_now.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) + + self.free_games_next = StoreGroup(self.tr("Free next week"), layout=QHBoxLayout, parent=self) + self.free_games_next.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) + + self.discounts_group = StoreGroup(self.tr("Wishlist discounts"), layout=FlowLayout, parent=self) + self.discounts_group.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) + + self.games_group = StoreGroup(self.tr("Games"), FlowLayout, self) + self.games_group.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) + self.games_group.loading(False) + self.games_group.setVisible(True) + + layout.addWidget(self.free_games_now, alignment=Qt.AlignTop) + layout.addWidget(self.free_games_next, alignment=Qt.AlignTop) + layout.addWidget(self.discounts_group, alignment=Qt.AlignTop) + layout.addWidget(self.games_group, alignment=Qt.AlignTop) + layout.addItem(QSpacerItem(0, 0, QSizePolicy.Fixed, QSizePolicy.Expanding)) + + def showEvent(self, a0: QShowEvent) -> None: + if a0.spontaneous(): + return super().showEvent(a0) + self.api.get_free_games(self.__add_free) + self.api.get_wishlist(self.__add_discounts) + return super().showEvent(a0) + + def hideEvent(self, a0: QHideEvent) -> None: + if a0.spontaneous(): + return super().hideEvent(a0) + # TODO: Implement tab unloading + return super().hideEvent(a0) + + def __add_discounts(self, wishlist: List[WishlistItemModel]): + for w in self.discounts_group.findChildren(StoreItemWidget, options=Qt.FindDirectChildrenOnly): + self.discounts_group.layout().removeWidget(w) + w.deleteLater() + + discounts = 0 + for game in wishlist: + if not game: + continue + try: + if game.offer.price.total_price["discount"] > 0: + w = StoreItemWidget(self.api.cached_manager, game.offer) + w.show_details.connect(self.show_details) + self.discounts_group.layout().addWidget(w) + discounts += 1 + except Exception as e: + logger.warning(f"{game} {e}") + continue + # self.discounts_group.setVisible(discounts > 0) + self.discounts_group.loading(False) + + def __add_free(self, free_games: List[CatalogOfferModel]): + for w in self.free_games_now.findChildren(StoreItemWidget, options=Qt.FindDirectChildrenOnly): + self.free_games_now.layout().removeWidget(w) + w.deleteLater() + + for w in self.free_games_next.findChildren(StoreItemWidget, options=Qt.FindDirectChildrenOnly): + self.free_games_next.layout().removeWidget(w) + w.deleteLater() + + date = datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc) + free_now = [] + free_next = [] + for game in free_games: + try: + if ( + game.price.total_price["fmtPrice"]["discountPrice"] == "0" + and game.price.total_price["fmtPrice"]["originalPrice"] + != game.price.total_price["fmtPrice"]["discountPrice"] + ): + free_now.append(game) + continue + + if game.title == "Mystery Game": + free_next.append(game) + continue + except KeyError as e: + logger.warning(str(e)) + + try: + # parse datetime to check if game is next week or now + try: + start_date = parse_date( + game.promotions["upcomingPromotionalOffers"][0]["promotionalOffers"][0]["startDate"] + ) + except Exception: + try: + start_date = parse_date( + game.promotions["promotionalOffers"][0]["promotionalOffers"][0]["startDate"] + ) + except Exception as e: + continue + + except TypeError: + print("type error") + continue + + if start_date > date: + free_next.append(game) + + # free games now + self.free_games_now.setVisible(bool(len(free_now))) + for game in free_now: + w = StoreItemWidget(self.api.cached_manager, game) + w.show_details.connect(self.show_details) + self.free_games_now.layout().addWidget(w) + self.free_games_now.loading(False) + + # free games next week + self.free_games_next.setVisible(bool(len(free_next))) + for game in free_next: + w = StoreItemWidget(self.api.cached_manager, game) + if game.title != "Mystery Game": + w.show_details.connect(self.show_details) + self.free_games_next.layout().addWidget(w) + self.free_games_next.loading(False) + + def show_games(self, data): + for w in self.games_group.findChildren(StoreItemWidget, options=Qt.FindDirectChildrenOnly): + self.games_group.layout().removeWidget(w) + w.deleteLater() + + if data: + for game in data: + w = StoreItemWidget(self.api.cached_manager, game) + w.show_details.connect(self.show_details) + self.games_group.layout().addWidget(w) + self.games_group.loading(False) diff --git a/rare/components/tabs/store/results.py b/rare/components/tabs/store/results.py new file mode 100644 index 00000000..f837a103 --- /dev/null +++ b/rare/components/tabs/store/results.py @@ -0,0 +1,57 @@ +from PyQt5.QtCore import Qt +from PyQt5.QtCore import pyqtSignal +from PyQt5.QtWidgets import ( + QWidget, + QSizePolicy, + QLabel, + QScrollArea, +) + +from rare.widgets.flow_layout import FlowLayout +from .api.models.response import CatalogOfferModel +from .widgets.items import ResultsItemWidget + + +class ResultsWidget(QScrollArea): + show_details = pyqtSignal(CatalogOfferModel) + + def __init__(self, store_api, parent=None): + super(ResultsWidget, self).__init__(parent=parent) + self.implements_scrollarea = True + self.store_api = store_api + + self.results_container = QWidget(self) + self.results_container.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) + self.results_layout = FlowLayout(self.results_container) + self.setWidget(self.results_container) + self.setWidgetResizable(True) + + # self.main_layout = QVBoxLayout(self) + # self.main_layout.setContentsMargins(0, 0, 0, 0) + # self.main_layout.addWidget(self.results_scrollarea) + + self.setEnabled(False) + + def load_results(self, text: str): + self.setEnabled(False) + if text != "": + self.store_api.search_game(text, self.show_results) + + def show_results(self, results: dict): + for w in self.results_container.findChildren(QLabel, options=Qt.FindDirectChildrenOnly): + self.results_layout.removeWidget(w) + w.deleteLater() + for w in self.results_container.findChildren(ResultsItemWidget, options=Qt.FindDirectChildrenOnly): + self.results_layout.removeWidget(w) + w.deleteLater() + + if not results: + self.results_layout.addWidget(QLabel(self.tr("No results found"))) + else: + for res in results: + w = ResultsItemWidget(self.store_api.cached_manager, res, parent=self.results_container) + w.show_details.connect(self.show_details.emit) + self.results_layout.addWidget(w) + self.results_layout.update() + self.setEnabled(True) + diff --git a/rare/components/tabs/store/search.py b/rare/components/tabs/store/search.py new file mode 100644 index 00000000..870e9daf --- /dev/null +++ b/rare/components/tabs/store/search.py @@ -0,0 +1,223 @@ +import logging +from typing import List + +from PyQt5.QtCore import pyqtSignal, Qt, pyqtSlot +from PyQt5.QtWidgets import ( + QCheckBox, + QWidget, + QSizePolicy, + QScrollArea, + QFrame, +) +from legendary.core import LegendaryCore + +from rare.ui.components.tabs.store.search import Ui_SearchWidget +from rare.utils.extra_widgets import ButtonLineEdit +from rare.widgets.side_tab import SideTabContents +from rare.widgets.sliding_stack import SlidingStackedWidget +from .api.models.query import SearchStoreQuery +from .api.models.response import CatalogOfferModel +from .constants import Constants +from .results import ResultsWidget +from .store_api import StoreAPI +from .widgets.details import DetailsWidget + +logger = logging.getLogger("Shop") + + +class SearchPage(SlidingStackedWidget, SideTabContents): + def __init__(self, core, api: StoreAPI, parent=None): + super(SearchPage, self).__init__(parent=parent) + self.implements_scrollarea = True + + self.search_widget = SearchWidget(core, api, parent=self) + self.search_widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) + self.search_widget.set_title.connect(self.set_title) + self.search_widget.show_details.connect(self.show_details) + + self.details_widget = DetailsWidget([], api, parent=self) + self.details_widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) + self.details_widget.set_title.connect(self.set_title) + self.details_widget.back_clicked.connect(self.show_main) + + self.details_scroll = QScrollArea(self) + self.details_scroll.setWidgetResizable(True) + self.details_scroll.setFrameStyle(QFrame.NoFrame | QFrame.Plain) + self.details_scroll.setWidget(self.details_widget) + + self.setDirection(Qt.Horizontal) + self.addWidget(self.search_widget) + self.addWidget(self.details_scroll) + + @pyqtSlot() + def show_main(self): + self.slideInWidget(self.search_widget) + + @pyqtSlot(object) + def show_details(self, game: CatalogOfferModel): + self.details_widget.update_game(game) + self.slideInWidget(self.details_scroll) + + +# noinspection PyAttributeOutsideInit,PyBroadException +class SearchWidget(QWidget, SideTabContents): + show_details = pyqtSignal(CatalogOfferModel) + + def __init__(self, core: LegendaryCore, api: StoreAPI, parent=None): + super(SearchWidget, self).__init__(parent=parent) + self.implements_scrollarea = True + self.ui = Ui_SearchWidget() + self.ui.setupUi(self) + self.ui.main_layout.setContentsMargins(0, 0, 3, 0) + + self.core = core + self.api_core = api + self.price = "" + self.tags = [] + self.types = [] + self.update_games_allowed = True + + self.free_game_widgets = [] + self.active_search_request = False + self.next_search = "" + self.wishlist: List = [] + + self.search_bar = ButtonLineEdit("fa.search", placeholder_text=self.tr("Search Games")) + self.results_scrollarea = ResultsWidget(self.api_core, self) + self.results_scrollarea.show_details.connect(self.show_details) + + self.ui.left_layout.addWidget(self.search_bar) + self.ui.left_layout.addWidget(self.results_scrollarea) + + self.search_bar.returnPressed.connect(self.show_search_results) + self.search_bar.buttonClicked.connect(self.show_search_results) + + # self.init_filter() + + def load(self): + # load browse games + self.prepare_request() + + def show_search_results(self): + if text := self.search_bar.text(): + self.results_scrollarea.load_results(text) + # self.show_info.emit(self.search_bar.text()) + + def init_filter(self): + self.ui.none_price.toggled.connect( + lambda: self.prepare_request("") if self.ui.none_price.isChecked() else None + ) + self.ui.free_button.toggled.connect( + lambda: self.prepare_request("free") if self.ui.free_button.isChecked() else None + ) + self.ui.under10.toggled.connect( + lambda: self.prepare_request("[0, 1000)") if self.ui.under10.isChecked() else None + ) + self.ui.under20.toggled.connect( + lambda: self.prepare_request("[0, 2000)") if self.ui.under20.isChecked() else None + ) + self.ui.under30.toggled.connect( + lambda: self.prepare_request("[0, 3000)") if self.ui.under30.isChecked() else None + ) + self.ui.above.toggled.connect( + lambda: self.prepare_request("[1499,]") if self.ui.above.isChecked() else None + ) + # self.on_discount.toggled.connect(lambda: self.prepare_request("sale") if self.on_discount.isChecked() else None) + self.ui.on_discount.toggled.connect(lambda: self.prepare_request()) + constants = Constants() + + self.checkboxes = [] + + for groupbox, variables in [ + (self.ui.genre_group, constants.categories), + (self.ui.platform_group, constants.platforms), + (self.ui.others_group, constants.others), + (self.ui.type_group, constants.types), + ]: + for text, tag in variables: + checkbox = CheckBox(text, tag) + checkbox.activated.connect(lambda x: self.prepare_request(added_tag=x)) + checkbox.deactivated.connect(lambda x: self.prepare_request(removed_tag=x)) + groupbox.layout().addWidget(checkbox) + self.checkboxes.append(checkbox) + self.ui.reset_button.clicked.connect(self.reset_filters) + self.ui.filter_scrollarea.setMinimumWidth( + self.ui.filter_container.sizeHint().width() + + self.ui.filter_container.layout().contentsMargins().left() + + self.ui.filter_container.layout().contentsMargins().right() + + self.ui.filter_scrollarea.verticalScrollBar().sizeHint().width() + ) + + def reset_filters(self): + self.update_games_allowed = False + for cb in self.checkboxes: + cb.setChecked(False) + self.ui.none_price.setChecked(True) + + self.tags = [] + self.types = [] + self.update_games_allowed = True + + self.ui.on_discount.setChecked(False) + + def prepare_request( + self, + price: str = None, + added_tag: int = 0, + removed_tag: int = 0, + added_type: str = "", + removed_type: str = "", + ): + if not self.update_games_allowed: + return + if price is not None: + self.price = price + + if added_tag != 0: + self.tags.append(added_tag) + if removed_tag != 0 and removed_tag in self.tags: + self.tags.remove(removed_tag) + + if added_type: + self.types.append(added_type) + if removed_type and removed_type in self.types: + self.types.remove(removed_type) + if (self.types or self.price) or self.tags or self.ui.on_discount.isChecked(): + # self.free_scrollarea.setVisible(False) + self.discounts_group.setVisible(False) + else: + # self.free_scrollarea.setVisible(True) + if len(self.discounts_group.layout().children()) > 0: + self.discounts_group.setVisible(True) + + self.games_group.loading(True) + + browse_model = SearchStoreQuery( + language=self.core.language_code, + country=self.core.country_code, + count=20, + price_range=self.price, + on_sale=self.ui.on_discount.isChecked(), + ) + browse_model.tag = "|".join(self.tags) + + if self.types: + browse_model.category = "|".join(self.types) + self.api_core.browse_games(browse_model, self.show_games) + + +class CheckBox(QCheckBox): + activated = pyqtSignal(str) + deactivated = pyqtSignal(str) + + def __init__(self, text, tag): + super(CheckBox, self).__init__(text) + self.tag = tag + + self.toggled.connect(self.handle_toggle) + + def handle_toggle(self): + if self.isChecked(): + self.activated.emit(self.tag) + else: + self.deactivated.emit(self.tag) diff --git a/rare/components/tabs/store/search_results.py b/rare/components/tabs/store/search_results.py deleted file mode 100644 index 24ec3fd1..00000000 --- a/rare/components/tabs/store/search_results.py +++ /dev/null @@ -1,98 +0,0 @@ -from PyQt5.QtCore import Qt -from PyQt5.QtCore import pyqtSignal -from PyQt5.QtGui import QMouseEvent -from PyQt5.QtWidgets import ( - QWidget, - QSizePolicy, - QLabel, QScrollArea, -) - -from rare.shared.image_manager import ImageSize -from rare.utils.qt_requests import QtRequestManager -from rare.widgets.flow_layout import FlowLayout -from rare.widgets.side_tab import SideTabContents -from .image_widget import ShopImageWidget - -from .api.debug import DebugDialog -from .api.models.response import CatalogOfferModel - - -class SearchResults(QScrollArea, SideTabContents): - show_info = pyqtSignal(CatalogOfferModel) - - def __init__(self, api_core, parent=None): - super(SearchResults, self).__init__(parent=parent) - self.implements_scrollarea = True - self.api_core = api_core - - self.results_container = QWidget(self) - self.results_container.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) - self.results_layout = FlowLayout(self.results_container) - self.setWidget(self.results_container) - self.setWidgetResizable(True) - - # self.main_layout = QVBoxLayout(self) - # self.main_layout.setContentsMargins(0, 0, 0, 0) - # self.main_layout.addWidget(self.results_scrollarea) - - self.setEnabled(False) - - def load_results(self, text: str): - self.setEnabled(False) - if text != "": - self.api_core.search_game(text, self.show_results) - - def show_results(self, results: dict): - for w in self.results_container.findChildren(QLabel, options=Qt.FindDirectChildrenOnly): - self.results_layout.removeWidget(w) - w.deleteLater() - for w in self.results_container.findChildren(SearchResultItem, options=Qt.FindDirectChildrenOnly): - self.results_layout.removeWidget(w) - w.deleteLater() - - if not results: - self.results_layout.addWidget(QLabel(self.tr("No results found"))) - else: - for res in results: - w = SearchResultItem(self.api_core.cached_manager, res, parent=self.results_container) - w.show_info.connect(self.show_info.emit) - self.results_layout.addWidget(w) - self.results_layout.update() - self.setEnabled(True) - - -class SearchResultItem(ShopImageWidget): - show_info = pyqtSignal(CatalogOfferModel) - - def __init__(self, manager: QtRequestManager, catalog_game: CatalogOfferModel, parent=None): - super(SearchResultItem, self).__init__(manager, parent=parent) - self.setFixedSize(ImageSize.Normal) - self.ui.setupUi(self) - - key_images = catalog_game.key_images - self.fetchPixmap(key_images.for_dimensions(self.width(), self.height()).url) - - self.ui.title_label.setText(catalog_game.title) - - price = catalog_game.price.total_price["fmtPrice"]["originalPrice"] - discount_price = catalog_game.price.total_price["fmtPrice"]["discountPrice"] - self.ui.price_label.setText(f'{price if price != "0" else self.tr("Free")}') - if price != discount_price: - font = self.ui.price_label.font() - font.setStrikeOut(True) - self.ui.price_label.setFont(font) - self.ui.discount_label.setText(f'{discount_price if discount_price != "0" else self.tr("Free")}') - else: - self.ui.discount_label.setVisible(False) - - self.catalog_game = catalog_game - - def mousePressEvent(self, a0: QMouseEvent) -> None: - if a0.button() == Qt.LeftButton: - a0.accept() - self.show_info.emit(self.catalog_game) - if a0.button() == Qt.RightButton: - a0.accept() - dialog = DebugDialog(self.catalog_game.__dict__, self) - dialog.show() - diff --git a/rare/components/tabs/store/shop_widget.py b/rare/components/tabs/store/shop_widget.py deleted file mode 100644 index 43e0342e..00000000 --- a/rare/components/tabs/store/shop_widget.py +++ /dev/null @@ -1,396 +0,0 @@ -import datetime -import logging -from typing import List - -from PyQt5.QtCore import pyqtSignal, Qt -from PyQt5.QtWidgets import ( - QGroupBox, - QCheckBox, - QLabel, - QPushButton, - QHBoxLayout, - QWidget, QSizePolicy, QStackedLayout, -) -from legendary.core import LegendaryCore - -from rare.ui.components.tabs.store.store import Ui_ShopWidget -from rare.utils.extra_widgets import ButtonLineEdit -from rare.widgets.flow_layout import FlowLayout -from rare.widgets.side_tab import SideTabContents -from .api.models.query import SearchStoreQuery -from .api.models.response import CatalogOfferModel, WishlistItemModel -from .constants import Constants -from .game_widgets import GameWidget -from .image_widget import WaitingSpinner -from .shop_api_core import ShopApiCore - -from .api.models.utils import parse_date - -logger = logging.getLogger("Shop") - - -# noinspection PyAttributeOutsideInit,PyBroadException -class ShopWidget(QWidget, SideTabContents): - show_info = pyqtSignal(str) - show_game = pyqtSignal(dict) - - def __init__(self, cache_dir, core: LegendaryCore, shop_api: ShopApiCore, parent=None): - super(ShopWidget, self).__init__(parent=parent) - self.implements_scrollarea = True - self.ui = Ui_ShopWidget() - self.ui.setupUi(self) - self.cache_dir = cache_dir - self.core = core - self.api_core = shop_api - self.price = "" - self.tags = [] - self.types = [] - self.update_games_allowed = True - - self.ui.free_scrollarea.setDisabled(True) - - self.free_game_widgets = [] - self.active_search_request = False - self.next_search = "" - self.wishlist: List = [] - - self.discounts_layout = QStackedLayout(self.ui.discounts_group) - self.discounts_spinner = WaitingSpinner(self.ui.discounts_group) - self.discounts_flow = QWidget(self.ui.discounts_group) - self.discounts_flow.setLayout(FlowLayout(self.discounts_flow)) - self.discounts_flow.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) - self.discounts_layout.addWidget(self.discounts_spinner) - self.discounts_layout.addWidget(self.discounts_flow) - - self.discounts_spinner.start() - self.discounts_layout.setCurrentWidget(self.discounts_spinner) - - self.games_layout = QStackedLayout(self.ui.games_group) - self.games_spinner = WaitingSpinner(self.ui.games_group) - self.games_flow = QWidget(self.ui.games_group) - self.games_flow.setLayout(FlowLayout(self.games_flow)) - self.games_flow.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) - self.games_layout.addWidget(self.games_spinner) - self.games_layout.addWidget(self.games_flow) - - self.games_spinner.start() - self.games_layout.setCurrentWidget(self.games_spinner) - - self.search_bar = ButtonLineEdit( - "fa.search", placeholder_text=self.tr("Search Games") - ) - self.ui.main_layout.addWidget(self.search_bar, 0, 0) - - # self.search_bar.textChanged.connect(self.search_games) - - self.search_bar.returnPressed.connect(self.show_search_results) - self.search_bar.buttonClicked.connect(self.show_search_results) - - self.init_filter() - - self.search_bar.setHidden(True) - self.filter_gb.setHidden(True) - self.filter_game_gb.setHidden(True) - - # self.search_bar.textChanged.connect(self.load_completer) - - def load(self): - # load free games - self.api_core.get_free_games(self.add_free_games) - # load wishlist - self.api_core.get_wishlist(self.add_wishlist_items) - # load browse games - self.prepare_request() - - def update_wishlist(self): - self.api_core.get_wishlist(self.add_wishlist_items) - - def add_wishlist_items(self, wishlist: List[WishlistItemModel]): - for w in self.discounts_flow.findChildren(QWidget, options=Qt.FindDirectChildrenOnly): - self.discounts_flow.layout().removeWidget(w) - w.deleteLater() - - # if wishlist and wishlist[0] == "error": - # self.discounts_group.layout().addWidget( - # QLabel(self.tr("Failed to get wishlist: {}").format(wishlist[1])) - # ) - # btn = QPushButton(self.tr("Reload")) - # self.discount_widget.layout().addWidget(btn) - # btn.clicked.connect( - # lambda: self.api_core.get_wishlist(self.add_wishlist_items) - # ) - # self.discount_stack.setCurrentIndex(0) - # return - - discounts = 0 - for game in wishlist: - if not game: - continue - try: - if game.offer.price.total_price["discount"] > 0: - w = GameWidget(self.api_core.cached_manager, game.offer) - w.show_info.connect(self.show_game) - self.discounts_flow.layout().addWidget(w) - discounts += 1 - except Exception as e: - logger.warning(f"{game} {e}") - continue - self.ui.discounts_group.setVisible(discounts > 0) - self.discounts_layout.setCurrentWidget(self.discounts_flow) - # FIXME: FlowLayout doesn't update on adding widget - self.discounts_flow.layout().update() - - def add_free_games(self, free_games: List[CatalogOfferModel]): - for w in self.ui.free_container.layout().findChildren(QGroupBox, options=Qt.FindDirectChildrenOnly): - self.ui.free_container.layout().removeWidget(w) - w.deleteLater() - - if free_games and free_games[0] == "error": - self.ui.free_container.layout().addWidget( - QLabel(self.tr("Failed to fetch free games: {}").format(free_games[1])) - ) - btn = QPushButton(self.tr("Reload")) - self.ui.free_container.layout().addWidget(btn) - btn.clicked.connect( - lambda: self.api_core.get_free_games(self.add_free_games) - ) - self.ui.free_container.setEnabled(True) - return - - self.free_games_now = QGroupBox(self.tr("Free now"), parent=self.ui.free_container) - free_games_now_layout = QHBoxLayout(self.free_games_now) - # free_games_now_layout.setContentsMargins(0, 0, 0, 0) - self.free_games_now.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) - self.free_games_now.setLayout(free_games_now_layout) - self.ui.free_container.layout().addWidget(self.free_games_now) - - self.free_games_next = QGroupBox(self.tr("Free next week"), parent=self.ui.free_container) - free_games_next_layout = QHBoxLayout(self.free_games_next) - # free_games_next_layout.setContentsMargins(0, 0, 0, 0) - self.free_games_next.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) - self.free_games_next.setLayout(free_games_next_layout) - self.ui.free_container.layout().addWidget(self.free_games_next) - - date = datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc) - free_games_now = [] - coming_free_games = [] - for game in free_games: - try: - if ( - game.price.total_price["fmtPrice"]["discountPrice"] == "0" - and game.price.total_price["fmtPrice"]["originalPrice"] - != game.price.total_price["fmtPrice"]["discountPrice"] - ): - free_games_now.append(game) - continue - - if game.title == "Mystery Game": - coming_free_games.append(game) - continue - except KeyError as e: - logger.warning(str(e)) - - try: - # parse datetime to check if game is next week or now - try: - start_date = parse_date( - game.promotions["upcomingPromotionalOffers"][0]["promotionalOffers"][0]["startDate"] - ) - except Exception: - try: - start_date = parse_date( - game.promotions["promotionalOffers"][0]["promotionalOffers"][0]["startDate"] - ) - except Exception as e: - - continue - - except TypeError: - print("type error") - continue - - if start_date > date: - coming_free_games.append(game) - # free games now - now_free = 0 - for free_game in free_games_now: - w = GameWidget(self.api_core.cached_manager, free_game) - w.show_info.connect(self.show_game) - self.free_games_now.layout().addWidget(w) - self.free_game_widgets.append(w) - now_free += 1 - if now_free == 0: - self.free_games_now.layout().addWidget( - QLabel(self.tr("Could not find current free game")) - ) - - # free games next week - for free_game in coming_free_games: - w = GameWidget(self.api_core.cached_manager, free_game) - if free_game.title != "Mystery Game": - w.show_info.connect(self.show_game) - self.free_games_next.layout().addWidget(w) - # self.coming_free_games.setFixedWidth(int(40 + len(coming_free_games) * 300)) - - self.ui.free_scrollarea.setMinimumHeight( - self.free_games_now.sizeHint().height() - + self.ui.free_container.layout().contentsMargins().top() - + self.ui.free_container.layout().contentsMargins().bottom() - + self.ui.free_scrollarea.horizontalScrollBar().sizeHint().height() - ) - self.ui.free_scrollarea.setEnabled(True) - - def show_search_results(self): - if self.search_bar.text(): - self.show_info.emit(self.search_bar.text()) - - def init_filter(self): - self.ui.none_price.toggled.connect( - lambda: self.prepare_request("") if self.ui.none_price.isChecked() else None - ) - self.ui.free_button.toggled.connect( - lambda: self.prepare_request("free") - if self.ui.free_button.isChecked() - else None - ) - self.ui.under10.toggled.connect( - lambda: self.prepare_request("[0, 1000)") - if self.ui.under10.isChecked() - else None - ) - self.ui.under20.toggled.connect( - lambda: self.prepare_request("[0, 2000)") - if self.ui.under20.isChecked() - else None - ) - self.ui.under30.toggled.connect( - lambda: self.prepare_request("[0, 3000)") - if self.ui.under30.isChecked() - else None - ) - self.ui.above.toggled.connect( - lambda: self.prepare_request("[1499,]") - if self.ui.above.isChecked() - else None - ) - # self.on_discount.toggled.connect(lambda: self.prepare_request("sale") if self.on_discount.isChecked() else None) - self.ui.on_discount.toggled.connect(lambda: self.prepare_request()) - constants = Constants() - - self.checkboxes = [] - - for groupbox, variables in [ - (self.ui.genre_group, constants.categories), - (self.ui.platform_group, constants.platforms), - (self.ui.others_group, constants.others), - (self.ui.type_group, constants.types), - ]: - - for text, tag in variables: - checkbox = CheckBox(text, tag) - checkbox.activated.connect(lambda x: self.prepare_request(added_tag=x)) - checkbox.deactivated.connect( - lambda x: self.prepare_request(removed_tag=x) - ) - groupbox.layout().addWidget(checkbox) - self.checkboxes.append(checkbox) - self.ui.reset_button.clicked.connect(self.reset_filters) - self.ui.filter_scrollarea.setMinimumWidth( - self.ui.filter_container.sizeHint().width() - + self.ui.filter_container.layout().contentsMargins().left() - + self.ui.filter_container.layout().contentsMargins().right() - + self.ui.filter_scrollarea.verticalScrollBar().sizeHint().width() - ) - - def reset_filters(self): - self.update_games_allowed = False - for cb in self.checkboxes: - cb.setChecked(False) - self.ui.none_price.setChecked(True) - - self.tags = [] - self.types = [] - self.update_games_allowed = True - self.prepare_request("") - - self.ui.on_discount.setChecked(False) - - def prepare_request( - self, - price: str = None, - added_tag: int = 0, - removed_tag: int = 0, - added_type: str = "", - removed_type: str = "", - ): - if not self.update_games_allowed: - return - if price is not None: - self.price = price - - if added_tag != 0: - self.tags.append(added_tag) - if removed_tag != 0 and removed_tag in self.tags: - self.tags.remove(removed_tag) - - if added_type: - self.types.append(added_type) - if removed_type and removed_type in self.types: - self.types.remove(removed_type) - if (self.types or self.price) or self.tags or self.ui.on_discount.isChecked(): - self.ui.free_scrollarea.setVisible(False) - self.ui.discounts_group.setVisible(False) - else: - self.ui.free_scrollarea.setVisible(True) - if len(self.ui.discounts_group.layout().children()) > 0: - self.ui.discounts_group.setVisible(True) - - self.games_layout.setCurrentWidget(self.games_spinner) - - browse_model = SearchStoreQuery( - language=self.core.language_code, - country=self.core.country_code, - count=20, - price_range=self.price, - on_sale=self.ui.on_discount.isChecked(), - ) - browse_model.tag = "|".join(self.tags) - - if self.types: - browse_model.category = "|".join(self.types) - self.api_core.browse_games(browse_model, self.show_games) - - def show_games(self, data): - for w in self.games_flow.findChildren(QWidget, options=Qt.FindDirectChildrenOnly): - self.games_flow.layout().removeWidget(w) - w.deleteLater() - - if data: - for game in data: - w = GameWidget(self.api_core.cached_manager, game) - w.show_info.connect(self.show_game) - self.games_flow.layout().addWidget(w) - else: - self.games_flow.layout().addWidget( - QLabel(self.tr("Could not get games matching the filter")) - ) - self.games_layout.setCurrentWidget(self.games_flow) - # FIXME: FlowLayout doesn't update on adding widget - self.games_flow.layout().update() - - -class CheckBox(QCheckBox): - activated = pyqtSignal(str) - deactivated = pyqtSignal(str) - - def __init__(self, text, tag): - super(CheckBox, self).__init__(text) - self.tag = tag - - self.toggled.connect(self.handle_toggle) - - def handle_toggle(self): - if self.isChecked(): - self.activated.emit(self.tag) - else: - self.deactivated.emit(self.tag) diff --git a/rare/components/tabs/store/shop_api_core.py b/rare/components/tabs/store/store_api.py similarity index 89% rename from rare/components/tabs/store/shop_api_core.py rename to rare/components/tabs/store/store_api.py index 662df6d0..90ac2b7e 100644 --- a/rare/components/tabs/store/shop_api_core.py +++ b/rare/components/tabs/store/store_api.py @@ -11,12 +11,11 @@ from rare.components.tabs.store.constants import ( wishlist_add_query, wishlist_remove_query, ) -from rare.components.tabs.store.shop_models import BrowseModel from rare.utils.paths import cache_dir from rare.utils.qt_requests import QtRequests from .api.models.query import SearchStoreQuery +from .api.models.diesel import DieselProduct from .api.models.response import ( - DieselProduct, ResponseModel, CatalogOfferModel, ) @@ -28,11 +27,11 @@ graphql_url = "https://graphql.epicgames.com/graphql" DEBUG: Callable[[], bool] = lambda: "--debug" in QApplication.arguments() -class ShopApiCore(QObject): +class StoreAPI(QObject): update_wishlist = pyqtSignal() - def __init__(self, token, language: str, country: str): - super(ShopApiCore, self).__init__() + def __init__(self, token, language: str, country: str, installed): + super(StoreAPI, self).__init__() self.token = token self.language_code: str = language self.country_code: str = country @@ -42,6 +41,8 @@ class ShopApiCore(QObject): self.authed_manager = QtRequests(token=token, parent=self) self.cached_manager = QtRequests(cache=str(cache_dir().joinpath("store")), parent=self) + self.installed = installed + self.browse_active = False self.next_browse_request = tuple(()) @@ -153,13 +154,13 @@ class ShopApiCore(QObject): "query": search_query, "variables": browse_model.to_dict() } - debug = DebugDialog(payload["variables"], None) - debug.exec() + # debug = DebugDialog(payload["variables"], None) + # debug.exec() self.manager.post(graphql_url, lambda data: self.__handle_browse_games(data, handle_func), payload) def __handle_browse_games(self, data, handle_func): - debug = DebugDialog(data, None) - debug.exec() + # debug = DebugDialog(data, None) + # debug.exec() self.browse_active = False if data is None: data = {} @@ -182,15 +183,29 @@ class ShopApiCore(QObject): self.browse_games(*self.next_browse_request) # pylint: disable=E1120 self.next_browse_request = tuple(()) - def get_game(self, slug: str, is_bundle: bool, handle_func): + def get_game_config_graphql(self, namespace: str, handle_func): + payload = { + "query": config_query, + "variables": { + "namespace": namespace + } + } + + def __make_graphql_query(self): + pass + + def __make_api_query(self): + pass + + def get_game_config_cms(self, slug: str, is_bundle: bool, handle_func): url = "https://store-content.ak.epicgames.com/api" url += f"/{self.locale}/content/{'products' if not is_bundle else 'bundles'}/{slug}" self.manager.get(url, lambda data: self.__handle_get_game(data, handle_func)) @staticmethod def __handle_get_game(data, handle_func): - debug = DebugDialog(data, None) - debug.exec() + # debug = DebugDialog(data, None) + # debug.exec() try: product = DieselProduct.from_dict(data) handle_func(product) @@ -214,8 +229,8 @@ class ShopApiCore(QObject): self.authed_manager.post(graphql_url, lambda data: self._handle_add_to_wishlist(data, handle_func), payload) def _handle_add_to_wishlist(self, data, handle_func): - debug = DebugDialog(data, None) - debug.exec() + # debug = DebugDialog(data, None) + # debug.exec() try: response = ResponseModel.from_dict(data) data = response.data.wishlist.add_to_wishlist @@ -240,8 +255,8 @@ class ShopApiCore(QObject): payload) def _handle_remove_from_wishlist(self, data, handle_func): - debug = DebugDialog(data, None) - debug.exec() + # debug = DebugDialog(data, None) + # debug.exec() try: response = ResponseModel.from_dict(data) data = response.data.wishlist.remove_from_wishlist diff --git a/rare/components/tabs/store/widgets/__init__.py b/rare/components/tabs/store/widgets/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/rare/components/tabs/store/game_info.py b/rare/components/tabs/store/widgets/details.py similarity index 90% rename from rare/components/tabs/store/game_info.py rename to rare/components/tabs/store/widgets/details.py index 19a27524..07f8c84c 100644 --- a/rare/components/tabs/store/game_info.py +++ b/rare/components/tabs/store/widgets/details.py @@ -1,8 +1,7 @@ import logging -from pprint import pprint from typing import List -from PyQt5.QtCore import Qt, QUrl +from PyQt5.QtCore import Qt, QUrl, pyqtSignal from PyQt5.QtGui import QFont, QDesktopServices, QFontMetrics from PyQt5.QtWidgets import ( QWidget, @@ -12,38 +11,42 @@ from PyQt5.QtWidgets import ( QSizePolicy, ) -from rare.components.tabs.store.api.models.response import CatalogOfferModel, DieselProduct, DieselProductDetail -from rare.shared import LegendaryCoreSingleton -from rare.shared.image_manager import ImageSize -from rare.ui.components.tabs.store.shop_game_info import Ui_ShopInfo +from rare.components.tabs.store.api.debug import DebugDialog +from rare.components.tabs.store.api.models.diesel import DieselProduct, DieselProductDetail +from rare.components.tabs.store.api.models.response import CatalogOfferModel +from rare.models.image import ImageSize +from rare.ui.components.tabs.store.details import Ui_DetailsWidget from rare.utils.misc import icon -from rare.widgets.side_tab import SideTabWidget, SideTabContents from rare.widgets.elide_label import ElideLabel -from .api.debug import DebugDialog -from .image_widget import ShopImageWidget +from rare.widgets.side_tab import SideTabWidget, SideTabContents +from .image import LoadingImageWidget -logger = logging.getLogger("ShopInfo") +logger = logging.getLogger("StoreDetails") -class ShopGameInfo(QWidget, SideTabContents): +class DetailsWidget(QWidget, SideTabContents): + back_clicked: pyqtSignal = pyqtSignal() # TODO Design def __init__(self, installed_titles: list, api_core, parent=None): - super(ShopGameInfo, self).__init__(parent=parent) - self.ui = Ui_ShopInfo() + super(DetailsWidget, self).__init__(parent=parent) + self.ui = Ui_DetailsWidget() self.ui.setupUi(self) + self.ui.main_layout.setContentsMargins(0, 0, 3, 0) + # self.core = LegendaryCoreSingleton() self.api_core = api_core self.installed = installed_titles - self.ui.open_store_button.clicked.connect(self.button_clicked) - self.image = ShopImageWidget(api_core.cached_manager, self) - self.image.setFixedSize(ImageSize.Normal) - self.ui.left_layout.insertWidget(0, self.image, alignment=Qt.AlignTop) - self.offer: CatalogOfferModel = None self.data: dict = {} + self.image = LoadingImageWidget(api_core.cached_manager, self) + self.image.setFixedSize(ImageSize.Normal) + self.ui.left_layout.insertWidget(0, self.image, alignment=Qt.AlignTop) + self.ui.left_layout.setAlignment(Qt.AlignTop) + self.ui.wishlist_button.clicked.connect(self.add_to_wishlist) + self.ui.open_store_button.clicked.connect(self.button_clicked) self.ui.wishlist_button.setVisible(True) self.in_wishlist = False self.wishlist = [] @@ -52,7 +55,9 @@ class ShopGameInfo(QWidget, SideTabContents): self.requirements_tabs.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.ui.requirements_layout.addWidget(self.requirements_tabs) - self.setDisabled(True) + self.ui.back_button.clicked.connect(self.back_clicked) + + self.setDisabled(False) def handle_wishlist_update(self, wishlist: List[CatalogOfferModel]): if wishlist and wishlist[0] == "error": @@ -67,7 +72,6 @@ class ShopGameInfo(QWidget, SideTabContents): def update_game(self, offer: CatalogOfferModel): debug = DebugDialog(offer.__dict__, None) debug.exec() - self.set_title.emit(offer.title) self.ui.title.setText(offer.title) self.title_str = offer.title self.id_str = offer.id @@ -107,7 +111,7 @@ class ShopGameInfo(QWidget, SideTabContents): # init API request if slug: - self.api_core.get_game(offer.product_slug, is_bundle, self.data_received) + self.api_core.get_game_config_cms(offer.product_slug, is_bundle, self.data_received) # else: # self.data_received({}) self.offer = offer @@ -220,7 +224,7 @@ class ShopGameInfo(QWidget, SideTabContents): # self.image_stack.setCurrentIndex(0) about = product_data.about - self.ui.description_label.setText(about.desciption) + self.ui.description_label.setMarkdown(about.desciption) self.ui.dev.setText(about.developer_attribution) # try: # if isinstance(aboudeveloper, list): diff --git a/rare/components/tabs/store/widgets/groups.py b/rare/components/tabs/store/widgets/groups.py new file mode 100644 index 00000000..3675ecc7 --- /dev/null +++ b/rare/components/tabs/store/widgets/groups.py @@ -0,0 +1,18 @@ +from PyQt5.QtWidgets import QGroupBox, QLayout + +from rare.widgets.loading_widget import LoadingWidget + + +class StoreGroup(QGroupBox): + def __init__(self, title: str, layout: type[QLayout], parent=None): + super().__init__(parent=parent) + self.setTitle(title) + self.main_layout = layout(self) + self.loading_widget = LoadingWidget(autostart=True, parent=self) + + def loading(self, state: bool) -> None: + if state: + self.loading_widget.start() + else: + self.loading_widget.stop() + diff --git a/rare/components/tabs/store/image_widget.py b/rare/components/tabs/store/widgets/image.py similarity index 67% rename from rare/components/tabs/store/image_widget.py rename to rare/components/tabs/store/widgets/image.py index ec925943..22bd0140 100644 --- a/rare/components/tabs/store/image_widget.py +++ b/rare/components/tabs/store/widgets/image.py @@ -1,10 +1,7 @@ -from PyQt5.QtCore import QEvent, QObject from PyQt5.QtCore import Qt from PyQt5.QtGui import ( QPixmap, QImage, - QMovie, - QShowEvent, ) from PyQt5.QtWidgets import ( QWidget, @@ -15,53 +12,9 @@ from PyQt5.QtWidgets import ( QLabel, ) -from rare.utils.qt_requests import QtRequestManager +from rare.utils.qt_requests import QtRequests from rare.widgets.image_widget import ImageWidget - - -class WaitingSpinner(QLabel): - def __init__(self, autostart=False, parent=None): - super(WaitingSpinner, self).__init__(parent=parent) - self.setObjectName(type(self).__name__) - self.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter) - self.movie = QMovie(":/images/loader.gif", parent=self) - self.setFixedSize(128, 128) - self.setMovie(self.movie) - if self.parent() is not None: - self.parent().installEventFilter(self) - if autostart: - self.movie.start() - - def __center_on_parent(self): - rect = self.rect() - rect.moveCenter(self.parent().contentsRect().center()) - self.setGeometry(rect) - - def event(self, e: QEvent) -> bool: - if e.type() == QEvent.ParentAboutToChange: - if self.parent() is not None: - self.parent().removeEventFilter(self) - if e.type() == QEvent.ParentChange: - if self.parent() is not None: - self.parent().installEventFilter(self) - return super().event(e) - - def showEvent(self, a0: QShowEvent) -> None: - self.__center_on_parent() - - def eventFilter(self, a0: QObject, a1: QEvent) -> bool: - if a0 is self.parent() and a1.type() == QEvent.Resize: - self.__center_on_parent() - return a0.event(a1) - return False - - def start(self): - self.setVisible(True) - self.movie.start() - - def stop(self): - self.setVisible(False) - self.movie.stop() +from rare.widgets.loading_widget import LoadingWidget class IconWidget(object): @@ -76,7 +29,7 @@ class IconWidget(object): # on-hover popup self.mini_widget = QWidget(parent=widget) self.mini_widget.setObjectName(f"{type(self).__name__}MiniWidget") - self.mini_widget.setFixedHeight(widget.height() // 4) + self.mini_widget.setFixedHeight(int(widget.height() // 3)) # game title self.title_label = QLabel(parent=self.mini_widget) @@ -130,11 +83,11 @@ class IconWidget(object): widget.setLayout(image_layout) -class ShopImageWidget(ImageWidget): - def __init__(self, manager: QtRequestManager, parent=None): - super(ShopImageWidget, self).__init__(parent=parent) +class LoadingImageWidget(ImageWidget): + def __init__(self, manager: QtRequests, parent=None): + super(LoadingImageWidget, self).__init__(parent=parent) self.ui = IconWidget() - self.spinner = WaitingSpinner(parent=self) + self.spinner = LoadingWidget(parent=self) self.spinner.setVisible(False) self.manager = manager diff --git a/rare/components/tabs/store/game_widgets.py b/rare/components/tabs/store/widgets/items.py similarity index 64% rename from rare/components/tabs/store/game_widgets.py rename to rare/components/tabs/store/widgets/items.py index 9b453969..96427501 100644 --- a/rare/components/tabs/store/game_widgets.py +++ b/rare/components/tabs/store/widgets/items.py @@ -3,26 +3,40 @@ import logging from PyQt5.QtCore import pyqtSignal, Qt from PyQt5.QtGui import QMouseEvent from PyQt5.QtWidgets import QPushButton -from orjson import orjson +from rare.components.tabs.store.api.debug import DebugDialog from rare.components.tabs.store.api.models.response import CatalogOfferModel -from rare.shared.image_manager import ImageSize +from rare.models.image import ImageSize from rare.utils.misc import qta_icon -from rare.utils.qt_requests import QtRequestManager -from .api.debug import DebugDialog -from .image_widget import ShopImageWidget +from rare.utils.qt_requests import QtRequests +from .image import LoadingImageWidget logger = logging.getLogger("GameWidgets") -class GameWidget(ShopImageWidget): - show_info = pyqtSignal(CatalogOfferModel) +class ItemWidget(LoadingImageWidget): + show_details = pyqtSignal(CatalogOfferModel) - def __init__(self, manager: QtRequestManager, catalog_game: CatalogOfferModel = None, parent=None): - super(GameWidget, self).__init__(manager, parent=parent) + def __init__(self, manager: QtRequests, catalog_game: CatalogOfferModel = None, parent=None): + super(ItemWidget, self).__init__(manager, parent=parent) + self.catalog_game = catalog_game + + def mousePressEvent(self, a0: QMouseEvent) -> None: + if a0.button() == Qt.LeftButton: + a0.accept() + self.show_details.emit(self.catalog_game) + if a0.button() == Qt.RightButton: + a0.accept() + print(self.catalog_game.__dict__) + dialog = DebugDialog(self.catalog_game.__dict__, self) + dialog.show() + + +class StoreItemWidget(ItemWidget): + def __init__(self, manager: QtRequests, catalog_game: CatalogOfferModel = None, parent=None): + super(StoreItemWidget, self).__init__(manager, catalog_game, parent=parent) self.setFixedSize(ImageSize.Wide) self.ui.setupUi(self) - self.catalog_game = catalog_game if catalog_game: self.init_ui(catalog_game) @@ -62,26 +76,38 @@ class GameWidget(ShopImageWidget): # else: # logger.info(", ".join([img["type"] for img in json_info["keyImages"]])) - def mousePressEvent(self, a0: QMouseEvent) -> None: - if a0.button() == Qt.LeftButton: - a0.accept() - self.show_info.emit(self.catalog_game) - if a0.button() == Qt.RightButton: - a0.accept() - print(self.catalog_game.__dict__) - dialog = DebugDialog(self.catalog_game.__dict__, self) - dialog.show() + +class ResultsItemWidget(ItemWidget): + def __init__(self, manager: QtRequests, catalog_game: CatalogOfferModel, parent=None): + super(ResultsItemWidget, self).__init__(manager, catalog_game, parent=parent) + self.setFixedSize(ImageSize.Normal) + self.ui.setupUi(self) + + key_images = catalog_game.key_images + self.fetchPixmap(key_images.for_dimensions(self.width(), self.height()).url) + + self.ui.title_label.setText(catalog_game.title) + + price = catalog_game.price.total_price["fmtPrice"]["originalPrice"] + discount_price = catalog_game.price.total_price["fmtPrice"]["discountPrice"] + self.ui.price_label.setText(f'{price if price != "0" else self.tr("Free")}') + if price != discount_price: + font = self.ui.price_label.font() + font.setStrikeOut(True) + self.ui.price_label.setFont(font) + self.ui.discount_label.setText(f'{discount_price if discount_price != "0" else self.tr("Free")}') + else: + self.ui.discount_label.setVisible(False) -class WishlistWidget(ShopImageWidget): - open_game = pyqtSignal(CatalogOfferModel) +class WishlistItemWidget(ItemWidget): + show_details = pyqtSignal(CatalogOfferModel) delete_from_wishlist = pyqtSignal(CatalogOfferModel) - def __init__(self, manager: QtRequestManager, catalog_game: CatalogOfferModel, parent=None): - super(WishlistWidget, self).__init__(manager, parent=parent) + def __init__(self, manager: QtRequests, catalog_game: CatalogOfferModel, parent=None): + super(WishlistItemWidget, self).__init__(manager, catalog_game, parent=parent) self.setFixedSize(ImageSize.Wide) self.ui.setupUi(self) - self.game = catalog_game for attr in catalog_game.custom_attributes: if attr["key"] == "developerName": developer = attr["value"] @@ -109,17 +135,7 @@ class WishlistWidget(ShopImageWidget): self.delete_button = QPushButton(self) self.delete_button.setIcon(icon("mdi.delete", color="white")) self.delete_button.clicked.connect( - lambda: self.delete_from_wishlist.emit(self.game) + lambda: self.delete_from_wishlist.emit(self.catalog_game) ) self.layout().insertWidget(0, self.delete_button, alignment=Qt.AlignRight) - def mousePressEvent(self, a0: QMouseEvent) -> None: - # left button - if a0.button() == Qt.LeftButton: - a0.accept() - self.open_game.emit(self.game) - # right - if a0.button() == Qt.RightButton: - a0.accept() - dialog = DebugDialog(self.game.__dict__, self) - dialog.show() diff --git a/rare/components/tabs/store/wishlist.py b/rare/components/tabs/store/wishlist.py index b8fe7277..3d9305fd 100644 --- a/rare/components/tabs/store/wishlist.py +++ b/rare/components/tabs/store/wishlist.py @@ -1,27 +1,65 @@ from typing import List -from PyQt5.QtCore import pyqtSignal, Qt -from PyQt5.QtWidgets import QMessageBox, QWidget +from PyQt5.QtCore import pyqtSignal, Qt, pyqtSlot +from PyQt5.QtGui import QShowEvent +from PyQt5.QtWidgets import QMessageBox, QWidget, QScrollArea, QFrame, QSizePolicy from rare.ui.components.tabs.store.wishlist import Ui_Wishlist from rare.utils.misc import icon -from rare.widgets.side_tab import SideTabContents from rare.widgets.flow_layout import FlowLayout -from .shop_api_core import ShopApiCore -from .game_widgets import WishlistWidget +from rare.widgets.side_tab import SideTabContents +from rare.widgets.sliding_stack import SlidingStackedWidget from .api.models.response import WishlistItemModel, CatalogOfferModel +from .store_api import StoreAPI +from .widgets.details import DetailsWidget +from .widgets.items import WishlistItemWidget -class Wishlist(QWidget, SideTabContents): - show_game_info = pyqtSignal(CatalogOfferModel) +class WishlistPage(SlidingStackedWidget, SideTabContents): + def __init__(self, api: StoreAPI, parent=None): + super(WishlistPage, self).__init__(parent=parent) + self.implements_scrollarea = True + + self.wishlist_widget = WishlistWidget(api, parent=self) + self.wishlist_widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) + self.wishlist_widget.set_title.connect(self.set_title) + self.wishlist_widget.show_details.connect(self.show_details) + + self.details_widget = DetailsWidget([], api, parent=self) + self.details_widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) + self.details_widget.set_title.connect(self.set_title) + self.details_widget.back_clicked.connect(self.show_main) + + self.details_scroll = QScrollArea(self) + self.details_scroll.setWidgetResizable(True) + self.details_scroll.setFrameStyle(QFrame.NoFrame | QFrame.Plain) + self.details_scroll.setWidget(self.details_widget) + + self.setDirection(Qt.Horizontal) + self.addWidget(self.wishlist_widget) + self.addWidget(self.details_scroll) + + @pyqtSlot() + def show_main(self): + self.slideInWidget(self.wishlist_widget) + + @pyqtSlot(object) + def show_details(self, game: CatalogOfferModel): + self.details_widget.update_game(game) + self.slideInWidget(self.details_scroll) + + +class WishlistWidget(QWidget, SideTabContents): + show_details = pyqtSignal(CatalogOfferModel) update_wishlist_signal = pyqtSignal() - def __init__(self, api_core: ShopApiCore, parent=None): - super(Wishlist, self).__init__(parent=parent) + def __init__(self, api: StoreAPI, parent=None): + super(WishlistWidget, self).__init__(parent=parent) self.implements_scrollarea = True - self.api_core = api_core + self.api = api self.ui = Ui_Wishlist() self.ui.setupUi(self) + self.ui.main_layout.setContentsMargins(0, 0, 3, 0) self.setEnabled(False) self.wishlist = [] self.widgets = [] @@ -37,12 +75,16 @@ class Wishlist(QWidget, SideTabContents): lambda: self.sort_wishlist(sort=self.ui.sort_cb.currentIndex()) ) + def showEvent(self, a0: QShowEvent) -> None: + self.update_wishlist() + return super().showEvent(a0) + def update_wishlist(self): self.setEnabled(False) - self.api_core.get_wishlist(self.set_wishlist) + self.api.get_wishlist(self.set_wishlist) def delete_from_wishlist(self, game: CatalogOfferModel): - self.api_core.remove_from_wishlist( + self.api.remove_from_wishlist( game.namespace, game.id, lambda success: self.update_wishlist() @@ -71,7 +113,7 @@ class Wishlist(QWidget, SideTabContents): self.ui.no_games_label.setVisible(False) def sort_wishlist(self, sort=0): - widgets = self.ui.list_container.findChildren(WishlistWidget, options=Qt.FindDirectChildrenOnly) + widgets = self.ui.list_container.findChildren(WishlistItemWidget, options=Qt.FindDirectChildrenOnly) for w in widgets: self.ui.list_container.layout().removeWidget(w) @@ -113,8 +155,8 @@ class Wishlist(QWidget, SideTabContents): self.ui.no_games_label.setVisible(False) for game in wishlist: - w = WishlistWidget(self.api_core.cached_manager, game.offer, self.ui.list_container) - w.open_game.connect(self.show_game_info) + w = WishlistItemWidget(self.api.cached_manager, game.offer, self.ui.list_container) + w.show_details.connect(self.show_details) w.delete_from_wishlist.connect(self.delete_from_wishlist) self.widgets.append(w) self.list_layout.addWidget(w) diff --git a/rare/ui/components/tabs/store/shop_game_info.py b/rare/ui/components/tabs/store/details.py similarity index 69% rename from rare/ui/components/tabs/store/shop_game_info.py rename to rare/ui/components/tabs/store/details.py index e827d9d5..e547df2f 100644 --- a/rare/ui/components/tabs/store/shop_game_info.py +++ b/rare/ui/components/tabs/store/details.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- -# Form implementation generated from reading ui file 'rare/ui/components/tabs/store/shop_game_info.ui' +# Form implementation generated from reading ui file 'rare/ui/components/tabs/store/details.ui' # -# Created by: PyQt5 UI code generator 5.15.9 +# Created by: PyQt5 UI code generator 5.15.10 # # WARNING: Any manual changes made to this file will be lost when pyuic5 is # run again. Do not edit this file unless you know what you are doing. @@ -11,24 +11,29 @@ from PyQt5 import QtCore, QtGui, QtWidgets -class Ui_ShopInfo(object): - def setupUi(self, ShopInfo): - ShopInfo.setObjectName("ShopInfo") - ShopInfo.resize(443, 347) - ShopInfo.setWindowTitle("ShopGameInfo") - self.main_layout = QtWidgets.QHBoxLayout(ShopInfo) +class Ui_DetailsWidget(object): + def setupUi(self, DetailsWidget): + DetailsWidget.setObjectName("DetailsWidget") + DetailsWidget.resize(414, 343) + DetailsWidget.setWindowTitle("DetailsWidget") + self.main_layout = QtWidgets.QHBoxLayout(DetailsWidget) self.main_layout.setObjectName("main_layout") self.left_layout = QtWidgets.QVBoxLayout() self.left_layout.setObjectName("left_layout") + self.back_button = QtWidgets.QPushButton(DetailsWidget) + self.back_button.setText("") + self.back_button.setObjectName("back_button") + self.left_layout.addWidget(self.back_button) self.main_layout.addLayout(self.left_layout) self.right_layout = QtWidgets.QVBoxLayout() self.right_layout.setObjectName("right_layout") self.info_layout = QtWidgets.QFormLayout() + self.info_layout.setSizeConstraint(QtWidgets.QLayout.SetFixedSize) self.info_layout.setLabelAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) self.info_layout.setContentsMargins(6, 6, 6, 6) self.info_layout.setSpacing(12) self.info_layout.setObjectName("info_layout") - self.title_label = QtWidgets.QLabel(ShopInfo) + self.title_label = QtWidgets.QLabel(DetailsWidget) font = QtGui.QFont() font.setBold(True) font.setWeight(75) @@ -36,12 +41,12 @@ class Ui_ShopInfo(object): self.title_label.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) self.title_label.setObjectName("title_label") self.info_layout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.title_label) - self.title = QtWidgets.QLabel(ShopInfo) + self.title = QtWidgets.QLabel(DetailsWidget) self.title.setText("title") self.title.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse|QtCore.Qt.TextSelectableByMouse) self.title.setObjectName("title") self.info_layout.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.title) - self.developer_label = QtWidgets.QLabel(ShopInfo) + self.developer_label = QtWidgets.QLabel(DetailsWidget) font = QtGui.QFont() font.setBold(True) font.setWeight(75) @@ -49,12 +54,12 @@ class Ui_ShopInfo(object): self.developer_label.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) self.developer_label.setObjectName("developer_label") self.info_layout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.developer_label) - self.dev = QtWidgets.QLabel(ShopInfo) + self.dev = QtWidgets.QLabel(DetailsWidget) self.dev.setText("dev") self.dev.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse|QtCore.Qt.TextSelectableByMouse) self.dev.setObjectName("dev") self.info_layout.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.dev) - self.status_label = QtWidgets.QLabel(ShopInfo) + self.status_label = QtWidgets.QLabel(DetailsWidget) font = QtGui.QFont() font.setBold(True) font.setWeight(75) @@ -62,10 +67,10 @@ class Ui_ShopInfo(object): self.status_label.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) self.status_label.setObjectName("status_label") self.info_layout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.status_label) - self.owned_label = QtWidgets.QLabel(ShopInfo) + self.owned_label = QtWidgets.QLabel(DetailsWidget) self.owned_label.setObjectName("owned_label") self.info_layout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.owned_label) - self.price_label = QtWidgets.QLabel(ShopInfo) + self.price_label = QtWidgets.QLabel(DetailsWidget) font = QtGui.QFont() font.setBold(True) font.setWeight(75) @@ -73,7 +78,7 @@ class Ui_ShopInfo(object): self.price_label.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) self.price_label.setObjectName("price_label") self.info_layout.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.price_label) - self.price_widget = QtWidgets.QWidget(ShopInfo) + self.price_widget = QtWidgets.QWidget(DetailsWidget) self.price_widget.setObjectName("price_widget") self.horizontalLayout = QtWidgets.QHBoxLayout(self.price_widget) self.horizontalLayout.setSizeConstraint(QtWidgets.QLayout.SetFixedSize) @@ -89,7 +94,7 @@ class Ui_ShopInfo(object): self.discount_price.setObjectName("discount_price") self.horizontalLayout.addWidget(self.discount_price) self.info_layout.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.price_widget) - self.tags_label = QtWidgets.QLabel(ShopInfo) + self.tags_label = QtWidgets.QLabel(DetailsWidget) font = QtGui.QFont() font.setBold(True) font.setWeight(75) @@ -97,11 +102,11 @@ class Ui_ShopInfo(object): self.tags_label.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) self.tags_label.setObjectName("tags_label") self.info_layout.setWidget(4, QtWidgets.QFormLayout.LabelRole, self.tags_label) - self.tags = QtWidgets.QLabel(ShopInfo) + self.tags = QtWidgets.QLabel(DetailsWidget) self.tags.setText("tags") self.tags.setObjectName("tags") self.info_layout.setWidget(4, QtWidgets.QFormLayout.FieldRole, self.tags) - self.links_label = QtWidgets.QLabel(ShopInfo) + self.links_label = QtWidgets.QLabel(DetailsWidget) font = QtGui.QFont() font.setBold(True) font.setWeight(75) @@ -109,7 +114,7 @@ class Ui_ShopInfo(object): self.links_label.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) self.links_label.setObjectName("links_label") self.info_layout.setWidget(5, QtWidgets.QFormLayout.LabelRole, self.links_label) - self.social_group = QtWidgets.QWidget(ShopInfo) + self.social_group = QtWidgets.QWidget(DetailsWidget) self.social_group.setMinimumSize(QtCore.QSize(250, 0)) self.social_group.setObjectName("social_group") self.social_layout = QtWidgets.QHBoxLayout(self.social_group) @@ -117,7 +122,7 @@ class Ui_ShopInfo(object): self.social_layout.setContentsMargins(0, 0, 0, 0) self.social_layout.setObjectName("social_layout") self.info_layout.setWidget(5, QtWidgets.QFormLayout.FieldRole, self.social_group) - self.actions_label = QtWidgets.QLabel(ShopInfo) + self.actions_label = QtWidgets.QLabel(DetailsWidget) font = QtGui.QFont() font.setBold(True) font.setWeight(75) @@ -125,7 +130,7 @@ class Ui_ShopInfo(object): self.actions_label.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) self.actions_label.setObjectName("actions_label") self.info_layout.setWidget(6, QtWidgets.QFormLayout.LabelRole, self.actions_label) - self.buttons_widget = QtWidgets.QWidget(ShopInfo) + self.buttons_widget = QtWidgets.QWidget(DetailsWidget) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) @@ -146,7 +151,7 @@ class Ui_ShopInfo(object): self.button_layout.addWidget(self.wishlist_button) self.info_layout.setWidget(6, QtWidgets.QFormLayout.FieldRole, self.buttons_widget) self.right_layout.addLayout(self.info_layout) - self.requirements_frame = QtWidgets.QFrame(ShopInfo) + self.requirements_frame = QtWidgets.QFrame(DetailsWidget) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) @@ -159,55 +164,34 @@ class Ui_ShopInfo(object): self.requirements_layout.setContentsMargins(0, 0, 0, 0) self.requirements_layout.setObjectName("requirements_layout") self.right_layout.addWidget(self.requirements_frame) - self.description_group = QtWidgets.QGroupBox(ShopInfo) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.description_group.sizePolicy().hasHeightForWidth()) - self.description_group.setSizePolicy(sizePolicy) - self.description_group.setFlat(False) - self.description_group.setObjectName("description_group") - self.description_layout = QtWidgets.QVBoxLayout(self.description_group) - self.description_layout.setObjectName("description_layout") - self.description_label = QtWidgets.QLabel(self.description_group) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.description_label.sizePolicy().hasHeightForWidth()) - self.description_label.setSizePolicy(sizePolicy) - self.description_label.setText("error") - self.description_label.setTextFormat(QtCore.Qt.MarkdownText) - self.description_label.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignTop) - self.description_label.setWordWrap(True) + self.description_label = QtWidgets.QTextBrowser(DetailsWidget) self.description_label.setOpenExternalLinks(True) self.description_label.setObjectName("description_label") - self.description_layout.addWidget(self.description_label) - self.right_layout.addWidget(self.description_group) + self.right_layout.addWidget(self.description_label) self.main_layout.addLayout(self.right_layout) self.main_layout.setStretch(1, 1) - self.retranslateUi(ShopInfo) + self.retranslateUi(DetailsWidget) - def retranslateUi(self, ShopInfo): + def retranslateUi(self, DetailsWidget): _translate = QtCore.QCoreApplication.translate - self.title_label.setText(_translate("ShopInfo", "Title")) - self.developer_label.setText(_translate("ShopInfo", "Developer")) - self.status_label.setText(_translate("ShopInfo", "Status")) - self.owned_label.setText(_translate("ShopInfo", "You already own this game")) - self.price_label.setText(_translate("ShopInfo", "Price")) - self.tags_label.setText(_translate("ShopInfo", "Tags")) - self.links_label.setText(_translate("ShopInfo", "Links")) - self.actions_label.setText(_translate("ShopInfo", "Actions")) - self.open_store_button.setText(_translate("ShopInfo", "Buy in Epic Games Store")) - self.wishlist_button.setText(_translate("ShopInfo", "Add to wishlist")) - self.description_group.setTitle(_translate("ShopInfo", "Description")) + self.title_label.setText(_translate("DetailsWidget", "Title")) + self.developer_label.setText(_translate("DetailsWidget", "Developer")) + self.status_label.setText(_translate("DetailsWidget", "Status")) + self.owned_label.setText(_translate("DetailsWidget", "You already own this game")) + self.price_label.setText(_translate("DetailsWidget", "Price")) + self.tags_label.setText(_translate("DetailsWidget", "Tags")) + self.links_label.setText(_translate("DetailsWidget", "Links")) + self.actions_label.setText(_translate("DetailsWidget", "Actions")) + self.open_store_button.setText(_translate("DetailsWidget", "Buy in Epic Games Store")) + self.wishlist_button.setText(_translate("DetailsWidget", "Add to wishlist")) if __name__ == "__main__": import sys app = QtWidgets.QApplication(sys.argv) - ShopInfo = QtWidgets.QWidget() - ui = Ui_ShopInfo() - ui.setupUi(ShopInfo) - ShopInfo.show() + DetailsWidget = QtWidgets.QWidget() + ui = Ui_DetailsWidget() + ui.setupUi(DetailsWidget) + DetailsWidget.show() sys.exit(app.exec_()) diff --git a/rare/ui/components/tabs/store/shop_game_info.ui b/rare/ui/components/tabs/store/details.ui similarity index 87% rename from rare/ui/components/tabs/store/shop_game_info.ui rename to rare/ui/components/tabs/store/details.ui index 9813c0cb..af07ca9f 100644 --- a/rare/ui/components/tabs/store/shop_game_info.ui +++ b/rare/ui/components/tabs/store/details.ui @@ -1,26 +1,37 @@ - ShopInfo - + DetailsWidget + 0 0 - 443 - 347 + 414 + 343 - ShopGameInfo + DetailsWidget - + + + + + + + + + + + QLayout::SetFixedSize + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -339,46 +350,10 @@ - - - - 0 - 0 - + + + true - - Description - - - false - - - - - - - 0 - 0 - - - - error - - - Qt::MarkdownText - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - true - - - true - - - - diff --git a/rare/ui/components/tabs/store/store.py b/rare/ui/components/tabs/store/landing.py similarity index 51% rename from rare/ui/components/tabs/store/store.py rename to rare/ui/components/tabs/store/landing.py index 14ea03df..c2aa0099 100644 --- a/rare/ui/components/tabs/store/store.py +++ b/rare/ui/components/tabs/store/landing.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- -# Form implementation generated from reading ui file 'rare/ui/components/tabs/store/store.ui' +# Form implementation generated from reading ui file 'rare/ui/components/tabs/store/landing.ui' # -# Created by: PyQt5 UI code generator 5.15.9 +# Created by: PyQt5 UI code generator 5.15.10 # # WARNING: Any manual changes made to this file will be lost when pyuic5 is # run again. Do not edit this file unless you know what you are doing. @@ -11,14 +11,22 @@ from PyQt5 import QtCore, QtGui, QtWidgets -class Ui_ShopWidget(object): - def setupUi(self, ShopWidget): - ShopWidget.setObjectName("ShopWidget") - ShopWidget.resize(788, 662) - ShopWidget.setWindowTitle("Store") - self.main_layout = QtWidgets.QGridLayout(ShopWidget) +class Ui_LandingWidget(object): + def setupUi(self, LandingWidget): + LandingWidget.setObjectName("LandingWidget") + LandingWidget.resize(788, 662) + LandingWidget.setWindowTitle("LandingWidget") + self.main_layout = QtWidgets.QHBoxLayout(LandingWidget) self.main_layout.setObjectName("main_layout") - self.filter_scrollarea = QtWidgets.QScrollArea(ShopWidget) + self.left_layout = QtWidgets.QVBoxLayout() + self.left_layout.setObjectName("left_layout") + self.main_layout.addLayout(self.left_layout) + self.right_layout = QtWidgets.QVBoxLayout() + self.right_layout.setObjectName("right_layout") + self.reset_button = QtWidgets.QPushButton(LandingWidget) + self.reset_button.setObjectName("reset_button") + self.right_layout.addWidget(self.reset_button) + self.filter_scrollarea = QtWidgets.QScrollArea(LandingWidget) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Expanding) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) @@ -89,81 +97,34 @@ class Ui_ShopWidget(object): self.others_layout.setObjectName("others_layout") self.filter_container_layout.addWidget(self.others_group) self.filter_scrollarea.setWidget(self.filter_container) - self.main_layout.addWidget(self.filter_scrollarea, 1, 1, 1, 1) - self.reset_button = QtWidgets.QPushButton(ShopWidget) - self.reset_button.setObjectName("reset_button") - self.main_layout.addWidget(self.reset_button, 0, 1, 1, 1) - self.games_scrollarea = QtWidgets.QScrollArea(ShopWidget) - self.games_scrollarea.setFrameShape(QtWidgets.QFrame.NoFrame) - self.games_scrollarea.setFrameShadow(QtWidgets.QFrame.Plain) - self.games_scrollarea.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents) - self.games_scrollarea.setWidgetResizable(True) - self.games_scrollarea.setObjectName("games_scrollarea") - self.games_container = QtWidgets.QWidget() - self.games_container.setGeometry(QtCore.QRect(0, 0, 628, 618)) - self.games_container.setObjectName("games_container") - self.games_container_layout = QtWidgets.QVBoxLayout(self.games_container) - self.games_container_layout.setContentsMargins(0, 0, 3, 0) - self.games_container_layout.setObjectName("games_container_layout") - self.free_scrollarea = QtWidgets.QScrollArea(self.games_container) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.free_scrollarea.sizePolicy().hasHeightForWidth()) - self.free_scrollarea.setSizePolicy(sizePolicy) - self.free_scrollarea.setFrameShape(QtWidgets.QFrame.NoFrame) - self.free_scrollarea.setFrameShadow(QtWidgets.QFrame.Plain) - self.free_scrollarea.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) - self.free_scrollarea.setWidgetResizable(True) - self.free_scrollarea.setObjectName("free_scrollarea") - self.free_container = QtWidgets.QWidget() - self.free_container.setGeometry(QtCore.QRect(0, 0, 16, 16)) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.free_container.sizePolicy().hasHeightForWidth()) - self.free_container.setSizePolicy(sizePolicy) - self.free_container.setObjectName("free_container") - self.free_container_layout = QtWidgets.QHBoxLayout(self.free_container) - self.free_container_layout.setContentsMargins(0, 0, 0, 3) - self.free_container_layout.setObjectName("free_container_layout") - self.free_scrollarea.setWidget(self.free_container) - self.games_container_layout.addWidget(self.free_scrollarea) - self.discounts_group = QtWidgets.QGroupBox(self.games_container) - self.discounts_group.setObjectName("discounts_group") - self.games_container_layout.addWidget(self.discounts_group) - self.games_group = QtWidgets.QGroupBox(self.games_container) - self.games_group.setObjectName("games_group") - self.games_container_layout.addWidget(self.games_group) - self.games_scrollarea.setWidget(self.games_container) - self.main_layout.addWidget(self.games_scrollarea, 1, 0, 1, 1) + self.right_layout.addWidget(self.filter_scrollarea) + self.main_layout.addLayout(self.right_layout) + self.main_layout.setStretch(0, 1) - self.retranslateUi(ShopWidget) + self.retranslateUi(LandingWidget) - def retranslateUi(self, ShopWidget): + def retranslateUi(self, LandingWidget): _translate = QtCore.QCoreApplication.translate - self.price_group.setTitle(_translate("ShopWidget", "Price")) - self.none_price.setText(_translate("ShopWidget", "None")) - self.free_button.setText(_translate("ShopWidget", "Free")) - self.under10.setText(_translate("ShopWidget", "Under 10")) - self.under20.setText(_translate("ShopWidget", "Under 20")) - self.under30.setText(_translate("ShopWidget", "Under 30")) - self.above.setText(_translate("ShopWidget", "14.99 and above")) - self.on_discount.setText(_translate("ShopWidget", "Discount")) - self.platform_group.setTitle(_translate("ShopWidget", "Platform")) - self.genre_group.setTitle(_translate("ShopWidget", "Genre")) - self.type_group.setTitle(_translate("ShopWidget", "Type")) - self.others_group.setTitle(_translate("ShopWidget", "Other tags")) - self.reset_button.setText(_translate("ShopWidget", "Reset filters")) - self.discounts_group.setTitle(_translate("ShopWidget", "Discounts from your wishlist")) - self.games_group.setTitle(_translate("ShopWidget", "Games")) + self.reset_button.setText(_translate("LandingWidget", "Reset filters")) + self.price_group.setTitle(_translate("LandingWidget", "Price")) + self.none_price.setText(_translate("LandingWidget", "None")) + self.free_button.setText(_translate("LandingWidget", "Free")) + self.under10.setText(_translate("LandingWidget", "Under 10")) + self.under20.setText(_translate("LandingWidget", "Under 20")) + self.under30.setText(_translate("LandingWidget", "Under 30")) + self.above.setText(_translate("LandingWidget", "14.99 and above")) + self.on_discount.setText(_translate("LandingWidget", "Discount")) + self.platform_group.setTitle(_translate("LandingWidget", "Platform")) + self.genre_group.setTitle(_translate("LandingWidget", "Genre")) + self.type_group.setTitle(_translate("LandingWidget", "Type")) + self.others_group.setTitle(_translate("LandingWidget", "Other tags")) if __name__ == "__main__": import sys app = QtWidgets.QApplication(sys.argv) - ShopWidget = QtWidgets.QWidget() - ui = Ui_ShopWidget() - ui.setupUi(ShopWidget) - ShopWidget.show() + LandingWidget = QtWidgets.QWidget() + ui = Ui_LandingWidget() + ui.setupUi(LandingWidget) + LandingWidget.show() sys.exit(app.exec_()) diff --git a/rare/ui/components/tabs/store/landing.ui b/rare/ui/components/tabs/store/landing.ui new file mode 100644 index 00000000..2e3b75b4 --- /dev/null +++ b/rare/ui/components/tabs/store/landing.ui @@ -0,0 +1,183 @@ + + + LandingWidget + + + + 0 + 0 + 788 + 662 + + + + LandingWidget + + + + + + + + + + + Reset filters + + + + + + + + 0 + 0 + + + + QFrame::NoFrame + + + QFrame::Plain + + + Qt::ScrollBarAlwaysOff + + + QAbstractScrollArea::AdjustToContents + + + true + + + + + 0 + 0 + 142 + 390 + + + + + 0 + 0 + + + + + 0 + + + 0 + + + 3 + + + 0 + + + + + Price + + + + + + None + + + true + + + + + + + Free + + + + + + + Under 10 + + + + + + + Under 20 + + + + + + + Under 30 + + + + + + + 14.99 and above + + + + + + + Discount + + + + + + + + + + Platform + + + + + + + + Genre + + + + + + + + Type + + + + + + + + Other tags + + + + + + + + + + + + + + + diff --git a/rare/ui/components/tabs/store/search.py b/rare/ui/components/tabs/store/search.py new file mode 100644 index 00000000..d7b78c95 --- /dev/null +++ b/rare/ui/components/tabs/store/search.py @@ -0,0 +1,130 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'rare/ui/components/tabs/store/search.ui' +# +# Created by: PyQt5 UI code generator 5.15.10 +# +# WARNING: Any manual changes made to this file will be lost when pyuic5 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt5 import QtCore, QtGui, QtWidgets + + +class Ui_SearchWidget(object): + def setupUi(self, SearchWidget): + SearchWidget.setObjectName("SearchWidget") + SearchWidget.resize(491, 382) + SearchWidget.setWindowTitle("SearchWidget") + self.main_layout = QtWidgets.QHBoxLayout(SearchWidget) + self.main_layout.setObjectName("main_layout") + self.left_layout = QtWidgets.QVBoxLayout() + self.left_layout.setObjectName("left_layout") + self.main_layout.addLayout(self.left_layout) + self.right_layout = QtWidgets.QVBoxLayout() + self.right_layout.setObjectName("right_layout") + self.reset_button = QtWidgets.QPushButton(SearchWidget) + self.reset_button.setObjectName("reset_button") + self.right_layout.addWidget(self.reset_button) + self.filter_scrollarea = QtWidgets.QScrollArea(SearchWidget) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.filter_scrollarea.sizePolicy().hasHeightForWidth()) + self.filter_scrollarea.setSizePolicy(sizePolicy) + self.filter_scrollarea.setFrameShape(QtWidgets.QFrame.NoFrame) + self.filter_scrollarea.setFrameShadow(QtWidgets.QFrame.Plain) + self.filter_scrollarea.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) + self.filter_scrollarea.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents) + self.filter_scrollarea.setWidgetResizable(True) + self.filter_scrollarea.setObjectName("filter_scrollarea") + self.filter_container = QtWidgets.QWidget() + self.filter_container.setGeometry(QtCore.QRect(0, 0, 142, 390)) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.filter_container.sizePolicy().hasHeightForWidth()) + self.filter_container.setSizePolicy(sizePolicy) + self.filter_container.setObjectName("filter_container") + self.filter_container_layout = QtWidgets.QVBoxLayout(self.filter_container) + self.filter_container_layout.setContentsMargins(0, 0, 3, 0) + self.filter_container_layout.setObjectName("filter_container_layout") + self.price_group = QtWidgets.QGroupBox(self.filter_container) + self.price_group.setObjectName("price_group") + self.price_layout = QtWidgets.QVBoxLayout(self.price_group) + self.price_layout.setObjectName("price_layout") + self.none_price = QtWidgets.QRadioButton(self.price_group) + self.none_price.setChecked(True) + self.none_price.setObjectName("none_price") + self.price_layout.addWidget(self.none_price) + self.free_button = QtWidgets.QRadioButton(self.price_group) + self.free_button.setObjectName("free_button") + self.price_layout.addWidget(self.free_button) + self.under10 = QtWidgets.QRadioButton(self.price_group) + self.under10.setObjectName("under10") + self.price_layout.addWidget(self.under10) + self.under20 = QtWidgets.QRadioButton(self.price_group) + self.under20.setObjectName("under20") + self.price_layout.addWidget(self.under20) + self.under30 = QtWidgets.QRadioButton(self.price_group) + self.under30.setObjectName("under30") + self.price_layout.addWidget(self.under30) + self.above = QtWidgets.QRadioButton(self.price_group) + self.above.setObjectName("above") + self.price_layout.addWidget(self.above) + self.on_discount = QtWidgets.QCheckBox(self.price_group) + self.on_discount.setObjectName("on_discount") + self.price_layout.addWidget(self.on_discount) + self.filter_container_layout.addWidget(self.price_group) + self.platform_group = QtWidgets.QGroupBox(self.filter_container) + self.platform_group.setObjectName("platform_group") + self.platfrom_layout = QtWidgets.QVBoxLayout(self.platform_group) + self.platfrom_layout.setObjectName("platfrom_layout") + self.filter_container_layout.addWidget(self.platform_group) + self.genre_group = QtWidgets.QGroupBox(self.filter_container) + self.genre_group.setObjectName("genre_group") + self.genre_layout = QtWidgets.QVBoxLayout(self.genre_group) + self.genre_layout.setObjectName("genre_layout") + self.filter_container_layout.addWidget(self.genre_group) + self.type_group = QtWidgets.QGroupBox(self.filter_container) + self.type_group.setObjectName("type_group") + self.type_layout = QtWidgets.QVBoxLayout(self.type_group) + self.type_layout.setObjectName("type_layout") + self.filter_container_layout.addWidget(self.type_group) + self.others_group = QtWidgets.QGroupBox(self.filter_container) + self.others_group.setObjectName("others_group") + self.others_layout = QtWidgets.QVBoxLayout(self.others_group) + self.others_layout.setObjectName("others_layout") + self.filter_container_layout.addWidget(self.others_group) + self.filter_scrollarea.setWidget(self.filter_container) + self.right_layout.addWidget(self.filter_scrollarea) + self.main_layout.addLayout(self.right_layout) + self.main_layout.setStretch(0, 1) + + self.retranslateUi(SearchWidget) + + def retranslateUi(self, SearchWidget): + _translate = QtCore.QCoreApplication.translate + self.reset_button.setText(_translate("SearchWidget", "Reset filters")) + self.price_group.setTitle(_translate("SearchWidget", "Price")) + self.none_price.setText(_translate("SearchWidget", "None")) + self.free_button.setText(_translate("SearchWidget", "Free")) + self.under10.setText(_translate("SearchWidget", "Under 10")) + self.under20.setText(_translate("SearchWidget", "Under 20")) + self.under30.setText(_translate("SearchWidget", "Under 30")) + self.above.setText(_translate("SearchWidget", "14.99 and above")) + self.on_discount.setText(_translate("SearchWidget", "Discount")) + self.platform_group.setTitle(_translate("SearchWidget", "Platform")) + self.genre_group.setTitle(_translate("SearchWidget", "Genre")) + self.type_group.setTitle(_translate("SearchWidget", "Type")) + self.others_group.setTitle(_translate("SearchWidget", "Other tags")) + + +if __name__ == "__main__": + import sys + app = QtWidgets.QApplication(sys.argv) + SearchWidget = QtWidgets.QWidget() + ui = Ui_SearchWidget() + ui.setupUi(SearchWidget) + SearchWidget.show() + sys.exit(app.exec_()) diff --git a/rare/ui/components/tabs/store/search.ui b/rare/ui/components/tabs/store/search.ui new file mode 100644 index 00000000..516433f4 --- /dev/null +++ b/rare/ui/components/tabs/store/search.ui @@ -0,0 +1,183 @@ + + + SearchWidget + + + + 0 + 0 + 491 + 382 + + + + SearchWidget + + + + + + + + + + + Reset filters + + + + + + + + 0 + 0 + + + + QFrame::NoFrame + + + QFrame::Plain + + + Qt::ScrollBarAlwaysOff + + + QAbstractScrollArea::AdjustToContents + + + true + + + + + 0 + 0 + 142 + 390 + + + + + 0 + 0 + + + + + 0 + + + 0 + + + 3 + + + 0 + + + + + Price + + + + + + None + + + true + + + + + + + Free + + + + + + + Under 10 + + + + + + + Under 20 + + + + + + + Under 30 + + + + + + + 14.99 and above + + + + + + + Discount + + + + + + + + + + Platform + + + + + + + + Genre + + + + + + + + Type + + + + + + + + Other tags + + + + + + + + + + + + + + + diff --git a/rare/ui/components/tabs/store/store.ui b/rare/ui/components/tabs/store/store.ui deleted file mode 100644 index c46a8bf1..00000000 --- a/rare/ui/components/tabs/store/store.ui +++ /dev/null @@ -1,282 +0,0 @@ - - - ShopWidget - - - - 0 - 0 - 788 - 662 - - - - Store - - - - - - - 0 - 0 - - - - QFrame::NoFrame - - - QFrame::Plain - - - Qt::ScrollBarAlwaysOff - - - QAbstractScrollArea::AdjustToContents - - - true - - - - - 0 - 0 - 142 - 390 - - - - - 0 - 0 - - - - - 0 - - - 0 - - - 3 - - - 0 - - - - - Price - - - - - - None - - - true - - - - - - - Free - - - - - - - Under 10 - - - - - - - Under 20 - - - - - - - Under 30 - - - - - - - 14.99 and above - - - - - - - Discount - - - - - - - - - - Platform - - - - - - - - Genre - - - - - - - - Type - - - - - - - - Other tags - - - - - - - - - - - - Reset filters - - - - - - - QFrame::NoFrame - - - QFrame::Plain - - - QAbstractScrollArea::AdjustToContents - - - true - - - - - 0 - 0 - 628 - 618 - - - - - 0 - - - 0 - - - 3 - - - 0 - - - - - - 0 - 0 - - - - QFrame::NoFrame - - - QFrame::Plain - - - Qt::ScrollBarAlwaysOff - - - true - - - - - 0 - 0 - 16 - 16 - - - - - 0 - 0 - - - - - 0 - - - 0 - - - 0 - - - 3 - - - - - - - - - Discounts from your wishlist - - - - - - - Games - - - - - - - - - - - - diff --git a/rare/ui/components/tabs/store/widgets/__init__.py b/rare/ui/components/tabs/store/widgets/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/rare/ui/components/tabs/store/wishlist_widget.py b/rare/ui/components/tabs/store/widgets/wishlist_widget.py similarity index 97% rename from rare/ui/components/tabs/store/wishlist_widget.py rename to rare/ui/components/tabs/store/widgets/wishlist_widget.py index 7ea93faf..57fb2850 100644 --- a/rare/ui/components/tabs/store/wishlist_widget.py +++ b/rare/ui/components/tabs/store/widgets/wishlist_widget.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- -# Form implementation generated from reading ui file 'rare/ui/components/tabs/store/wishlist_widget.ui' +# Form implementation generated from reading ui file 'rare/ui/components/tabs/store/widgets/wishlist_widget.ui' # -# Created by: PyQt5 UI code generator 5.15.9 +# Created by: PyQt5 UI code generator 5.15.10 # # WARNING: Any manual changes made to this file will be lost when pyuic5 is # run again. Do not edit this file unless you know what you are doing. diff --git a/rare/ui/components/tabs/store/wishlist_widget.ui b/rare/ui/components/tabs/store/widgets/wishlist_widget.ui similarity index 100% rename from rare/ui/components/tabs/store/wishlist_widget.ui rename to rare/ui/components/tabs/store/widgets/wishlist_widget.ui diff --git a/rare/ui/components/tabs/store/wishlist.py b/rare/ui/components/tabs/store/wishlist.py index 66c042f6..2bea5fa8 100644 --- a/rare/ui/components/tabs/store/wishlist.py +++ b/rare/ui/components/tabs/store/wishlist.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'rare/ui/components/tabs/store/wishlist.ui' # -# Created by: PyQt5 UI code generator 5.15.9 +# Created by: PyQt5 UI code generator 5.15.10 # # WARNING: Any manual changes made to this file will be lost when pyuic5 is # run again. Do not edit this file unless you know what you are doing. @@ -16,8 +16,8 @@ class Ui_Wishlist(object): Wishlist.setObjectName("Wishlist") Wishlist.resize(423, 153) Wishlist.setWindowTitle("Wishlist") - self.verticalLayout = QtWidgets.QVBoxLayout(Wishlist) - self.verticalLayout.setObjectName("verticalLayout") + self.main_layout = QtWidgets.QVBoxLayout(Wishlist) + self.main_layout.setObjectName("main_layout") self.tool_layout = QtWidgets.QHBoxLayout() self.tool_layout.setObjectName("tool_layout") self.sort_label = QtWidgets.QLabel(Wishlist) @@ -59,10 +59,10 @@ class Ui_Wishlist(object): self.reload_button.setText("") self.reload_button.setObjectName("reload_button") self.tool_layout.addWidget(self.reload_button) - self.verticalLayout.addLayout(self.tool_layout) + self.main_layout.addLayout(self.tool_layout) self.no_games_label = QtWidgets.QLabel(Wishlist) self.no_games_label.setObjectName("no_games_label") - self.verticalLayout.addWidget(self.no_games_label) + self.main_layout.addWidget(self.no_games_label) self.list_scrollarea = QtWidgets.QScrollArea(Wishlist) self.list_scrollarea.setWidgetResizable(True) self.list_scrollarea.setObjectName("list_scrollarea") @@ -70,7 +70,7 @@ class Ui_Wishlist(object): self.list_container.setGeometry(QtCore.QRect(0, 0, 407, 83)) self.list_container.setObjectName("list_container") self.list_scrollarea.setWidget(self.list_container) - self.verticalLayout.addWidget(self.list_scrollarea) + self.main_layout.addWidget(self.list_scrollarea) self.retranslateUi(Wishlist) diff --git a/rare/ui/components/tabs/store/wishlist.ui b/rare/ui/components/tabs/store/wishlist.ui index b28a8ce0..1d2fe6bd 100644 --- a/rare/ui/components/tabs/store/wishlist.ui +++ b/rare/ui/components/tabs/store/wishlist.ui @@ -13,7 +13,7 @@ Wishlist - +