cleaned up and commented code
This commit is contained in:
+24
-23
@@ -3,12 +3,14 @@ import QtQuick.Window
|
|||||||
import NomadInput 1.0
|
import NomadInput 1.0
|
||||||
|
|
||||||
QtObject {
|
QtObject {
|
||||||
|
// this is our top screen, for now, though in the future I would like to have it configurable which screen does which.
|
||||||
|
// I'll burn that bridge when I get to it
|
||||||
property Window topWindow: Window {
|
property Window topWindow: Window {
|
||||||
width: 960
|
width: 960 // this resolution size is for development. I'd like to have a cleaner way to develop for 1080p without getting a dedicated monitor for testing nomad on my dev computer
|
||||||
height: 540
|
height: 540
|
||||||
visible: true
|
visible: true
|
||||||
title: "nomad-top"
|
title: "nomad-top" // this is necesarry for Sway, which auto-assigns the windows
|
||||||
property string lastInputText: "none"
|
property string lastInputText: "none" // I've never really integrated NomadInput into this debug section in the top half and now I'm too lazy to. it's just a placeholder anyway.
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: inputManager
|
target: inputManager
|
||||||
@@ -21,7 +23,7 @@ QtObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle { // just a placeholder for the top screen until we do something better
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
color: "#181818"
|
color: "#181818"
|
||||||
|
|
||||||
@@ -45,19 +47,10 @@ QtObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
property Window bottomWindow: Window {
|
property Window bottomWindow: Window {
|
||||||
width: 620
|
width: 620 // again, half-resolution
|
||||||
height: 540
|
height: 540
|
||||||
visible: true
|
visible: true
|
||||||
title: "nomad-bottom"
|
title: "nomad-bottom" // window titling for Sway auto-assign
|
||||||
|
|
||||||
readonly property var mainMenuNavRegions: ({
|
|
||||||
DockMenu: 0,
|
|
||||||
GamesList: 1,
|
|
||||||
Count: 2
|
|
||||||
})
|
|
||||||
|
|
||||||
property int focusRegion: bottomWindow.mainMenuNavRegions.GamesList
|
|
||||||
property int dockMenuIndex: 0
|
|
||||||
|
|
||||||
NavigationManager {
|
NavigationManager {
|
||||||
id: navigationManager
|
id: navigationManager
|
||||||
@@ -67,6 +60,8 @@ QtObject {
|
|||||||
Connections {
|
Connections {
|
||||||
target: inputManager
|
target: inputManager
|
||||||
|
|
||||||
|
// you don't wanna know what this used to look like :dread:
|
||||||
|
// just checks if our navigation is directional, and, if so, moves the selection in that direction
|
||||||
function onActionPressed(action) {
|
function onActionPressed(action) {
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case InputManager.Up:
|
case InputManager.Up:
|
||||||
@@ -85,7 +80,8 @@ QtObject {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
color: "#181818"
|
color: "#181818"
|
||||||
|
|
||||||
Text {
|
// top text where a menu dock will probably be eventually
|
||||||
|
Text {
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.topMargin: 30
|
anchors.topMargin: 30
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
@@ -95,6 +91,8 @@ QtObject {
|
|||||||
font.pixelSize: 24
|
font.pixelSize: 24
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this is the game carousel. mostly self explanatory, but I wanna point out that the local index for this listView does probably still matter.
|
||||||
|
// I got rid of dedicated menu indexing for the dock, but I think it still matters here, for the sake of auto-scrolling
|
||||||
ListView {
|
ListView {
|
||||||
id: gamesList
|
id: gamesList
|
||||||
|
|
||||||
@@ -102,23 +100,25 @@ QtObject {
|
|||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
|
// TODO: Tune this stuff !!! it feels just slightly janky and off for now, game cards don't align when scrolling, they go all the way out
|
||||||
|
// to the edge with no spacing at all when on the leftmost or rightmost cards, and vertical navigation from the dock feels *wrong*, idk,
|
||||||
|
// fix it sometime.
|
||||||
height: 140
|
height: 140
|
||||||
|
|
||||||
orientation: ListView.Horizontal
|
orientation: ListView.Horizontal
|
||||||
preferredHighlightBegin: 200
|
preferredHighlightBegin: 200
|
||||||
preferredHighlightEnd: 420
|
preferredHighlightEnd: 420
|
||||||
highlightRangeMode: ListView.ApplyRange
|
highlightRangeMode: ListView.ApplyRange
|
||||||
spacing: 20
|
spacing: 20
|
||||||
|
|
||||||
model: 20
|
model: 20 // this is a placeholder for now, I don't know how I really want to handle max game carousel size yet. another time.
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: { // on startup, this should select the first game in the list.
|
||||||
if (currentItem) {
|
if (currentItem) {
|
||||||
navigationManager.focusItem(currentItem)
|
navigationManager.focusItem(currentItem)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
delegate: Rectangle {
|
delegate: Rectangle { // TODO: design a real game card QML to replace this placeholder
|
||||||
id: gameTile
|
id: gameTile
|
||||||
|
|
||||||
width: 110
|
width: 110
|
||||||
@@ -126,7 +126,7 @@ QtObject {
|
|||||||
|
|
||||||
color: navigationManager.currentItem === gameTile ? "blue" : "white"
|
color: navigationManager.currentItem === gameTile ? "blue" : "white"
|
||||||
|
|
||||||
function navFocus() {
|
function onNavigationFocused() {
|
||||||
gamesList.currentIndex = index
|
gamesList.currentIndex = index
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,8 +168,9 @@ QtObject {
|
|||||||
|
|
||||||
color: navigationManager.currentItem === dockButton ? "blue" : "white"
|
color: navigationManager.currentItem === dockButton ? "blue" : "white"
|
||||||
|
|
||||||
function navFocus() {
|
function onNavigationFocused() {
|
||||||
bottomWindow.dockMenuIndex = index
|
// there used to be code here when my navigation was worse. I bet I'll need it eventually tho
|
||||||
|
// this entire section is a placeholder dock button anyway though.
|
||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
|
|||||||
@@ -1,13 +1,18 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import NomadInput 1.0
|
import NomadInput 1.0
|
||||||
|
|
||||||
|
// NavigationManager
|
||||||
|
// This thing's job is to keep track of the "physical" logcation of every UI element that can be navigated to, and allow for directional
|
||||||
|
// navigation of the on-screen UI. It requires every selectable object on-screen to register itself so that we can keep track of what is
|
||||||
|
// where, so much of it's functionality is registering and unregistering and reviewing that registry of items.
|
||||||
QtObject {
|
QtObject {
|
||||||
id: manager
|
id: manager
|
||||||
|
|
||||||
property Item rootItem: null
|
property Item rootItem: null // this is necesarry for the sake of having a global co-ordinate system by which to keep track of stuff
|
||||||
property list<QtObject> items
|
property list<QtObject> items
|
||||||
property var currentItem: null
|
property var currentItem: null
|
||||||
|
|
||||||
|
// set the focus on a particular item in the registry
|
||||||
function focusItem(item) {
|
function focusItem(item) {
|
||||||
if (!item) {
|
if (!item) {
|
||||||
return
|
return
|
||||||
@@ -15,8 +20,9 @@ QtObject {
|
|||||||
|
|
||||||
currentItem = item
|
currentItem = item
|
||||||
|
|
||||||
if (typeof item.navFocus === "function") {
|
// this is a little helper function that is called on anything that's focused (Assuming the object has such a function).
|
||||||
item.navFocus()
|
if (typeof item.onNavigationFocused === "function") {
|
||||||
|
item.onNavigationFocused()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -25,6 +31,7 @@ QtObject {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// double check that we haven't registered this yet
|
||||||
for (let i = 0; i < items.length; i++) {
|
for (let i = 0; i < items.length; i++) {
|
||||||
if (items[i] === item) {
|
if (items[i] === item) {
|
||||||
return
|
return
|
||||||
@@ -53,13 +60,18 @@ QtObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function move(action) {
|
function move(action) {
|
||||||
|
// just to make sure everything is set up right
|
||||||
if (!currentItem || !rootItem) {
|
if (!currentItem || !rootItem) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this is our direction vector by which to move.
|
||||||
|
// since I made it like this, I could probably put analog directions into this easily.
|
||||||
|
// don't know if I would actually want to, but I probably could easily.
|
||||||
let dx = 0
|
let dx = 0
|
||||||
let dy = 0
|
let dy = 0
|
||||||
|
|
||||||
|
// analog input aside, this just takes our cardinal input directiosn and sets the vector accordingly
|
||||||
switch(action) {
|
switch(action) {
|
||||||
case InputManager.Left:
|
case InputManager.Left:
|
||||||
dx = -1
|
dx = -1
|
||||||
@@ -81,14 +93,17 @@ QtObject {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// we want the center point of the actual item, not just the coordinates it's set to. better for directionality.
|
||||||
let currentCenter = currentItem.mapToItem(rootItem, currentItem.width / 2, currentItem.height / 2)
|
let currentCenter = currentItem.mapToItem(rootItem, currentItem.width / 2, currentItem.height / 2)
|
||||||
|
|
||||||
let bestItem = null
|
let bestItem = null
|
||||||
let bestScore = Infinity
|
let bestScore = Infinity
|
||||||
|
|
||||||
|
// sorts through all our items to see which is closest
|
||||||
for (let i = 0; i < items.length; i++) {
|
for (let i = 0; i < items.length; i++) {
|
||||||
let candidate = items[i]
|
let candidate = items[i]
|
||||||
|
|
||||||
|
// sanity checks
|
||||||
if (!candidate) {
|
if (!candidate) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -101,6 +116,7 @@ QtObject {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// we want the center point of the item, just like before
|
||||||
let candidateCenter = candidate.mapToItem(rootItem, candidate.width / 2, candidate.height / 2)
|
let candidateCenter = candidate.mapToItem(rootItem, candidate.width / 2, candidate.height / 2)
|
||||||
|
|
||||||
let differenceX = candidateCenter.x - currentCenter.x
|
let differenceX = candidateCenter.x - currentCenter.x
|
||||||
@@ -109,31 +125,28 @@ QtObject {
|
|||||||
let forwardDistance
|
let forwardDistance
|
||||||
let sidewaysDistance
|
let sidewaysDistance
|
||||||
|
|
||||||
if (dx !== 0) {
|
if (dx !== 0) { // Horizontal directions
|
||||||
forwardDistance = differenceX * dx
|
forwardDistance = differenceX * dx
|
||||||
sidewaysDistance = Math.abs(differenceY)
|
sidewaysDistance = Math.abs(differenceY)
|
||||||
} else {
|
} else { // vectical directions
|
||||||
forwardDistance = differenceY * dy
|
forwardDistance = differenceY * dy
|
||||||
sidewaysDistance = Math.abs(differenceX)
|
sidewaysDistance = Math.abs(differenceX)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (forwardDistance <= 0) {
|
if (forwardDistance <= 0) { // if it's on the wrong side of the object just discard it
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
let score = forwardDistance + sidewaysDistance * 2
|
let score = forwardDistance + sidewaysDistance * 2 // this might need to be tuned later. prioritizes vertical alignment
|
||||||
|
|
||||||
if (score < bestScore) {
|
if (score < bestScore) {
|
||||||
bestScore = score
|
bestScore = score
|
||||||
bestItem = candidate
|
bestItem = candidate
|
||||||
}
|
}
|
||||||
console.log("candidate:", candidate, "center:", candidateCenter.x, candidateCenter.y, "difference:", differenceX, differenceY)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bestItem) {
|
if (bestItem) {
|
||||||
focusItem(bestItem)
|
focusItem(bestItem)
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("MOVE", inputManager.actionName(action), "current:", currentItem, "center:", currentCenter.x, currentCenter.y)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,6 +3,9 @@
|
|||||||
#include <QEvent>
|
#include <QEvent>
|
||||||
#include <QKeyEvent>
|
#include <QKeyEvent>
|
||||||
|
|
||||||
|
// InputManager is intended to make all input detection standard across all input devices that
|
||||||
|
// nomad supports, turning them into generic input commands for other parts of the program to
|
||||||
|
// follow. It emits signals when inputs are pressed and when inputs are released
|
||||||
bool InputManager::eventFilter(QObject *watched, QEvent *event)
|
bool InputManager::eventFilter(QObject *watched, QEvent *event)
|
||||||
{
|
{
|
||||||
// filter for keyboard presses and releases
|
// filter for keyboard presses and releases
|
||||||
@@ -17,6 +20,7 @@ bool InputManager::eventFilter(QObject *watched, QEvent *event)
|
|||||||
// translate to nomad Action
|
// translate to nomad Action
|
||||||
Action action;
|
Action action;
|
||||||
|
|
||||||
|
// hard-coded keyboard binding. may or may not ever make it configurable.
|
||||||
switch (keyEvent->key()) {
|
switch (keyEvent->key()) {
|
||||||
case Qt::Key_Up:
|
case Qt::Key_Up:
|
||||||
action = Action::Up;
|
action = Action::Up;
|
||||||
@@ -115,6 +119,7 @@ bool InputManager::eventFilter(QObject *watched, QEvent *event)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// defines our list of actions
|
||||||
QString InputManager::actionName(Action action) const
|
QString InputManager::actionName(Action action) const
|
||||||
{
|
{
|
||||||
switch (action) {
|
switch (action) {
|
||||||
|
|||||||
+13
-8
@@ -6,26 +6,31 @@
|
|||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
|
// creates our Qt GUI app
|
||||||
QGuiApplication app(argc, argv);
|
QGuiApplication app(argc, argv);
|
||||||
|
|
||||||
|
// creats InputManager and installs it as an application-wide event filter so it can observe input events before normal delivery
|
||||||
InputManager inputManager;
|
InputManager inputManager;
|
||||||
app.installEventFilter(&inputManager);
|
app.installEventFilter(&inputManager);
|
||||||
|
|
||||||
qmlRegisterUncreatableMetaObject(
|
// Expose InputManager's Qt meta-object to QML under the NomadInput module, for ease of reading code.
|
||||||
InputManager::staticMetaObject,
|
// This lets us refer to inputs as InputManager.Left, InputManager.Right, et cetera
|
||||||
"NomadInput",
|
// QML isn't allowed to create it's own
|
||||||
1, 0,
|
qmlRegisterUncreatableMetaObject(InputManager::staticMetaObject, "NomadInput", 1, 0, "InputManager", "InputManager is created by nomad");
|
||||||
"InputManager",
|
|
||||||
"InputManager is created by nomad"
|
|
||||||
);
|
|
||||||
|
|
||||||
|
// start up the QML engine
|
||||||
QQmlApplicationEngine engine;
|
QQmlApplicationEngine engine;
|
||||||
|
// make our InputManager instance available to QML for signals
|
||||||
engine.rootContext()->setContextProperty("inputManager", &inputManager);
|
engine.rootContext()->setContextProperty("inputManager", &inputManager);
|
||||||
|
|
||||||
|
// load our Main.qml into the engine to start things off
|
||||||
engine.loadFromModule("nomad", "Main");
|
engine.loadFromModule("nomad", "Main");
|
||||||
|
|
||||||
if (engine.rootObjects().isEmpty())
|
// makes sure we've got QML objects
|
||||||
|
if (engine.rootObjects().isEmpty()) {
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the event loop and keeps nomad running until it exits
|
||||||
return app.exec();
|
return app.exec();
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user