mirror of
https://github.com/gradenGnostic/LegacyLauncher.git
synced 2026-08-02 18:22:38 +00:00
Merge pull request #5 from rubiidev18alt/codex/integrate-controller-button-assets-into-ui
Add controller layout preset UI with interactions and sprite attribution
This commit is contained in:
commit
bb721c5ae0
|
|
@ -76,7 +76,11 @@ The launcher supports several compatibility options for Linux:
|
||||||
- **discord-rpc**: Discord Rich Presence integration
|
- **discord-rpc**: Discord Rich Presence integration
|
||||||
- **extract-zip**: ZIP archive extraction
|
- **extract-zip**: ZIP archive extraction
|
||||||
- **Tailwind CSS**: UI styling (via CDN)
|
- **Tailwind CSS**: UI styling (via CDN)
|
||||||
- **UI Sounds**: Using the free version of [JDSherbert's Ultimate UI SFX Pack on itch.io](https://jdsherbert.itch.io/ultimate-ui-sfx-pack)
|
|
||||||
|
## Assets
|
||||||
|
|
||||||
|
- Controller button sprites: [greatdocbrown](https://greatdocbrown.itch.io/gamepad-ui)
|
||||||
|
- UI Sounds: Using the free version of [JDSherbert's Ultimate UI SFX Pack on itch.io](https://jdsherbert.itch.io/ultimate-ui-sfx-pack)
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
|
|
|
||||||
10
index.html
10
index.html
|
|
@ -274,6 +274,16 @@
|
||||||
<option value="xbox">Xbox Style (A = Confirm, B = Cancel)</option>
|
<option value="xbox">Xbox Style (A = Confirm, B = Cancel)</option>
|
||||||
<option value="nintendo">Nintendo Style (B = Confirm, A = Cancel)</option>
|
<option value="nintendo">Nintendo Style (B = Confirm, A = Cancel)</option>
|
||||||
</select>
|
</select>
|
||||||
|
<div id="controller-layout-presets" class="controller-layout-presets" role="radiogroup" aria-label="Controller layout presets">
|
||||||
|
<button type="button" class="controller-layout-preset nav-item" data-layout="xbox" tabindex="0" aria-pressed="false">
|
||||||
|
<span class="controller-layout-icon controller-layout-icon-xbox" aria-hidden="true"></span>
|
||||||
|
<span class="controller-layout-text">Xbox Style (A = Confirm, B = Cancel)</span>
|
||||||
|
</button>
|
||||||
|
<button type="button" class="controller-layout-preset nav-item" data-layout="nintendo" tabindex="0" aria-pressed="false">
|
||||||
|
<span class="controller-layout-icon controller-layout-icon-switch" aria-hidden="true"></span>
|
||||||
|
<span class="controller-layout-text">Nintendo Style (B = Confirm, A = Cancel)</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex gap-4 w-full mt-4">
|
<div class="flex gap-4 w-full mt-4">
|
||||||
|
|
|
||||||
54
renderer.js
54
renderer.js
|
|
@ -617,6 +617,52 @@ function applyRepoPreset() {
|
||||||
repoInput.value = presetSelect.value;
|
repoInput.value = presetSelect.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function applyControllerLayoutPresetState(layoutMode = 'auto') {
|
||||||
|
const presets = document.querySelectorAll('.controller-layout-preset');
|
||||||
|
const activeLayout = layoutMode === 'nintendo' ? 'nintendo' : 'xbox';
|
||||||
|
presets.forEach((preset) => {
|
||||||
|
const isActive = preset.dataset.layout === activeLayout;
|
||||||
|
preset.classList.toggle('active', isActive);
|
||||||
|
preset.setAttribute('aria-pressed', isActive ? 'true' : 'false');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function initControllerLayoutPresets() {
|
||||||
|
const layoutSelect = document.getElementById('controller-layout-select');
|
||||||
|
if (!layoutSelect) return;
|
||||||
|
|
||||||
|
const presets = document.querySelectorAll('.controller-layout-preset');
|
||||||
|
presets.forEach((preset) => {
|
||||||
|
const pressOn = () => preset.classList.add('is-pressed');
|
||||||
|
const pressOff = () => preset.classList.remove('is-pressed');
|
||||||
|
|
||||||
|
preset.addEventListener('pointerdown', pressOn);
|
||||||
|
preset.addEventListener('pointerup', pressOff);
|
||||||
|
preset.addEventListener('pointerleave', pressOff);
|
||||||
|
preset.addEventListener('blur', pressOff);
|
||||||
|
|
||||||
|
preset.addEventListener('keydown', (event) => {
|
||||||
|
if (event.key === 'Enter' || event.key === ' ') pressOn();
|
||||||
|
});
|
||||||
|
preset.addEventListener('keyup', (event) => {
|
||||||
|
if (event.key === 'Enter' || event.key === ' ') pressOff();
|
||||||
|
});
|
||||||
|
|
||||||
|
preset.addEventListener('click', () => {
|
||||||
|
const selectedLayout = preset.dataset.layout || 'xbox';
|
||||||
|
layoutSelect.value = selectedLayout;
|
||||||
|
applyControllerLayoutPresetState(selectedLayout);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
layoutSelect.addEventListener('change', () => {
|
||||||
|
applyControllerLayoutPresetState(layoutSelect.value || 'auto');
|
||||||
|
});
|
||||||
|
|
||||||
|
applyControllerLayoutPresetState(layoutSelect.value || 'auto');
|
||||||
|
}
|
||||||
|
|
||||||
window.onload = async () => {
|
window.onload = async () => {
|
||||||
try {
|
try {
|
||||||
await migrateLegacyConfig();
|
await migrateLegacyConfig();
|
||||||
|
|
@ -641,6 +687,7 @@ window.onload = async () => {
|
||||||
if (serverCheck) serverCheck.checked = currentInstance.isServer;
|
if (serverCheck) serverCheck.checked = currentInstance.isServer;
|
||||||
if (installInput) installInput.value = currentInstance.installPath;
|
if (installInput) installInput.value = currentInstance.installPath;
|
||||||
if (controllerLayoutSelect) controllerLayoutSelect.value = await Store.get('legacy_controller_layout_mode', 'auto');
|
if (controllerLayoutSelect) controllerLayoutSelect.value = await Store.get('legacy_controller_layout_mode', 'auto');
|
||||||
|
initControllerLayoutPresets();
|
||||||
syncRepoPresetFromInput();
|
syncRepoPresetFromInput();
|
||||||
|
|
||||||
if (process.platform === 'linux' || process.platform === 'darwin') {
|
if (process.platform === 'linux' || process.platform === 'darwin') {
|
||||||
|
|
@ -1349,6 +1396,7 @@ async function toggleOptions(show) {
|
||||||
const savedLayoutMode = await Store.get('legacy_controller_layout_mode', 'auto');
|
const savedLayoutMode = await Store.get('legacy_controller_layout_mode', 'auto');
|
||||||
layoutSelect.value = savedLayoutMode;
|
layoutSelect.value = savedLayoutMode;
|
||||||
GamepadManager.setControlLayoutMode(savedLayoutMode);
|
GamepadManager.setControlLayoutMode(savedLayoutMode);
|
||||||
|
applyControllerLayoutPresetState(savedLayoutMode);
|
||||||
}
|
}
|
||||||
syncRepoPresetFromInput();
|
syncRepoPresetFromInput();
|
||||||
document.activeElement?.blur(); modal.style.display = 'flex'; modal.style.opacity = '1';
|
document.activeElement?.blur(); modal.style.display = 'flex'; modal.style.opacity = '1';
|
||||||
|
|
@ -1471,6 +1519,7 @@ async function saveOptions() {
|
||||||
await Store.set('legacy_steamdeck_mode', isSteamDeckMode);
|
await Store.set('legacy_steamdeck_mode', isSteamDeckMode);
|
||||||
await Store.set('legacy_controller_layout_mode', controllerLayoutMode);
|
await Store.set('legacy_controller_layout_mode', controllerLayoutMode);
|
||||||
GamepadManager.setControlLayoutMode(controllerLayoutMode);
|
GamepadManager.setControlLayoutMode(controllerLayoutMode);
|
||||||
|
applyControllerLayoutPresetState(controllerLayoutMode);
|
||||||
applyTheme(isClassic);
|
applyTheme(isClassic);
|
||||||
applySteamDeckMode(isSteamDeckMode);
|
applySteamDeckMode(isSteamDeckMode);
|
||||||
await saveInstancesToStore(); toggleOptions(false); fetchGitHubData(); updatePlayButtonText(); showToast("Settings Saved");
|
await saveInstancesToStore(); toggleOptions(false); fetchGitHubData(); updatePlayButtonText(); showToast("Settings Saved");
|
||||||
|
|
@ -1716,7 +1765,10 @@ async function loadControllerLayoutMode() {
|
||||||
const mode = await Store.get('legacy_controller_layout_mode', 'auto');
|
const mode = await Store.get('legacy_controller_layout_mode', 'auto');
|
||||||
GamepadManager.setControlLayoutMode(mode);
|
GamepadManager.setControlLayoutMode(mode);
|
||||||
const select = document.getElementById('controller-layout-select');
|
const select = document.getElementById('controller-layout-select');
|
||||||
if (select) select.value = mode;
|
if (select) {
|
||||||
|
select.value = mode;
|
||||||
|
applyControllerLayoutPresetState(mode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function applySteamDeckMode(enabled) {
|
function applySteamDeckMode(enabled) {
|
||||||
|
|
|
||||||
98
style.css
98
style.css
|
|
@ -452,6 +452,18 @@ body.steamdeck-mode .nav-item:focus {
|
||||||
box-shadow: 0 0 0 2px rgba(255,255,255,0.95), 0 0 22px rgba(255,255,255,0.7), 0 0 34px rgba(90,170,255,0.36) !important;
|
box-shadow: 0 0 0 2px rgba(255,255,255,0.95), 0 0 22px rgba(255,255,255,0.7), 0 0 34px rgba(90,170,255,0.36) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
body.steamdeck-mode .controller-layout-preset {
|
||||||
|
min-height: 62px;
|
||||||
|
font-size: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.steamdeck-mode .controller-layout-icon {
|
||||||
|
transform: scale(1.3);
|
||||||
|
transform-origin: left center;
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
body.steamdeck-mode .sidebar {
|
body.steamdeck-mode .sidebar {
|
||||||
width: 420px;
|
width: 420px;
|
||||||
}
|
}
|
||||||
|
|
@ -671,6 +683,92 @@ body.steamdeck-mode .sidebar {
|
||||||
border-color: #fff !important;
|
border-color: #fff !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.controller-layout-presets {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: 10px;
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.controller-layout-preset {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 52px;
|
||||||
|
border: 2px solid #555;
|
||||||
|
background: #0a0a0a;
|
||||||
|
color: #fff;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 19px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.controller-layout-preset:hover,
|
||||||
|
.controller-layout-preset:focus {
|
||||||
|
border-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.controller-layout-preset.active {
|
||||||
|
border-color: #55ff55;
|
||||||
|
box-shadow: inset 0 0 0 1px #55ff55;
|
||||||
|
background: #111;
|
||||||
|
}
|
||||||
|
|
||||||
|
.controller-layout-icon {
|
||||||
|
--sprite-x: -51px;
|
||||||
|
--sprite-y: -531px;
|
||||||
|
--sprite-w: 26px;
|
||||||
|
--sprite-h: 26px;
|
||||||
|
--sprite-pressed-x: -51px;
|
||||||
|
--sprite-pressed-y: -563px;
|
||||||
|
width: var(--sprite-w);
|
||||||
|
height: var(--sprite-h);
|
||||||
|
flex: 0 0 var(--sprite-w);
|
||||||
|
display: inline-block;
|
||||||
|
overflow: hidden;
|
||||||
|
image-rendering: pixelated;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: 560px 640px;
|
||||||
|
background-position: var(--sprite-x) var(--sprite-y);
|
||||||
|
}
|
||||||
|
|
||||||
|
.controller-layout-preset:hover .controller-layout-icon,
|
||||||
|
.controller-layout-preset:focus .controller-layout-icon,
|
||||||
|
.controller-layout-preset.active .controller-layout-icon,
|
||||||
|
.controller-layout-preset.is-pressed .controller-layout-icon {
|
||||||
|
background-position: var(--sprite-pressed-x) var(--sprite-pressed-y);
|
||||||
|
}
|
||||||
|
|
||||||
|
.controller-layout-icon-xbox {
|
||||||
|
background-image: url('gdb-xbox-2.png');
|
||||||
|
/* action_button_189 (idle) + action_button_228 (pressed) from spritesheet JSON */
|
||||||
|
--sprite-x: -51px;
|
||||||
|
--sprite-y: -531px;
|
||||||
|
--sprite-w: 26px;
|
||||||
|
--sprite-h: 26px;
|
||||||
|
--sprite-pressed-x: -51px;
|
||||||
|
--sprite-pressed-y: -563px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.controller-layout-icon-switch {
|
||||||
|
background-image: url('gdb-switch-2.png');
|
||||||
|
/* Switch sheet uses the same atlas layout coordinates */
|
||||||
|
--sprite-x: -51px;
|
||||||
|
--sprite-y: -531px;
|
||||||
|
--sprite-w: 26px;
|
||||||
|
--sprite-h: 26px;
|
||||||
|
--sprite-pressed-x: -51px;
|
||||||
|
--sprite-pressed-y: -563px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.controller-layout-text {
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
#server-checkbox.focused {
|
#server-checkbox.focused {
|
||||||
outline: 2px solid #fff;
|
outline: 2px solid #fff;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue