diff --git a/shell/CMakeLists.txt b/shell/CMakeLists.txt index ec02379..0a2a75a 100644 --- a/shell/CMakeLists.txt +++ b/shell/CMakeLists.txt @@ -19,6 +19,7 @@ qt_add_qml_module(nomad-shell VERSION 1.0 QML_FILES qml/Main.qml + qml/NavigationManager.qml ) target_link_libraries(nomad-shell diff --git a/shell/qml/Main.qml b/shell/qml/Main.qml index 6eb7fc0..c586700 100644 --- a/shell/qml/Main.qml +++ b/shell/qml/Main.qml @@ -59,50 +59,27 @@ QtObject { property int focusRegion: bottomWindow.mainMenuNavRegions.GamesList property int dockMenuIndex: 0 + NavigationManager { + id: navigationManager + rootItem: bottomRoot + } + Connections { target: inputManager function onActionPressed(action) { - if (action === InputManager.Up) { - if (bottomWindow.focusRegion < bottomWindow.mainMenuNavRegions.Count - 1) { - bottomWindow.focusRegion += 1 - } - } - if (action === InputManager.Down) { - if (bottomWindow.focusRegion > 0) { - bottomWindow.focusRegion -= 1 - } - } - if (action === InputManager.Left || action === InputManager.Right) { - switch (bottomWindow.focusRegion) { - case bottomWindow.mainMenuNavRegions.GamesList: - if (action === InputManager.Left) { - if (gamesList.currentIndex > 0) { - gamesList.currentIndex -= 1 - } - } else { - if (gamesList.currentIndex < gamesList.count - 1) { - gamesList.currentIndex += 1 - } - } - break - - case bottomWindow.mainMenuNavRegions.DockMenu: - if (action === InputManager.Left) { - if (bottomWindow.dockMenuIndex > 0) { - bottomWindow.dockMenuIndex -= 1 - } - } else { - if (bottomWindow.dockMenuIndex < dockRepeater.count - 1) { - bottomWindow.dockMenuIndex += 1 - } - } - break - } + switch (action) { + case InputManager.Up: + case InputManager.Down: + case InputManager.Left: + case InputManager.Right: + navigationManager.move(action) + break } } } + Rectangle { id: bottomRoot anchors.fill: parent @@ -135,17 +112,36 @@ QtObject { model: 20 + Component.onCompleted: { + if (currentItem) { + navigationManager.focusItem(currentItem) + } + } + delegate: Rectangle { + id: gameTile + width: 110 height: 110 - color: bottomWindow.focusRegion === bottomWindow.mainMenuNavRegions.GamesList && index === gamesList.currentIndex ? "blue" : "white" + color: navigationManager.currentItem === gameTile ? "blue" : "white" + + function navFocus() { + gamesList.currentIndex = index + } + + Component.onCompleted: { + navigationManager.registerItem(gameTile) + } + + Component.onDestruction: { + navigationManager.unregisterItem(gameTile) + } MouseArea { anchors.fill: parent onClicked: { - bottomWindow.focusRegion = bottomWindow.mainMenuNavRegions.GamesList - gamesList.currentIndex = index + navigationManager.focusItem(gameTile) } } } @@ -165,9 +161,32 @@ QtObject { model: 5 Rectangle { + id: dockButton + width: 60 height: 60 - color: bottomWindow.focusRegion === bottomWindow.mainMenuNavRegions.DockMenu && index === bottomWindow.dockMenuIndex ? "blue" : "white" + + color: navigationManager.currentItem === dockButton ? "blue" : "white" + + function navFocus() { + bottomWindow.dockMenuIndex = index + } + + Component.onCompleted: { + navigationManager.registerItem(dockButton) + } + + Component.onDestruction: { + navigationManager.unregisterItem(dockButton) + } + + MouseArea { + anchors.fill: parent + + onClicked: { + navigationManager.focusItem(dockButton) + } + } } } } diff --git a/shell/qml/NavigationManager.qml b/shell/qml/NavigationManager.qml new file mode 100644 index 0000000..eaa5199 --- /dev/null +++ b/shell/qml/NavigationManager.qml @@ -0,0 +1,139 @@ +import QtQuick +import NomadInput 1.0 + +QtObject { + id: manager + + property Item rootItem: null + property list items + property var currentItem: null + + function focusItem(item) { + if (!item) { + return + } + + currentItem = item + + if (typeof item.navFocus === "function") { + item.navFocus() + } + } + + function registerItem(item) { + if (!item) { + return + } + + for (let i = 0; i < items.length; i++) { + if (items[i] === item) { + return + } + } + + items.push(item) + } + + function unregisterItem(item) { + if (!item) { + return + } + + for (let i = 0; i < items.length; i++) { + if (items[i] === item) { + items.splice(i, 1) + + if (currentItem === item) { + currentItem = null + } + + return; + } + } + } + + function move(action) { + if (!currentItem || !rootItem) { + return + } + + let dx = 0 + let dy = 0 + + switch(action) { + case InputManager.Left: + dx = -1 + break + + case InputManager.Right: + dx = 1 + break + + case InputManager.Up: + dy = -1 + break + + case InputManager.Down: + dy = 1 + break + + default: + return + } + + let currentCenter = currentItem.mapToItem(rootItem, currentItem.width / 2, currentItem.height / 2) + + let bestItem = null + let bestScore = Infinity + + for (let i = 0; i < items.length; i++) { + let candidate = items[i] + + if (!candidate) { + continue + } + + if (candidate === currentItem) { + continue + } + + if (!candidate.visible || !candidate.enabled) { + continue + } + + let candidateCenter = candidate.mapToItem(rootItem, candidate.width / 2, candidate.height / 2) + + let differenceX = candidateCenter.x - currentCenter.x + let differenceY = candidateCenter.y - currentCenter.y + + let forwardDistance + let sidewaysDistance + + if (dx !== 0) { + forwardDistance = differenceX * dx + sidewaysDistance = Math.abs(differenceY) + } else { + forwardDistance = differenceY * dy + sidewaysDistance = Math.abs(differenceX) + } + + if (forwardDistance <= 0) { + continue + } + + let score = forwardDistance + sidewaysDistance * 2 + + if (score < bestScore) { + bestScore = score + bestItem = candidate + } + console.log("candidate:", candidate, "center:", candidateCenter.x, candidateCenter.y, "difference:", differenceX, differenceY) + } + + if (bestItem) { + focusItem(bestItem) + } + + console.log("MOVE", inputManager.actionName(action), "current:", currentItem, "center:", currentCenter.x, currentCenter.y) + } +} \ No newline at end of file