diff --git a/_sass/styles.scss b/_sass/styles.scss index 36b12ee6..e6aca98b 100644 --- a/_sass/styles.scss +++ b/_sass/styles.scss @@ -164,3 +164,22 @@ table tr:nth-child(2n) { .list-group { --bs-list-group-bg: var(--navbar-col); } + +/* gamepad selector cards */ +.gamepad-selector-card { + transition: all 0.2s ease-in-out; + user-select: none; +} + +.gamepad-selector-card:hover { + transform: translateY(-2px); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3); +} + +.gamepad-selector-card.border-primary { + box-shadow: 0 0 10px rgba(13, 110, 253, 0.3); +} + +.gamepad-selector-card.border-primary:hover { + box-shadow: 0 4px 12px rgba(13, 110, 253, 0.5); +} diff --git a/assets/js/gamepad-tester.js b/assets/js/gamepad-tester.js index 551c598d..ecad6797 100644 --- a/assets/js/gamepad-tester.js +++ b/assets/js/gamepad-tester.js @@ -20,13 +20,15 @@ document.addEventListener('DOMContentLoaded', function() { // Setup gamepad event listeners window.addEventListener("gamepadconnected", function(e) { gamepads[e.gamepad.index] = e.gamepad; - updateGamepadSelector(); + + // Always activate the newly connected gamepad + activeGamepadIndex = e.gamepad.index; + + updateGamepadSelector(); // This will highlight the active card updateStatus(`Gamepad ${e.gamepad.id} connected`); - // If this is the first gamepad, activate it - if (activeGamepadIndex === null) { - activeGamepadIndex = e.gamepad.index; - gamepadSelector.value = activeGamepadIndex; + // Start the loop if it's not already running + if (!animationFrameId) { startGamepadLoop(); } }); @@ -45,18 +47,21 @@ document.addEventListener('DOMContentLoaded', function() { const remainingIndices = Object.keys(gamepads); if (remainingIndices.length > 0) { activeGamepadIndex = Number.parseInt(remainingIndices[0]); - gamepadSelector.value = activeGamepadIndex; } else { stopGamepadLoop(); } } }); - // Event listener for gamepad selector change - gamepadSelector.addEventListener('change', function() { - activeGamepadIndex = Number.parseInt(this.value); - initGamepadButtons(); - initGamepadAxes(); + // Event delegation for gamepad selector cards + gamepadSelector.addEventListener('click', function(e) { + const card = e.target.closest('.gamepad-selector-card'); + if (card) { + activeGamepadIndex = Number.parseInt(card.dataset.index); + updateGamepadSelector(); // Re-render to update active state + initGamepadButtons(); + initGamepadAxes(); + } }); // Event listeners for vibration controls @@ -76,7 +81,7 @@ document.addEventListener('DOMContentLoaded', function() { document.getElementById('strong-value').textContent = this.value; }); - // Update gamepad selector dropdown + // Update gamepad selector buttons function updateGamepadSelector() { gamepadSelector.innerHTML = ''; @@ -87,20 +92,56 @@ document.addEventListener('DOMContentLoaded', function() { gamepadInfoSection.style.display = 'flex'; gamepadIndices.forEach(index => { - const option = document.createElement('option'); - option.value = index; - option.textContent = `${gamepads[index].id} (Index: ${index})`; - gamepadSelector.appendChild(option); + const card = document.createElement('div'); + card.className = 'card gamepad-selector-card'; + card.dataset.index = index; + card.style.cursor = 'pointer'; + card.style.minWidth = '200px'; + card.style.maxWidth = '300px'; + card.style.borderWidth = '2px'; // Always use 2px border + + const isActive = activeGamepadIndex !== null && Number.parseInt(index) === activeGamepadIndex; + + // Mark active gamepad card + if (isActive) { + card.classList.add('border-primary'); + card.style.backgroundColor = 'rgba(13, 110, 253, 0.15)'; + } else { + card.classList.add('border-secondary'); + card.style.backgroundColor = 'rgba(255, 255, 255, 0.05)'; + } + + const cardBody = document.createElement('div'); + cardBody.className = 'card-body p-2'; + + const gamepadInfo = gamepads[index]; + const typeInfo = gamepadHelper.getGamepadInfo(gamepadInfo.id); + + cardBody.innerHTML = ` +
+
+ +
+
+
${typeInfo.name}
+
Index: ${index}
+
${gamepadInfo.buttons.length} buttons, ${gamepadInfo.axes.length} axes
+
+
+ ${isActive ? 'Active' : ''} +
+
+ `; + + card.appendChild(cardBody); + gamepadSelector.appendChild(card); }); - // Select the active gamepad + // Initialize buttons and axes for the selected gamepad if (activeGamepadIndex !== null) { - gamepadSelector.value = activeGamepadIndex; + initGamepadButtons(); + initGamepadAxes(); } - - // Initialize buttons and axes for the selected gamepad - initGamepadButtons(); - initGamepadAxes(); } else { gamepadSelectorContainer.style.display = 'none'; gamepadInfoSection.style.display = 'none'; @@ -481,18 +522,17 @@ document.addEventListener('DOMContentLoaded', function() { // Initial check for already connected gamepads const initialGamepads = navigator.getGamepads(); - for (let i = 0; i < initialGamepads.length; i++) { - if (initialGamepads[i]) { - gamepads[initialGamepads[i].index] = initialGamepads[i]; + for (const element of initialGamepads) { + if (element) { + gamepads[element.index] = element; } } - updateGamepadSelector(); - - // If we have gamepads already, start the loop + // If we have gamepads already, activate the first one if (Object.keys(gamepads).length > 0) { activeGamepadIndex = Number.parseInt(Object.keys(gamepads)[0]); updateStatus(`Gamepad ${gamepads[activeGamepadIndex].id} connected`); + updateGamepadSelector(); // Update after setting activeGamepadIndex startGamepadLoop(); } }); diff --git a/gamepad-tester.html b/gamepad-tester.html index 666fa0b7..6c40d656 100644 --- a/gamepad-tester.html +++ b/gamepad-tester.html @@ -26,11 +26,8 @@

Gamepad Tester