Unverified Commit 8c3903d8 authored by Mikhail Tergoev's avatar Mikhail Tergoev Committed by GitHub

Merge pull request #3 from yageorgiy/main

Basic game launching
parents ed0cf291 b07b7b0b
......@@ -2,13 +2,10 @@ import QtQuick
import QtQuick.Controls as C
C.Button {
// control.down
// control.activeFocus
id: control
width: 150
height: 50
text: qsTr("Button")
contentItem: Text {
......
import QtQuick
import QtQuick.Controls as C
import "."
Button {
property string imageUrl: "../images/generic.svg"
id: button
contentItem: Image {
source: button.imageUrl
fillMode: Image.PreserveAspectFit // ensure it fits
}
}
import QtQuick
import QtQuick.Controls as C
import QtQuick.Layouts
import "../delegates"
import "../constants/tabs.js" as TabConstants
Rectangle {
// PROPERTIES
property string currentTab: TabConstants.systemManagementTab
......@@ -13,6 +17,9 @@ Rectangle {
height: 480
color: "#ffffff"
// onWidthChanged: { console.log("Window Width changed: " + width) }
// onHeightChanged: { console.log("Window Height changed: " + height)}
// COMPONENTS
Grid {
......@@ -38,26 +45,51 @@ Rectangle {
Rectangle { color: "magenta"; width: 10; height: 10 }
}
Grid {
id: gamesGrid
visible: tabs.currentTab == TabConstants.gamesTab
columns: 3
spacing: 2
C.ScrollView {
visible: tabs.currentTab == TabConstants.gamesTab
id: gamesScroller
anchors.fill: parent
anchors.topMargin: 60
clip : true
GridLayout {
id: gamesGrid
readonly property int elementWidth: 256 + gamesGrid.rowSpacing*2
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.topMargin: 60
anchors.rightMargin: 0
anchors.leftMargin: 0
anchors.bottomMargin: 0
Text {
id: text2
text: qsTr("Text")
font.pixelSize: 12
// columns: Math.max(Math.floor(parent.width / elementWidth), 1)
// rows: Math.max(Math.ceil(children.length / columns), 1)
columns: Math.max(Math.floor(gamesScroller.width / elementWidth), 1)
rows: Math.max(Math.ceil(children.length / columns), 1)
anchors.rightMargin: 8
anchors.leftMargin: 8
anchors.bottomMargin: 8
anchors.topMargin: 8
rowSpacing: 8
columnSpacing: rowSpacing
Repeater {
// Layout.fillHeight: true
// Layout.fillWidth: true
model: core_app.games
Game {
gameTitle: model.name
gameExec: model.exec
gameIcon: model.icon
width: 256
height: 256
// icon: core_app.games.icon
// exec: core_app.games.exec
}
}
}
}
......@@ -88,6 +120,8 @@ Rectangle {
Button {
id: buttonSystemManagement
text: "System management"
width: 150
height: 50
onClicked: function(){
tabs.currentTab = TabConstants.systemManagementTab;
// tabs.changeTab();
......@@ -98,10 +132,14 @@ Rectangle {
Button {
id: buttonGames
text: "Games"
width: 150
height: 50
onClicked: function(){
tabs.currentTab = TabConstants.gamesTab;
if(app === undefined) return;
app.get_games();
//if(core_app === undefined) return;
//console.log("core_app found");
//app.get_games();
// tabs.changeTab();
// console.log(tabs.currentTab);
}
......@@ -111,3 +149,11 @@ Rectangle {
}
/*##^##
Designer {
D{i:0}D{i:1;invisible:true}
}
##^##*/
var homeScene = "home";
var gameInfoScene = "gameInfo";
var runningScene = "running";
\ No newline at end of file
import QtQuick
import "../constants/scene.js" as SceneConstants
import "../components/" as C
C.Button {
property string gameTitle: "Generic title"
property string gameIcon: ""
property string gameExec: ""
id: game
width: 256
height: 256
implicitWidth: 256
implicitHeight: 256
text: ""
// color: "#efefef"
//radius: 5
// border.width: 1
onClicked: function(){
// console.log(game.title);
gameInfoScene.title = game.gameTitle;
gameInfoScene.icon = game.gameIcon;
gameInfoScene.exec = game.gameExec;
window.scene = SceneConstants.gameInfoScene;
}
Image {
id: image
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: parent.bottom
source: game.gameIcon
anchors.rightMargin: 8
anchors.bottomMargin: 47
anchors.leftMargin: 8
anchors.topMargin: 8
fillMode: Image.PreserveAspectFit
}
Text {
id: title
y: 439
height: 33
text: game.gameTitle
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
font.pixelSize: 22
horizontalAlignment: Text.AlignHCenter
anchors.rightMargin: 8
anchors.leftMargin: 8
anchors.bottomMargin: 8
}
}
<?xml version="1.0" encoding="UTF-8"?>
<svg width="128" height="128" version="1.1" viewBox="0 0 33.867 33.867" xmlns="http://www.w3.org/2000/svg">
<path d="m32.211 22.315h-18.796v6.0794l-11.461-11.461 11.461-11.461v6.0794h18.796z" fill="#f9f9f9" stroke="#1a1a1a" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.438" style="paint-order:normal"/>
</svg>
<?xml version="1.0" encoding="UTF-8"?>
<svg width="128" height="128" version="1.1" viewBox="0 0 33.867 33.867" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(-1.1156 2.8582)" stroke="#1a1a1a" stroke-linecap="round" stroke-linejoin="round">
<path d="m13.952 5.5733c0.1189-0.93676 0.77503-1.6426 1.43-2.2733 0.38912-0.37471 0.62422-0.62811 1.1-0.88 1.5766-0.83466 2.771-0.42136 4.0333 0.69667 0.50459 0.44693 0.70636 0.62165 1.0633 1.1733 0.96765 1.4955 0.55549 2.3404-0.14667 3.85-0.32616 0.70125-0.69834 1.3791-1.1367 2.0167-0.47007 0.68375-1.0553 1.271-1.54 1.9433-0.20617 0.28598-0.39111 0.58667-0.58667 0.88-0.42478 0.63717-0.89741 1.2767-1.21 1.98-0.36563 0.82266-0.54629 1.6465-0.69667 2.53-0.18045 1.0601-0.32624 2.1115-0.25667 3.19 0.04662 0.72258 0.36404 1.3488 0.58667 2.0167" fill="none" stroke-width="1.438" style="paint-order:normal"/>
<path d="m17.942 25.729a0.63785 0.63785 0 0 1-0.63785 0.63785 0.63785 0.63785 0 0 1-0.63785-0.63785 0.63785 0.63785 0 0 1 0.63785-0.63785 0.63785 0.63785 0 0 1 0.63785 0.63785z" stroke-width="1.0423"/>
</g>
</svg>
......@@ -3,8 +3,23 @@ import QtQuick
// Import all components from folder
import "scenes"
import "constants/scene.js" as SceneConstants
Window {
property string scene: SceneConstants.homeScene
Connections {
target: core_app
function onGameStarted(done) {
console.log("gameStarted!!");
window.scene = SceneConstants.runningScene;
}
function onGameEnded(done) {
console.log("gameEnded!!");
window.scene = SceneConstants.gameInfoScene;
}
}
id: window
width: 640
height: 480
......@@ -12,10 +27,25 @@ Window {
title: qsTr("Launcher")
HomeScene {
id: grid
visible: scene == SceneConstants.homeScene
id: homeScene
anchors.fill: parent
}
GameInfoScene {
visible: scene == SceneConstants.gameInfoScene
id: gameInfoScene
anchors.fill: parent
}
RunningScene {
visible: scene == SceneConstants.runningScene
id: runningScene
anchors.fill: parent
}
/* InputPanelHomeScene {
id: inputPanel
z: 99
......
import QtQuick
import QtQuick.Controls
import "../components"
import "../constants/scene.js" as S
Rectangle {
property string title: "Generic title"
property string icon: ""
property string exec: ""
id: container
x: 0
y: 0
width: 640
height: 480
Rectangle {
id: rectangle
height: 64
color: "#2b2b2b"
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.rightMargin: 0
anchors.leftMargin: 0
anchors.topMargin: 0
}
ButtonImage {
id: back
x: 0
y: 0
width: 64
height: 64
imageUrl: "../images/back.svg"
onClicked: function(){
window.scene = S.homeScene;
}
}
Image {
id: gameImage
width: 128
height: 128
anchors.left: parent.left
anchors.top: parent.top
source: container.icon
anchors.leftMargin: 8
anchors.topMargin: 70
fillMode: Image.PreserveAspectFit
}
Text {
id: title
height: 49
text: container.title
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
font.pixelSize: 32
anchors.leftMargin: 142
anchors.topMargin: 70
anchors.rightMargin: 8
}
Button {
anchors.left: parent.left
anchors.top: parent.top
anchors.leftMargin: 142
anchors.topMargin: 125
text: "Run game"
onClicked: function(){
if(core_app === undefined) return;
core_app.start_game(container.exec);
}
}
}
......@@ -12,7 +12,7 @@ Rectangle {
Rectangle {
id: rectangle
height: 50
height: 64
color: "#2b2b2b"
anchors.left: parent.left
anchors.right: parent.right
......@@ -28,7 +28,7 @@ Rectangle {
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.topMargin: 48
anchors.topMargin: 64
anchors.bottomMargin: 0
anchors.leftMargin: 0
anchors.rightMargin: 0
......
import QtQuick
import QtQuick.Controls
import "../components"
Rectangle {
id: container
x: 0
y: 0
width: 640
height: 480
Rectangle {
id: rectangle
color: "#2b2b2b"
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.bottomMargin: 0
anchors.rightMargin: 0
anchors.leftMargin: 0
anchors.topMargin: 0
Text {
id: text1
color: "#ffffff"
text: qsTr("Game is now running...")
anchors.fill: parent
font.pixelSize: 24
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
anchors.rightMargin: 0
anchors.bottomMargin: 0
anchors.leftMargin: 0
anchors.topMargin: 0
}
}
}
# This Python file uses the following encoding: utf-8
import sys
from pathlib import Path
from PySide6.QtGui import QGuiApplication
from PySide6.QtQml import QQmlApplicationEngine
from src.models.App import App
from models.App import App
from models.GamesModel import GamesModel
# TODO: add VirtualKeyboard
......@@ -13,10 +13,13 @@ if __name__ == "__main__":
app = QGuiApplication(sys.argv)
qml_file = Path(__file__).resolve().parent / "../qml/qml.qml"
engine = QQmlApplicationEngine()
app_model = App()
context = engine.rootContext()
context.setContextProperty("core_app", app_model)
engine.load(qml_file)
if not engine.rootObjects():
sys.exit(-1)
appModel = App()
context = engine.rootContext()
context.setContextProperty("app", appModel)
sys.exit(app.exec())
import threading
from time import sleep
from PySide6 import QtCore
from os.path import expanduser
import glob
from desktop_parser import DesktopFile
import os.path
from pathlib import Path
from PySide6.QtCore import Property, Signal, Slot, QObject, Qt
from models.GamesModel import Game, GamesModel
import subprocess
class GameShortcut:
......@@ -12,11 +20,16 @@ class GameShortcut:
class App(QtCore.QObject):
game_started = Signal(bool, name="gameStarted")
game_ended = Signal(bool, name="gameEnded")
def __init__(self):
super().__init__()
self.games_model = GamesModel()
self.home = expanduser('~')
self.config_location = '/.config/PortProton.conf'
self.portproton_location = ''
self.running_game_process = None
self.setup()
def setup(self):
......@@ -25,18 +38,71 @@ class App(QtCore.QObject):
self.portproton_location = file.read().strip()
print(f'Current PortProton location: {self.portproton_location}')
self.games_model.clear()
files = glob.glob(f"{self.portproton_location}/*.desktop")
# for val in files:
# print(val)
# desktop_file = DesktopFile.from_file("path/to/file.desktop")
for val in files:
desktop_file = DesktopFile.from_file(val)
data = desktop_file.data
entry = data['Desktop Entry']
name = entry['Name'] or 'generic'
exec = 'Exec' in entry and entry['Exec'] or ''
icon = entry['Icon']
assert (isinstance(name, str)
and isinstance(exec, str)
and isinstance(icon, str))
exec_split = exec.split(' ')
# Ignore extra non-related desktop entries
if (len(exec_split) <= 1 or
('data/scripts/start.sh' not in exec_split[1] or '%F' in exec_split[-1])):
continue
# TODO parse product name
icon = (os.path.isfile(icon) and icon
or os.path.realpath(f"{Path(__file__).resolve().parent}../../../qml/images/game_icon.png"))
self.games_model.add_game(Game(name=name, icon=icon, exec=exec))
except FileNotFoundError:
print('File not found')
except Exception:
print('An error occurred')
except Exception as e:
print('An error occurred', e)
pass
### SLOTS ###
@QtCore.Slot()
def get_games(self):
@Slot(str)
def start_game(self, exec):
self.game_started.emit(True)
def run_in_thread(t, _exec):
t.running_game_process = subprocess.Popen(
_exec,
shell=True,
bufsize=0,
stdout=subprocess.PIPE,
universal_newlines=True
)
t.running_game_process.wait()
t.game_ended.emit(True)
# output = self.running_game_process.stdout.read()
# self.running_game_process.stdout.close()
return
thread = threading.Thread(target=run_in_thread, args=(self, exec))
thread.start()
pass
### PROPERTIES ###
@Property(QObject, constant=True)
def games(self):
return self.games_model
import typing
from dataclasses import dataclass, fields
from PySide6.QtCore import QAbstractListModel, QModelIndex, Qt, QByteArray
@dataclass
class Game:
name: str = ''
exec: str = ''
icon: str = ''
class GamesModel(QAbstractListModel):
def __init__(self):
super().__init__()
self._list = []
def data(self, index: QModelIndex, role: int = Qt.DisplayRole) -> typing.Any:
if 0 <= index.row() < self.rowCount():
student = self._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(Game)):
d[Qt.DisplayRole + i] = field.name.encode()
return d
def rowCount(self, index: QModelIndex = QModelIndex()) -> int:
return len(self._list)
def add_game(self, game: Game) -> None:
self.beginInsertRows(QModelIndex(), self.rowCount(), self.rowCount())
self._list.append(game)
self.endInsertRows()
def clear(self) -> None:
self.beginInsertRows(QModelIndex(), self.rowCount(), self.rowCount())
self._list = []
self.endInsertRows()
pass
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