mirror of
https://github.com/gradenGnostic/LegacyLauncher.git
synced 2026-04-26 08:57:33 +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
|
||||
- **extract-zip**: ZIP archive extraction
|
||||
- **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
|
||||
|
||||
|
|
|
|||
10
index.html
10
index.html
|
|
@ -274,6 +274,16 @@
|
|||
<option value="xbox">Xbox Style (A = Confirm, B = Cancel)</option>
|
||||
<option value="nintendo">Nintendo Style (B = Confirm, A = Cancel)</option>
|
||||
</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 class="flex gap-4 w-full mt-4">
|
||||
|
|
|
|||
54
renderer.js
54
renderer.js
|
|
@ -617,6 +617,52 @@ function applyRepoPreset() {
|
|||
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 () => {
|
||||
try {
|
||||
await migrateLegacyConfig();
|
||||
|
|
@ -641,6 +687,7 @@ window.onload = async () => {
|
|||
if (serverCheck) serverCheck.checked = currentInstance.isServer;
|
||||
if (installInput) installInput.value = currentInstance.installPath;
|
||||
if (controllerLayoutSelect) controllerLayoutSelect.value = await Store.get('legacy_controller_layout_mode', 'auto');
|
||||
initControllerLayoutPresets();
|
||||
syncRepoPresetFromInput();
|
||||
|
||||
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');
|
||||
layoutSelect.value = savedLayoutMode;
|
||||
GamepadManager.setControlLayoutMode(savedLayoutMode);
|
||||
applyControllerLayoutPresetState(savedLayoutMode);
|
||||
}
|
||||
syncRepoPresetFromInput();
|
||||
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_controller_layout_mode', controllerLayoutMode);
|
||||
GamepadManager.setControlLayoutMode(controllerLayoutMode);
|
||||
applyControllerLayoutPresetState(controllerLayoutMode);
|
||||
applyTheme(isClassic);
|
||||
applySteamDeckMode(isSteamDeckMode);
|
||||
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');
|
||||
GamepadManager.setControlLayoutMode(mode);
|
||||
const select = document.getElementById('controller-layout-select');
|
||||
if (select) select.value = mode;
|
||||
if (select) {
|
||||
select.value = mode;
|
||||
applyControllerLayoutPresetState(mode);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
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 {
|
||||
width: 420px;
|
||||
}
|
||||
|
|
@ -671,6 +683,92 @@ body.steamdeck-mode .sidebar {
|
|||
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 {
|
||||
outline: 2px solid #fff;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue