Commit 557445f9 authored by Yankovskiy Georgiy's avatar Yankovskiy Georgiy

Native games tab (without native entry images)

parent e117be13
......@@ -17,6 +17,7 @@ from ingame.models.GameEntry import GameEntry
from ingame.models.GameAgent import GameAgent
from PySide6.QtCore import Property, Signal, Slot, QObject, Qt
class App(QtCore.QObject):
app_name = "ingame"
app_author = "foss"
......@@ -41,7 +42,8 @@ class App(QtCore.QObject):
self.config_path = user_config_dir(App.app_name, App.app_author)
self.cache_path = user_cache_dir(App.app_name, App.app_author)
self.games_model: GamesModel = GamesModel()
self.portproton_games_model: GamesModel = GamesModel()
self.native_games_model: GamesModel = GamesModel()
self.portproton_config_location: str = '/.config/PortProton.conf'
self.portproton_location: str = ''
self.running_game_process: Union[subprocess.Popen, None] = None
......@@ -65,11 +67,51 @@ class App(QtCore.QObject):
with open(self.home + self.portproton_config_location, 'r') as file:
self.portproton_location = file.read().strip()
print(f'Current PortProton location: {self.portproton_location}')
self.__portproton_games_setup(glob.glob(f"{self.portproton_location}/*.desktop"))
except FileNotFoundError:
print('File not found')
except Exception as e:
print('An error occurred', e)
self.__native_games_setup()
self.gamepad.run()
self.retrieve_games_details()
self.games_model.clear()
files = glob.glob(f"{self.portproton_location}/*.desktop")
def __native_games_setup(self):
# Reference: https://askubuntu.com/a/490398
# TODO: replace with DesktopFile instance
self.native_games_model.clear()
for val in files:
for val in glob.glob("/usr/share/applications/*.desktop"):
try:
desktop_file = DesktopFile.from_file(val)
desktop_file_data = desktop_file.data
desktop_entry = desktop_file_data['Desktop Entry']
if 'Categories' not in desktop_entry:
continue
# Game;ArcadeGame;
entry_categories = desktop_entry['Categories'] or ''
if "Game" not in entry_categories:
continue
entry_name = desktop_entry['Name'] or 'generic'
entry_exec = 'Exec' in desktop_entry and desktop_entry['Exec'] or ''
entry_icon = '' # desktop_entry['Icon']
assert (isinstance(entry_name, str)
and isinstance(entry_exec, str)
and isinstance(entry_icon, str))
self.native_games_model.add_game(GameEntry(name=entry_name, icon=entry_icon, exec=entry_exec))
except:
continue
def __portproton_games_setup(self, files):
self.portproton_games_model.clear()
for val in files:
try:
desktop_file = DesktopFile.from_file(val)
desktop_file_data = desktop_file.data
desktop_entry = desktop_file_data['Desktop Entry']
......@@ -96,31 +138,28 @@ class App(QtCore.QObject):
# Remove extra env in the beginning
entry_exec = f"env START_FROM_STEAM=1 {entry_exec[4:len(entry_exec)]}"
self.games_model.add_game(GameEntry(name=entry_name, icon=entry_icon, exec=entry_exec))
self.gamepad.run()
self.retrieve_games_details()
except FileNotFoundError:
print('File not found')
except Exception as e:
print('An error occurred', e)
pass
self.portproton_games_model.add_game(GameEntry(name=entry_name, icon=entry_icon, exec=entry_exec))
except:
continue
# TODO: fix: progress=1.0 not emitted if details already cached/downloaded
def retrieve_games_details(self):
def retrieve_games_details_thread(t):
def retrieve_games_details_thread(t, model):
t.game_list_details_retrieving_progress.emit(0.0)
all_count: int = len(self.games_model.games_list)
all_count: int = len(model.games_list)
game_entry: GameEntry
i: int = 0
for game_entry in self.games_model.games_list:
for game_entry in model.games_list:
game_description = t.agent.retrieve_game_description(game_entry.name)
game_entry.icon = game_description['image_location_path'] or game_entry.icon
t.game_list_details_retrieving_progress.emit(float(i) / all_count)
i += 1
t.game_list_details_retrieving_progress.emit(1.0)
thread = threading.Thread(target=retrieve_games_details_thread, args=(self,))
thread = threading.Thread(target=retrieve_games_details_thread, args=(self, self.portproton_games_model))
thread.start()
thread = threading.Thread(target=retrieve_games_details_thread, args=(self, self.native_games_model))
thread.start()
''' CALLBACKS '''
......@@ -165,4 +204,8 @@ class App(QtCore.QObject):
@Property(QObject, constant=True)
def games(self):
return self.games_model
return self.portproton_games_model
@Property(QObject, constant=True)
def native_games(self):
return self.native_games_model
......@@ -3,6 +3,7 @@ import QtQuick.Controls
import QtQuick.Layouts
import QtQuick.Dialogs
import "../delegates"
import '../tabs'
import "../constants/tabs.js" as TabConstants
import "../constants/style.js" as Style
import "../components" as TopMenuBut
......@@ -11,6 +12,7 @@ import "../constants/scene.js" as S
// TODO: code refactor
Rectangle {
property string currentTab: TabConstants.gamesTab
property var currentTabInstance: portProtonGamesTab
property var activeButtonTab: buttonGames
id: tabs
......@@ -131,15 +133,19 @@ Rectangle {
tabs.activeButtonTab.isActive = true;
}
// TODO: поменять всё на Repeater
// слишком много дубликатов кода
TopMenuBut.TextButton {
id: buttonSystemManagement;
text: TabConstants.systemManagementTab;
width: 400;
onClicked: function(){
tabButtons.x = tabButtons.tempX
tabButtons.changeButtonActiveTab(this)
tabButtons.toggle = true
tabButtons.x = tabButtons.tempX;
tabButtons.changeButtonActiveTab(this);
tabButtons.toggle = true;
tabs.currentTab = TabConstants.systemManagementTab;
tabs.currentTabInstance = systemManagementGrid;
}
onReleased: tabButtons.toggle = false
}
......@@ -147,10 +153,12 @@ Rectangle {
id: buttonGames
text: TabConstants.gamesTab
onClicked: function(){
tabButtons.x = tabButtons.tempX
tabButtons.changeButtonActiveTab(this)
tabButtons.toggle = true
tabButtons.x = tabButtons.tempX;
tabButtons.changeButtonActiveTab(this);
tabButtons.toggle = true;
tabs.currentTab = TabConstants.gamesTab;
tabs.currentTabInstance = portProtonGamesTab;
//if(core_app === undefined) return;
//console.log("core_app found");
......@@ -165,15 +173,17 @@ Rectangle {
}
TopMenuBut.TextButton {
id: testbut2
text: "Test"
id: nativeGamesButton
text: TabConstants.nativeGamesTab
//font.pixelSize: 60
//height:Math.ceil(tabs.height/100 * 10)
onClicked: function(){
tabButtons.x = tabButtons.tempX
tabButtons.changeButtonActiveTab(this)
tabButtons.toggle = true
tabButtons.x = tabButtons.tempX;
tabButtons.changeButtonActiveTab(this);
tabButtons.toggle = true;
tabs.currentTab = TabConstants.nativeGamesTab;
tabs.currentTabInstance = nativeGamesTab;
}
onReleased: tabButtons.toggle = false
......@@ -240,112 +250,25 @@ Rectangle {
}
}
// Сетка игр
ScrollView {
// PortProton Games
GamesTab {
id: portProtonGamesTab
visible: tabs.currentTab == TabConstants.gamesTab
id: gamesScroller
anchors.fill: parent
anchors.topMargin: topNavigation.height
ScrollBar.vertical: ScrollBar {
id: scrolV;
height: parent.height
opacity: 0
position: 0
property double fromAnim: 0.0
property double toAnim: 0.0
}
function scrollToY(y,HItem) {
scrolV.fromAnim = scrolV.position
scrolV.position = (1.0 - scrolV.size) * y / gamesScroller.height
scrolV.toAnim = (1.0 - scrolV.size) * y / gamesScroller.height
if(scrolV.toAnim != scrolV.fromAnim)
scrollAnimation.start()
}
// Анимация авто скролла
PropertyAnimation {
to: scrolV.toAnim;
from: scrolV.fromAnim;
target: scrolV;
id: scrollAnimation;
property: "position";
duration: 200;
}
GridLayout {
id: gamesGrid
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: parent.bottom
columns: 5
rows: Math.max(Math.ceil(children.length / columns), 1)
anchors.rightMargin: rowSpacing * 2
anchors.leftMargin: rowSpacing * 2
anchors.bottomMargin : 90
anchors.topMargin: Math.floor( gamesScroller.width / 100 * 3)
rowSpacing: Math.floor( gamesScroller.width / 100 * 3)
columnSpacing: rowSpacing
// Повторитель
Repeater {
id: gamesGridRepeater
model: core_app.games
// Карточка игры
Game {
id: game
gameTitle: model.name
gameExec: model.exec
gameIcon: model.icon
Layout.bottomMargin :
(index - index % gamesGrid.columns) /
gamesGrid.columns === gamesGrid.rows - 1 ? gamesGrid.rowSpacing * 2 : 0
onFocusChanged: if(focus) {
gamesScroller.scrollToY(y);
}
Layout.preferredWidth:
(gamesScroller.width - (gamesGrid.columns -1) *
gamesGrid.rowSpacing - gamesGrid.anchors.rightMargin - gamesGrid.anchors.leftMargin)
/ gamesGrid.columns
Layout.preferredHeight: Layout.preferredWidth / 2 * 3
// Component.onCompleted: {a3.start()}
// SequentialAnimation {
// id:a3
// NumberAnimation {
// property:Layout.topMargin;
// easing.type: Easing.InOutQuad;
// duration: 300;
// from: 100//Layout.preferredHeight;
// to: 0;
// }
// NumberAnimation {
// property:Layout.topMargin;
// easing.type: Easing.InOutQuad;
// duration: 300;
// from: 0//Layout.preferredHeight;
// to: 100;
// }
// loops: Animation.Infinite
// }
// Layout.topMargin: Layout.preferredHeight
}
}
}
model: core_app.games
}
// Native games
GamesTab {
id: nativeGamesTab
visible: tabs.currentTab == TabConstants.nativeGamesTab
model: core_app.native_games
}
// LOGIC
property int focusedItems: 0;
property int focusedTabs: 0;
function getTabs(){
......@@ -353,7 +276,7 @@ Rectangle {
buttonSystemManagement,
buttonGames,
// testbut1,
testbut2
nativeGamesButton
];
}
......@@ -375,58 +298,38 @@ Rectangle {
// tabButtons.changeButtonActiveTab(item);
}
function applyItemsFocus(inc){
if(window.scene !== S.homeScene) return;
let c = gamesGrid.children;
let l = c.length - 1; // exclude QQuickRepeater
// console.log(tabs.focusedItems);
if(tabs.focusedItems + inc >= l) {
tabs.focusedItems = (c.focusedItems + inc === l - 1) ? 0 : l - 1;
} else if(tabs.focusedItems + inc < 0) {
tabs.focusedItems = (c.focusedItems + inc === 0) ? l - 1 : 0; //;
} else {
tabs.focusedItems += inc;
}
c[tabs.focusedItems].forceActiveFocus();
// gamesScroller.contentY = c[tabs.focusedItems].y; // not working
// c[tabs.focusedItems].clicked();
}
/* SIGNALS */
function onGamepadClickedLB(args){
tabs.applyTabsFocus(-1)
tabs.applyTabsFocus(-1);
}
function onGamepadClickedRB(args){
tabs.applyTabsFocus(1)
tabs.applyTabsFocus(1);
}
function onGamepadAxisUp(done){
tabs.applyItemsFocus(-gamesGrid.columns);
currentTabInstance.applyItemsFocusByLine(-1);
}
function onGamepadAxisDown(done){
tabs.applyItemsFocus(gamesGrid.columns);
currentTabInstance.applyItemsFocusByLine(1);
}
function onGamepadAxisLeft(done){
tabs.applyItemsFocus(-1)
currentTabInstance.applyItemsFocus(-1);
}
function onGamepadAxisRight(args){
tabs.applyItemsFocus(1)
currentTabInstance.applyItemsFocus(1);
}
function onGamepadClickedApply(args){
if(window.scene !== S.homeScene) return;
let c = gamesGrid.children;
c[tabs.focusedItems].press();
currentTabInstance.pressFocusedItem();
}
function onGameListDetailsRetrievingProgress(args) {
let progress = args[0];
console.log(progress);
if(progress === 1.0){
gamesGridRepeater.model = [];
gamesGridRepeater.model = core_app.games;
portProtonGamesTab.refreshItems(core_app.games);
nativeGamesTab.refreshItems(core_app.native_games);
}
}
......
var systemManagementTab = "System";
var gamesTab = "Games";
var nativeGamesTab = "Native";
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import QtQuick.Dialogs
import "../delegates"
import "../constants/tabs.js" as TabConstants
import "../constants/style.js" as Style
import "../components" as TopMenuBut
import "../constants/scene.js" as S
ScrollView {
property var model: buttonGames
id: gamesScroller
anchors.fill: parent
anchors.topMargin: topNavigation.height
ScrollBar.vertical: ScrollBar {
id: scrolV;
height: parent.height
opacity: 0
position: 0
property double fromAnim: 0.0
property double toAnim: 0.0
}
function scrollToY(y, HItem) {
scrolV.fromAnim = scrolV.position;
scrolV.position = (1.0 - scrolV.size) * y / gamesScroller.height;
scrolV.toAnim = (1.0 - scrolV.size) * y / gamesScroller.height;
if(scrolV.toAnim != scrolV.fromAnim) {
scrollAnimation.start();
}
}
// Анимация авто скролла
PropertyAnimation {
to: scrolV.toAnim;
from: scrolV.fromAnim;
target: scrolV;
id: scrollAnimation;
property: "position";
duration: 200;
}
GridLayout {
id: gamesGrid
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: parent.bottom
columns: 5
rows: Math.max(Math.ceil(children.length / columns), 1)
anchors.rightMargin: rowSpacing * 2
anchors.leftMargin: rowSpacing * 2
anchors.bottomMargin : 90
anchors.topMargin: Math.floor( gamesScroller.width / 100 * 3)
rowSpacing: Math.floor( gamesScroller.width / 100 * 3)
columnSpacing: rowSpacing
// Повторитель
Repeater {
id: gamesGridRepeater
model: gamesScroller.model
// Карточка игры
Game {
id: game
gameTitle: model.name
gameExec: model.exec
gameIcon: model.icon
Layout.bottomMargin :
(index - index % gamesGrid.columns) /
gamesGrid.columns === gamesGrid.rows - 1 ? gamesGrid.rowSpacing * 2 : 0
onFocusChanged: if(focus) {
gamesScroller.scrollToY(y);
}
Layout.preferredWidth:
(gamesScroller.width - (gamesGrid.columns -1) *
gamesGrid.rowSpacing - gamesGrid.anchors.rightMargin - gamesGrid.anchors.leftMargin)
/ gamesGrid.columns
Layout.preferredHeight: Layout.preferredWidth / 2 * 3
// Component.onCompleted: {a3.start()}
// SequentialAnimation {
// id:a3
// NumberAnimation {
// property:Layout.topMargin;
// easing.type: Easing.InOutQuad;
// duration: 300;
// from: 100//Layout.preferredHeight;
// to: 0;
// }
// NumberAnimation {
// property:Layout.topMargin;
// easing.type: Easing.InOutQuad;
// duration: 300;
// from: 0//Layout.preferredHeight;
// to: 100;
// }
// loops: Animation.Infinite
// }
// Layout.topMargin: Layout.preferredHeight
}
}
}
// LOGIC
property int focusedItems: 0;
function applyItemsFocus(inc){
if(window.scene !== S.homeScene) return;
let c = gamesGrid.children;
let l = c.length - 1; // exclude QQuickRepeater
if(gamesScroller.focusedItems + inc >= l) {
gamesScroller.focusedItems = (gamesScroller.focusedItems + inc === l - 1) ? 0 : l - 1;
} else if(gamesScroller.focusedItems + inc < 0) {
gamesScroller.focusedItems = (gamesScroller.focusedItems + inc === 0) ? l - 1 : 0; //;
} else {
gamesScroller.focusedItems += inc;
}
if(c[gamesScroller.focusedItems] !== undefined) {
c[gamesScroller.focusedItems].forceActiveFocus();
}
}
function applyItemsFocusByLine(inc){
gamesScroller.applyItemsFocus(inc * gamesGrid.columns);
}
function pressFocusedItem(){
let c = gamesGrid.children;
if(c[gamesScroller.focusedItems] !== undefined) {
c[gamesScroller.focusedItems].press();
}
}
function refreshItems(data){
gamesGridRepeater.model = [];
gamesGridRepeater.model = data;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment