Commit 891da8b2 authored by Yankovskiy Georgiy's avatar Yankovskiy Georgiy

System tabs with exit option

parent 69edd3a4
......@@ -11,6 +11,8 @@ from os.path import expanduser
from desktop_parser import DesktopFile
from platformdirs import user_cache_dir, user_config_dir
from ingame.models.EntriesModel import EntriesModel
from ingame.models.Entry import Entry
from ingame.models.Gamepad import Gamepad
from ingame.models.GamesModel import GamesModel
from ingame.models.GameEntry import GameEntry
......@@ -44,6 +46,7 @@ class App(QtCore.QObject):
self.portproton_games_model: GamesModel = GamesModel()
self.native_games_model: GamesModel = GamesModel()
self.system_entries_model: EntriesModel = EntriesModel()
self.portproton_config_location: str = '/.config/PortProton.conf'
self.portproton_location: str = ''
self.running_game_process: Union[subprocess.Popen, None] = None
......@@ -74,6 +77,7 @@ class App(QtCore.QObject):
print('An error occurred', e)
self.__native_games_setup()
self.__system_entries_setup()
self.gamepad.run()
self.retrieve_games_details()
......@@ -142,6 +146,10 @@ class App(QtCore.QObject):
except:
continue
def __system_entries_setup(self):
self.system_entries_model.add_entry(Entry(name="Exit launcher", icon="../images/exit.svg", exec="exit"))
pass
# TODO: fix: progress=1.0 not emitted if details already cached/downloaded
def retrieve_games_details(self):
def retrieve_games_details_thread(t, model):
......@@ -209,3 +217,7 @@ class App(QtCore.QObject):
@Property(QObject, constant=True)
def native_games(self):
return self.native_games_model
@Property(QObject, constant=True)
def system_entries(self):
return self.system_entries_model
import typing
from dataclasses import fields
from PySide6.QtCore import QAbstractListModel, QModelIndex, Qt, QByteArray
from ingame.models.Entry import Entry
from ingame.models.GameEntry import GameEntry
# TODO: set to be parent of GamesModel
class EntriesModel(QAbstractListModel):
def __init__(self):
super().__init__()
self.entries_list = []
def data(self, index: QModelIndex, role: int = Qt.DisplayRole) -> typing.Any:
if 0 <= index.row() < self.rowCount():
student = self.entries_list[index.row()]
name = self.roleNames().get(role)
if name:
return getattr(student, name.decode())
def roleNames(self) -> dict[int, QByteArray]:
d = {}
for i, field in enumerate(fields(Entry)):
d[Qt.DisplayRole + i] = field.name.encode()
return d
def rowCount(self, index: QModelIndex = QModelIndex()) -> int:
return len(self.entries_list)
def add_entry(self, entry: Entry) -> None:
self.beginInsertRows(QModelIndex(), self.rowCount(), self.rowCount())
self.entries_list.append(entry)
self.endInsertRows()
def clear(self) -> None:
self.beginInsertRows(QModelIndex(), self.rowCount(), self.rowCount())
self.entries_list = []
self.endInsertRows()
pass
from dataclasses import dataclass
# TODO: merge with GameEntry (?), remove code duplication
@dataclass
class Entry:
name: str = ''
exec: str = ''
icon: str = ''
......@@ -5,6 +5,8 @@ from PySide6.QtCore import QAbstractListModel, QModelIndex, Qt, QByteArray
from ingame.models.GameEntry import GameEntry
# TODO: set to be child of GamesModel
class GamesModel(QAbstractListModel):
def __init__(self):
......
......@@ -30,6 +30,10 @@ Rectangle {
Component.onCompleted: {
tabButtons.changeButtonActiveTab(tabs.activeButtonTab);
tabButtons.x = tabButtons.tempX;
// being required in EntriesTab directly (see tabs/EntriesTab.qml)
// systemManagementGrid.model = core_app.system_entries;
// console.log("Tabs completed!");
}
onWidthChanged: function(){
......@@ -206,62 +210,26 @@ Rectangle {
}
}
// Заглушка Системных настроек
Grid {
// System tab
EntriesTab {
id: systemManagementGrid
visible: tabs.currentTab == TabConstants.systemManagementTab
columns: 3
spacing: 2
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: parent.bottom
Layout.topMargin: 190
anchors.rightMargin: 0
anchors.leftMargin: 0
anchors.bottomMargin: 90
Rectangle {
color: "red";
width: 50;
height: 50;
}
Rectangle {
color: "green";
width: 20;
height: 50;
}
Rectangle {
color: "blue";
width: 50;
height: 20;
}
Rectangle {
color: "cyan";
width: 50;
height: 50;
}
Rectangle {
color: "magenta";
width: 10;
height: 10;
}
// model: core_app.system_tab ?? [] // no action here for some reason, see Component.onComplete
// TODO: extra check here?
}
// PortProton Games
GamesTab {
id: portProtonGamesTab
visible: tabs.currentTab == TabConstants.gamesTab
model: core_app.games
model: core_app.games // TODO: fix TypeError: Cannot read property 'games' of null (on application closed)
}
// Native games
GamesTab {
id: nativeGamesTab
visible: tabs.currentTab == TabConstants.nativeGamesTab
model: core_app.native_games
model: core_app.native_games // TODO: fix TypeError: Cannot read property 'native_games' of null (on application closed)
}
......
import QtQuick
import "../constants/scene.js" as SceneConstants
//import "../components/" as C
import QtQuick.Controls as C
// Подключить для работы с типом объекта LinearGradient
import Qt5Compat.GraphicalEffects
// TODO: set Game to be child (dependent element) of Entry
Game {
id: entry
function press(){
switch(entry.gameExec){
case 'exit':
window.close();
break;
}
}
}
......@@ -4,6 +4,9 @@ import "../constants/scene.js" as SceneConstants
import QtQuick.Controls as C
// Подключить для работы с типом объекта LinearGradient
import Qt5Compat.GraphicalEffects
// TODO: set Entry to be parent (root element) of Game
C.Button {
property string gameTitle: "Generic title"
property string gameIcon: ""
......
<?xml version="1.0" encoding="UTF-8"?>
<svg width="512" height="512" version="1.1" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg">
<g id="line" transform="matrix(18.066 0 0 18.066 39.155 48.134)">
<path d="m16 9h-8.5859l1.2929-1.293a1 1 0 0 0-1.414-1.414l-3 3a1.0151 1.0151 0 0 0 0 1.4141l3 3a1 1 0 0 0 1.414-1.4141l-1.2929-1.293h8.5859a2 2 0 0 1 0 4h-4a1 1 0 0 0 0 2h4a4 4 0 0 0 0-8z" fill="#fff"/>
</g>
</svg>
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
// TODO: set to be parent (root element) of GamesTab
// TODO: remove code duplication (!)
ScrollView {
property var model: core_app.system_entries; // TODO: fix TypeError: Cannot read property 'system_entries' of null (on application closes)
id: entriesScroller
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 / entriesScroller.height;
scrolV.toAnim = (1.0 - scrolV.size) * y / entriesScroller.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: entriesGrid
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)+1
anchors.rightMargin: rowSpacing * 2
anchors.leftMargin: rowSpacing * 2
anchors.bottomMargin : 90
anchors.topMargin: Math.floor( entriesScroller.width / 100 * 3)
rowSpacing: Math.floor( entriesScroller.width / 100 * 3)
columnSpacing: rowSpacing
// Повторитель
Repeater {
id: entriesGridRepeater
model: entriesScroller.model
Entry {
id: game
gameTitle: model.name
gameExec: model.exec
gameIcon: model.icon
Layout.bottomMargin :
(index - index % entriesGrid.columns) /
entriesGrid.columns === entriesGrid.rows - 2 ? entriesGrid.rowSpacing * 2 : 0
onFocusChanged: if(focus) {
entriesScroller.scrollToY(y);
}
Layout.preferredWidth:
(entriesScroller.width - (entriesGrid.columns -1) *
entriesGrid.rowSpacing - entriesGrid.anchors.rightMargin - entriesGrid.anchors.leftMargin)
/ entriesGrid.columns
Layout.preferredHeight: Layout.preferredWidth / 2 * 3
}
}
Rectangle {
Layout.preferredWidth: 1
Layout.preferredHeight: 10
color: "red"
opacity: 0
}
}
// LOGIC
property int focusedItems: 0;
function applyItemsFocus(inc){
if(window.scene !== S.homeScene) return;
let c = entriesGrid.children;
let l = c.length - 1; // exclude QQuickRepeater
if(entriesScroller.focusedItems + inc >= l) {
entriesScroller.focusedItems = (entriesScroller.focusedItems + inc === l - 1) ? 0 : l - 1;
} else if(entriesScroller.focusedItems + inc < 0) {
entriesScroller.focusedItems = (entriesScroller.focusedItems + inc === 0) ? l - 1 : 0; //;
} else {
entriesScroller.focusedItems += inc;
}
if(c[entriesScroller.focusedItems] !== undefined) {
c[entriesScroller.focusedItems].forceActiveFocus();
}
}
function applyItemsFocusByLine(inc){
entriesScroller.applyItemsFocus(inc * entriesGrid.columns);
}
function pressFocusedItem(){
let c = entriesGrid.children;
if(c[entriesScroller.focusedItems] !== undefined) {
c[entriesScroller.focusedItems].press();
}
}
function refreshItems(data){
// entriesGridRepeater.model = [];
// entriesGridRepeater.model = data;
}
}
......@@ -8,6 +8,9 @@ import "../constants/style.js" as Style
import "../components" as TopMenuBut
import "../constants/scene.js" as S
// TODO: set to be child (dependent element) of EntriesTab
// TODO: remove code duplication (!)
ScrollView {
property var model: buttonGames
......
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