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 ...@@ -11,6 +11,8 @@ from os.path import expanduser
from desktop_parser import DesktopFile from desktop_parser import DesktopFile
from platformdirs import user_cache_dir, user_config_dir 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.Gamepad import Gamepad
from ingame.models.GamesModel import GamesModel from ingame.models.GamesModel import GamesModel
from ingame.models.GameEntry import GameEntry from ingame.models.GameEntry import GameEntry
...@@ -44,6 +46,7 @@ class App(QtCore.QObject): ...@@ -44,6 +46,7 @@ class App(QtCore.QObject):
self.portproton_games_model: GamesModel = GamesModel() self.portproton_games_model: GamesModel = GamesModel()
self.native_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_config_location: str = '/.config/PortProton.conf'
self.portproton_location: str = '' self.portproton_location: str = ''
self.running_game_process: Union[subprocess.Popen, None] = None self.running_game_process: Union[subprocess.Popen, None] = None
...@@ -74,6 +77,7 @@ class App(QtCore.QObject): ...@@ -74,6 +77,7 @@ class App(QtCore.QObject):
print('An error occurred', e) print('An error occurred', e)
self.__native_games_setup() self.__native_games_setup()
self.__system_entries_setup()
self.gamepad.run() self.gamepad.run()
self.retrieve_games_details() self.retrieve_games_details()
...@@ -142,6 +146,10 @@ class App(QtCore.QObject): ...@@ -142,6 +146,10 @@ class App(QtCore.QObject):
except: except:
continue 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 # TODO: fix: progress=1.0 not emitted if details already cached/downloaded
def retrieve_games_details(self): def retrieve_games_details(self):
def retrieve_games_details_thread(t, model): def retrieve_games_details_thread(t, model):
...@@ -209,3 +217,7 @@ class App(QtCore.QObject): ...@@ -209,3 +217,7 @@ class App(QtCore.QObject):
@Property(QObject, constant=True) @Property(QObject, constant=True)
def native_games(self): def native_games(self):
return self.native_games_model 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 ...@@ -5,6 +5,8 @@ from PySide6.QtCore import QAbstractListModel, QModelIndex, Qt, QByteArray
from ingame.models.GameEntry import GameEntry from ingame.models.GameEntry import GameEntry
# TODO: set to be child of GamesModel
class GamesModel(QAbstractListModel): class GamesModel(QAbstractListModel):
def __init__(self): def __init__(self):
......
...@@ -30,6 +30,10 @@ Rectangle { ...@@ -30,6 +30,10 @@ Rectangle {
Component.onCompleted: { Component.onCompleted: {
tabButtons.changeButtonActiveTab(tabs.activeButtonTab); tabButtons.changeButtonActiveTab(tabs.activeButtonTab);
tabButtons.x = tabButtons.tempX; tabButtons.x = tabButtons.tempX;
// being required in EntriesTab directly (see tabs/EntriesTab.qml)
// systemManagementGrid.model = core_app.system_entries;
// console.log("Tabs completed!"); // console.log("Tabs completed!");
} }
onWidthChanged: function(){ onWidthChanged: function(){
...@@ -206,62 +210,26 @@ Rectangle { ...@@ -206,62 +210,26 @@ Rectangle {
} }
} }
// Заглушка Системных настроек // System tab
Grid { EntriesTab {
id: systemManagementGrid id: systemManagementGrid
visible: tabs.currentTab == TabConstants.systemManagementTab visible: tabs.currentTab == TabConstants.systemManagementTab
// model: core_app.system_tab ?? [] // no action here for some reason, see Component.onComplete
columns: 3 // TODO: extra check here?
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;
}
} }
// PortProton Games // PortProton Games
GamesTab { GamesTab {
id: portProtonGamesTab id: portProtonGamesTab
visible: tabs.currentTab == TabConstants.gamesTab 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 // Native games
GamesTab { GamesTab {
id: nativeGamesTab id: nativeGamesTab
visible: tabs.currentTab == TabConstants.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 ...@@ -4,6 +4,9 @@ import "../constants/scene.js" as SceneConstants
import QtQuick.Controls as C import QtQuick.Controls as C
// Подключить для работы с типом объекта LinearGradient // Подключить для работы с типом объекта LinearGradient
import Qt5Compat.GraphicalEffects import Qt5Compat.GraphicalEffects
// TODO: set Entry to be parent (root element) of Game
C.Button { C.Button {
property string gameTitle: "Generic title" property string gameTitle: "Generic title"
property string gameIcon: "" 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 ...@@ -8,6 +8,9 @@ import "../constants/style.js" as Style
import "../components" as TopMenuBut import "../components" as TopMenuBut
import "../constants/scene.js" as S import "../constants/scene.js" as S
// TODO: set to be child (dependent element) of EntriesTab
// TODO: remove code duplication (!)
ScrollView { ScrollView {
property var model: buttonGames 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