├── README.md
├── aspect-ratio-hint.svg
├── css
├── elements.css
├── reset.css
└── style.css
├── img
├── 1.png
├── 2-button.png
├── 2-button_gba.png
├── 2-button_gba_modified.png
├── 2.png
├── 3-button.png
├── 4-button.png
├── 6-button.png
├── 6-button_genesis.png
├── A.png
├── A_nxengine.png
├── A_right_nxengine.png
├── A_wswan.png
├── B.png
├── B_wswan.png
├── C.png
├── C_pokemini.png
├── C_pokemini_smaller.png
├── D.png
├── E.png
├── F.png
├── I.png
├── II.png
├── III.png
├── IV.png
├── L.png
├── L1.png
├── L1_down.png
├── L1_down_smaller.png
├── L1_smaller.png
├── L2.png
├── L2_up.png
├── L3.png
├── L3_smaller.png
├── L_and_R.png
├── L_and_R_down.png
├── L_and_R_left.png
├── L_down.png
├── L_down_smaller_gba.png
├── L_face.png
├── L_right.png
├── L_right_smaller.png
├── L_smaller.png
├── R.png
├── R1.png
├── R1_smaller.png
├── R2.png
├── R2_up.png
├── R3_smaller.png
├── R_down.png
├── R_down_smaller.png
├── R_face.png
├── R_smaller.png
├── S_nxengine.png
├── V.png
├── VI.png
├── W_nxengine.png
├── X.png
├── X1_wswan.png
├── X2_wswan.png
├── X3_wswan.png
├── X4_wswan.png
├── Y.png
├── Y1_wswan.png
├── Y2_wswan.png
├── Y3_wswan.png
├── Y4_wswan.png
├── Z.png
├── Z_down.png
├── Z_down_smaller.png
├── analog.png
├── c_down.png
├── c_left.png
├── c_right.png
├── c_up.png
├── circle.png
├── coin.png
├── cross.png
├── digital.png
├── dpad-down.png
├── dpad-down_no-mark.png
├── dpad-down_psx.png
├── dpad-down_psx_no-mark.png
├── dpad-left.png
├── dpad-left_no-mark.png
├── dpad-left_psx.png
├── dpad-left_psx_no-mark.png
├── dpad-right.png
├── dpad-right_no-mark.png
├── dpad-right_psx.png
├── dpad-right_psx_no-mark.png
├── dpad-up.png
├── dpad-up_no-mark.png
├── dpad-up_psx.png
├── dpad-up_psx_no-mark.png
├── fast_forward.png
├── genesis_mode.png
├── hide.png
├── keyboard.png
├── no-analog.png
├── overlay-A.png
├── overlay-B.png
├── overlay-C.png
├── overlay-D.png
├── pause_square_text.png
├── pokemini_power.png
├── pokemini_shake.png
├── reset_square_text.png
├── retroarch-icon.svg
├── rgui.png
├── rotate.png
├── select_psx.png
├── select_rounded_big.png
├── select_square_text.png
├── show.png
├── square.png
├── start.png
├── start_dc.png
├── start_genesis.png
├── start_psx.png
├── start_rounded.png
├── start_rounded_big.png
├── test.png
├── thumbstick-background.png
├── thumbstick-pad-hollow.png
├── thumbstick-pad.png
├── thumbstick-pad_arcade.png
└── triangle.png
├── index.html
└── js
├── config-handler.js
├── defaults.js
├── editor.js
└── file-input-helper.js
/README.md:
--------------------------------------------------------------------------------
1 | # RetroPad Editor
2 | Online tool to create and edit onscreen gamepads for RetroArch.
3 |
4 | https://valent-in.github.io/retropad-editor/
5 |
6 | Features:
7 | - Create, move, resize buttons of virtual gamepad.
8 | - Multiple layers; auto switch orientation for portrait overlays.
9 | - Fix overlay aspect ratio.
10 | - Import image resources in addition to 'flat' image set.
11 | - Scale viewport for comfortable editing on small screens.
12 | - Display sensitivity range for analog sticks.
13 | - Old format support (auto normalize integer overlays).
14 | - Non-fullscreen overlays support.
15 |
16 | Config file and images must be stored in same folder.
17 | On most Android devices RetroArch will open config only from internal memory even access to sdcard is granted.
18 | - This editor can load but NOT save configs with image paths (img/A.png will be saved as A.png)
19 | ---
20 | Libretro Docs: https://docs.libretro.com/development/retroarch/input/overlay/
21 |
22 | Used media resources from https://github.com/libretro/common-overlays (button images) and https://github.com/libretro/RetroArch (icon).
23 |
24 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License version 3.
25 |
26 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY.
--------------------------------------------------------------------------------
/aspect-ratio-hint.svg:
--------------------------------------------------------------------------------
1 |
2 |
278 |
--------------------------------------------------------------------------------
/css/elements.css:
--------------------------------------------------------------------------------
1 | button {
2 | background-color: #08d;
3 | border: 1px solid #06b;
4 | border-radius: 3px;
5 | color: white;
6 | padding: 2px 4px 2px 4px;
7 | display: inline-block;
8 | cursor: pointer;
9 | }
10 |
11 | button:hover {
12 | background-color: #19e;
13 | }
14 |
15 | button:active {
16 | background-color: #06b;
17 | }
18 |
19 | button.danger {
20 | background-color: #d03;
21 | border: 1px solid #b01;
22 | }
23 |
24 | button.danger:hover {
25 | background-color: #e14;
26 | }
27 |
28 | button.danger:active {
29 | background-color: #b01;
30 | }
31 |
32 | button.close-button {
33 | box-sizing: content-box;
34 | height: 1em;
35 | width: 1em;
36 | padding: 1px;
37 | background-image: url("data:image/svg+xml;utf8,");
38 | background-position: center center;
39 | background-repeat: no-repeat;
40 | background-size: 100% 100%;
41 | }
42 |
43 | button.important {
44 | background-color: #0a1;
45 | border: 1px solid #080;
46 | }
47 |
48 | button.important:hover {
49 | background-color: #1b2;
50 | }
51 |
52 | button.important:active {
53 | background-color: #080;
54 | }
55 |
56 | button.empty {
57 | background-color: rgba(0, 0, 0, 0);
58 | color: #06b;
59 | }
60 |
61 | button.empty:hover {
62 | background-color: rgba(0, 0, 255, 0.1);
63 | }
64 |
65 | button.empty:active {
66 | background-color: rgba(0, 0, 255, 0.2);
67 | }
68 |
69 | button.empty.expander {
70 | padding-right: 1.5em;
71 | position: relative;
72 | background-image: url("data:image/svg+xml;utf8,");
73 | background-position: center right 0.5em;
74 | background-repeat: no-repeat;
75 | background-size: auto 25%;
76 | }
77 |
78 | button.empty.expander.expanded {
79 | background-image: url("data:image/svg+xml;utf8,");
80 | }
81 |
82 | button:focus {
83 | box-shadow: 0 0 0 2px rgba(180, 200, 220, 0.7);
84 | outline: none;
85 | }
86 |
87 | select {
88 | appearance: none;
89 | -moz-appearance: none;
90 | -webkit-appearance: none;
91 | border: 1px solid #ccd;
92 | background-color: #e9e9f1;
93 | color: #777;
94 | box-shadow: none;
95 | padding: 4px;
96 | padding-right: 1em;
97 | border-radius: 3px;
98 | position: relative;
99 | background-image: url("data:image/svg+xml;utf8,");
100 | background-position: center right 0.5em;
101 | background-repeat: no-repeat;
102 | background-size: auto 25%;
103 | cursor: pointer;
104 | }
105 |
106 | option {
107 | color: initial;
108 | background-color: initial;
109 | }
110 |
111 | select:focus {
112 | box-shadow: 0 0 0 2px rgba(180, 200, 220, 0.3);
113 | outline: none;
114 | }
115 |
116 | select:hover {
117 | border-color: #aab;
118 | }
119 |
120 | input[type=checkbox] {
121 | appearance: none;
122 | -moz-appearance: none;
123 | -webkit-appearance: none;
124 | border: 1px solid #ccd;
125 | border-radius: 3px;
126 | background-color: #e9e9f1;
127 | box-shadow: none;
128 | width: 1em;
129 | height: 1em;
130 | vertical-align: text-bottom;
131 | margin-bottom: 1px;
132 | margin-top: -1px;
133 | box-sizing: content-box;
134 | cursor: pointer;
135 | }
136 |
137 | input[type=checkbox]:checked {
138 | background-image: url("data:image/svg+xml;utf8,");
139 | background-position: center center;
140 | background-repeat: no-repeat;
141 | background-size: 90% 90%;
142 | }
143 |
144 | input[type=radio] {
145 | appearance: none;
146 | -moz-appearance: none;
147 | -webkit-appearance: none;
148 | border: 1px solid #ccd;
149 | border-radius: 50%;
150 | background-color: #e9e9f1;
151 | box-shadow: none;
152 | width: 1em;
153 | height: 1em;
154 | vertical-align: text-bottom;
155 | margin-bottom: 1px;
156 | margin-top: -1px;
157 | box-sizing: content-box;
158 | cursor: pointer;
159 | }
160 |
161 | input[type=radio]:checked {
162 | background-image: url("data:image/svg+xml;utf8,");
163 | background-position: center center;
164 | background-repeat: no-repeat;
165 | background-size: 100% 100%;
166 | }
167 |
168 | input[type=radio]:focus, input[type=checkbox]:focus {
169 | box-shadow: 0 0 0 2px rgba(180, 200, 220, 0.3);
170 | outline: none;
171 | }
172 |
173 | input[type=radio]:hover, input[type=checkbox]:hover {
174 | border-color: #aab;
175 | }
176 |
177 | input[type=number] {
178 | -webkit-appearance: none;
179 | -moz-appearance: textfield;
180 | appearance: textfield;
181 | border: 1px solid #ccd;
182 | background-color: #f7f7fa;
183 | color: #777;
184 | padding: 4px;
185 | border-radius: 3px;
186 | }
187 |
188 | input[type=text] {
189 | -webkit-appearance: none;
190 | -moz-appearance: none;
191 | appearance: none;
192 | border: 1px solid #ccd;
193 | background-color: #f7f7fa;
194 | color: #777;
195 | box-shadow: none;
196 | padding: 4px;
197 | border-radius: 3px;
198 | }
199 |
200 | input[type=number]::-webkit-inner-spin-button {
201 | -webkit-appearance: none;
202 | }
203 |
204 | input[type=number]:hover, input[type=text]:hover {
205 | border-color: #aab;
206 | }
207 |
208 | input[type=number]:invalid, input[type=text]:invalid {
209 | background-color: #f7d7da;
210 | box-shadow: none;
211 | }
212 |
213 | input[type=number]:focus, input[type=text]:focus {
214 | box-shadow: 0 0 0 2px rgba(180, 200, 220, 0.3);
215 | outline: none;
216 | }
217 |
218 | input[type=range] {
219 | -webkit-appearance: none;
220 | background: transparent;
221 | height: 22px;
222 | padding: 0;
223 | cursor: pointer;
224 | }
225 |
226 | input[type=range]::-webkit-slider-thumb {
227 | -webkit-appearance: none;
228 | }
229 |
230 | input[type=range]:focus {
231 | outline: none;
232 | }
233 |
234 | input[type=range]::-ms-track {
235 | cursor: pointer;
236 | background: transparent;
237 | border-color: transparent;
238 | color: transparent;
239 | }
240 |
241 | input[type=range]::-webkit-slider-thumb {
242 | -webkit-appearance: none;
243 | border: 6px solid #08d;
244 | height: 10px;
245 | width: 5px;
246 | border-radius: 3px;
247 | background: #e9e9f1;
248 | cursor: pointer;
249 | margin-top: -8px;
250 | box-shadow: none;
251 | box-sizing: content-box;
252 | }
253 |
254 | input[type=range]:active::-webkit-slider-thumb {
255 | background: #ffffff;
256 | }
257 |
258 | input[type=range]::-moz-range-thumb {
259 | box-shadow: none;
260 | border: 6px solid #08d;
261 | height: 10px;
262 | width: 5px;
263 | border-radius: 3px;
264 | background: #e9e9f1;
265 | cursor: pointer;
266 | }
267 |
268 | input[type=range]:active::-moz-range-thumb {
269 | background: #ffffff;
270 | }
271 |
272 | input[type=range]::-ms-thumb {
273 | box-shadow: none;
274 | border: 6px solid #08d;
275 | height: 10px;
276 | width: 5px;
277 | border-radius: 3px;
278 | background: #e9e9f1;
279 | cursor: pointer;
280 | margin-bottom: -8px;
281 | margin-top: -8px;
282 | box-sizing: content-box;
283 | }
284 |
285 | input[type=range]:active::-ms-thumb {
286 | background: #ffffff;
287 | }
288 |
289 | input[type=range]::-webkit-slider-runnable-track {
290 | width: 100%;
291 | height: 8px;
292 | cursor: pointer;
293 | border: 1px solid #ccd;
294 | background-color: #e9e9f1;
295 | border-radius: 4px;
296 | box-sizing: border-box;
297 | }
298 |
299 | input[type=range]:focus::-webkit-slider-runnable-track {
300 | box-shadow: 0 0 0 2px rgba(180, 200, 220, 0.3);
301 | }
302 |
303 | input[type=range]::-moz-range-track {
304 | width: 100%;
305 | height: 8px;
306 | cursor: pointer;
307 | border: 1px solid #ccd;
308 | background-color: #e9e9f1;
309 | border-radius: 4px;
310 | box-sizing: border-box;
311 | }
312 |
313 | input[type=range]:focus::-moz-range-track {
314 | box-shadow: 0 0 0 2px rgba(180, 200, 220, 0.3);
315 | }
316 |
317 | input[type=range]::-ms-track {
318 | width: 100%;
319 | height: 8px;
320 | cursor: pointer;
321 | border: 1px solid #ccd;
322 | background-color: #e9e9f1;
323 | border-radius: 4px;
324 | box-sizing: border-box;
325 | box-shadow: none;
326 | }
327 |
328 | input[type=range]:focus::-ms-track {
329 | box-shadow: 0 0 0 2px rgba(180, 200, 220, 0.3);
330 | }
331 |
332 | input[type=range]::-ms-fill-lower {
333 | width: 100%;
334 | height: 8px;
335 | cursor: pointer;
336 | border: 1px solid #ccd;
337 | background-color: #e9e9f1;
338 | border-radius: 4px;
339 | box-sizing: border-box;
340 | }
341 |
342 | input[type=range]:focus::-ms-fill-lower {
343 | background: #e9e9f1;
344 | }
345 |
346 | input[type=range]::-ms-fill-upper {
347 | width: 100%;
348 | height: 8px;
349 | cursor: pointer;
350 | border: 1px solid #ccd;
351 | background-color: #e9e9f1;
352 | border-radius: 4px;
353 | box-sizing: border-box;
354 | }
355 |
356 | input[type=range]:focus::-ms-fill-upper {
357 | background: #e9e9f1;
358 | }
359 |
360 | .input_file {
361 | display: inline-block;
362 | width: 100%;
363 | position: relative;
364 | white-space: nowrap;
365 | }
366 |
367 | .input_file input[type=file] {
368 | opacity: 0;
369 | display: block;
370 | position: absolute;
371 | top: 0;
372 | left: 0;
373 | width: 100%;
374 | height: 100%;
375 | cursor: pointer;
376 | }
377 |
378 | .input_file label {
379 | background-color: rgba(0, 0, 0, 0);
380 | color: #06b;
381 | border: 1px solid #06b;
382 | border-radius: 3px;
383 | padding: 2px 4px 2px 4px;
384 | margin: 2px 0 2px 0;
385 | width: 100%;
386 | max-width: min(35vw, 35vh);
387 | text-align: center;
388 | display: inline-block;
389 | cursor: pointer;
390 | overflow-x: hidden;
391 | text-overflow: ellipsis;
392 | box-sizing: border-box;
393 | vertical-align: middle;
394 | }
395 |
396 | .input_file input[type=file]:hover+label {
397 | background-color: rgba(0, 0, 255, 0.1);
398 | }
399 |
400 | .input_file input[type=file]:active+label {
401 | background-color: rgba(0, 0, 255, 0.2);
402 | }
403 |
404 | .input_file input[type=file]:focus+label {
405 | box-shadow: 0 0 0 2px rgba(180, 200, 220, 0.3);
406 | }
407 |
408 | .input_file label:empty::before {
409 | content: 'Browse...';
410 | display: inline-block;
411 | text-align: center;
412 | width: 100%;
413 | }
414 |
415 | textarea {
416 | border: 1px solid #ccd;
417 | background-color: #f7f7fa;
418 | color: #777;
419 | box-shadow: none;
420 | padding: 4px;
421 | border-radius: 3px;
422 | }
423 |
424 | textarea:hover {
425 | border-color: #aab;
426 | }
427 |
428 | textarea:invalid {
429 | background-color: #f7d7da;
430 | box-shadow: none;
431 | }
432 |
433 | textarea:focus {
434 | box-shadow: 0 0 0 2px rgba(180, 200, 220, 0.3);
435 | outline: none;
436 | }
437 |
438 | select:disabled, input:disabled, button:disabled, textarea:disabled, .input_file input[type=file]:disabled+label {
439 | opacity: 0.4;
440 | }
441 |
442 | input:disabled+label {
443 | opacity: 0.4;
444 | }
--------------------------------------------------------------------------------
/css/reset.css:
--------------------------------------------------------------------------------
1 | * {
2 | padding: 0;
3 | margin: 0;
4 | font-family: sans-serif;
5 | border: none;
6 | outline: none;
7 | }
8 |
9 | body, button, label, select, input {
10 | font-size: 20px;
11 | }
12 |
13 | @media (max-width: 600px), (max-height: 600px) {
14 | body, button, label, select, input {
15 | font-size: 14px;
16 | }
17 | }
--------------------------------------------------------------------------------
/css/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | background-color: #d5d5d5;
3 | }
4 |
5 | table {
6 | border-spacing: 1vw;
7 | }
8 |
9 | button {
10 | min-width: 13vw;
11 | }
12 |
13 | #header button, #toolbar button {
14 | margin: 1px;
15 | }
16 |
17 | button.button--normal-size {
18 | min-width: initial;
19 | }
20 |
21 | button.close-button {
22 | min-width: initial;
23 | position: absolute;
24 | top: 4px;
25 | right: 4px;
26 | }
27 |
28 | button a {
29 | display: inline-block;
30 | width: 100%;
31 | }
32 |
33 | br.wide+* {
34 | margin-top: 1vh;
35 | }
36 |
37 | br.narrow+* {
38 | margin-top: 0.5vh;
39 | }
40 |
41 | input[type=number] {
42 | width: 15vw;
43 | }
44 |
45 | #main-container {
46 | max-width: 100%;
47 | }
48 |
49 | #header {
50 | background-color: white;
51 | border-radius: 4px;
52 | margin: 0.5vw;
53 | padding: 1vw 1.5vw 1vw 1.5vw;
54 | overflow: hidden;
55 | box-shadow: 0.3vw 0.3vw 0.3vw #bbb;
56 | }
57 |
58 | #header__caption {
59 | position: relative;
60 | float: right;
61 | padding: 4px;
62 | color: #06b;
63 | font-weight: bold;
64 | padding-left: 2em;
65 | background-size: auto 100%;
66 | background-repeat: no-repeat;
67 | }
68 |
69 | #header__caption img {
70 | position: absolute;
71 | top: 0;
72 | left: 0;
73 | height: 100%;
74 | }
75 |
76 | #workarea-container {
77 | display: flex;
78 | flex-wrap: wrap;
79 | }
80 |
81 | #toolbar {
82 | flex-grow: 1;
83 | flex-basis: 9%;
84 | background-color: white;
85 | border-radius: 4px;
86 | margin: 0.5vw;
87 | padding-top: 0.5vw;
88 | padding-bottom: 1px;
89 | box-shadow: 0.3vw 0.3vw 0.3vw #bbb;
90 | }
91 |
92 | .toolbar-group {
93 | display: inline-block;
94 | margin: 0.5vw;
95 | padding: 0 1vw 0.5vw 1vw;
96 | border-bottom: 1px solid #ccd;
97 | }
98 |
99 | .toolbar-group__checkboxes {
100 | display: inline-block;
101 | letter-spacing: 1vw;
102 | }
103 |
104 | .checkbox-wrapper {
105 | display: inline-block;
106 | min-width: 10vw;
107 | margin: 4px 1px 4px 1px;
108 | letter-spacing: initial;
109 | }
110 |
111 | .toolbar-group__buttons {
112 | display: flex;
113 | flex-wrap: wrap;
114 | gap: 0 4px;
115 | }
116 |
117 | .toolbar-group__buttons button {
118 | flex-grow: 1;
119 | }
120 |
121 | #show-colors-button {
122 | min-width: 0;
123 | color: transparent;
124 | background-image: linear-gradient(#d03 0%, #d03 15%, #0a1 40%, #0a1 60%, #08d 80%, #08d 100%);
125 | }
126 |
127 | #show-colors-button:active, #show-colors-button:hover {
128 | background-image: none;
129 | }
130 |
131 | .join-buttons-div {
132 | display: inline-block;
133 | white-space: nowrap;
134 | }
135 |
136 | .toolbar-group select {
137 | min-width: 13vw;
138 | max-width: 10em;
139 | margin: 1px;
140 | text-overflow: ellipsis;
141 | }
142 |
143 | .group-header {
144 | display: inline-block;
145 | min-width: 9vw;
146 | color: #06b;
147 | }
148 |
149 | .toolbar-group .group-header {
150 | padding: 8px 0 8px 0;
151 | }
152 |
153 | #editor-container {
154 | flex-grow: 7;
155 | overflow-x: hidden;
156 | background-color: white;
157 | border-radius: 4px;
158 | box-shadow: 0.3vw 0.3vw 0.3vw #bbb;
159 | margin: 0.5vw;
160 | }
161 |
162 | #gamepad-container {
163 | text-align: center;
164 | overflow-x: auto;
165 | border-bottom: 1px solid #ccd;
166 | margin-left: 0.5vw;
167 | margin-right: 0.5vw;
168 | }
169 |
170 | #gamepad-container__scroll-frame {
171 | display: inline-block;
172 | padding: 1vw 0.5vw 1vw 0.5vw;
173 | }
174 |
175 | #screenpad {
176 | background-color: #999;
177 | position: relative;
178 | width: 800px;
179 | height: 450px;
180 | padding: 0;
181 | }
182 |
183 | #screenpad.scheme-1, #screenpad.scheme-2 {
184 | background-color: #000;
185 | }
186 |
187 | .screenpad-background {
188 | position: absolute;
189 | top: 0;
190 | left: 0;
191 | width: 100%;
192 | height: 100%;
193 | background-size: 100% 100%;
194 | }
195 |
196 | .hide-offscreen {
197 | overflow: hidden;
198 | }
199 |
200 | .show-offscreen {
201 | overflow: visible;
202 | margin: 100px;
203 | }
204 |
205 | .selection-box {
206 | position: absolute;
207 | background-color: rgba(150,100,200,0.4);
208 | z-index: 1;
209 | box-sizing: border-box;
210 | border: 1px solid #96c;
211 | display: none;
212 | }
213 |
214 | .rect {
215 | position: absolute;
216 | background-size: 100% 100%;
217 | text-align: center;
218 | cursor: pointer;
219 | text-shadow: 1px 1px #aac;
220 | -webkit-user-select: none;
221 | user-select: none;
222 | }
223 |
224 | #screenpad.scheme-1 .rect {
225 | color: #69e;
226 | text-shadow: 1px 1px #034;
227 | }
228 |
229 | #screenpad.scheme-2 .rect {
230 | color: #6e9;
231 | text-shadow: 1px 1px #043;
232 | }
233 |
234 | .rect.selected {
235 | background-color: rgba(200, 0, 0, 0.3);
236 | box-shadow: 0 0 0 2px rgba(200, 0, 0, 0.15);
237 | }
238 |
239 | #screenpad.scheme-1 .rect.selected, #screenpad.scheme-2 .rect.selected {
240 | background-color: rgba(200, 100, 50, 0.4);
241 | box-shadow: 0 0 0 2px rgba(200, 100, 50, 0.25);
242 | }
243 |
244 | .show-borders .rect:after {
245 | content: '';
246 | border: 1px solid black;
247 | width: 100%;
248 | height: 100%;
249 | position: absolute;
250 | top: 0;
251 | left: 0;
252 | box-sizing: border-box;
253 | background-color: rgba(1, 0, 0, 0.1);
254 | }
255 |
256 | #screenpad.scheme-1.show-borders .rect:after {
257 | border-color: #89b;
258 | }
259 |
260 | #screenpad.scheme-2.show-borders .rect:after {
261 | border-color: #8b9;
262 | }
263 |
264 | .show-borders .rect.radial:after {
265 | border-radius: 50%;
266 | }
267 |
268 | .show-borders .rect div {
269 | position: absolute;
270 | top: 0;
271 | left: 0;
272 | right: 0;
273 | bottom: 0;
274 | border-radius: 50%;
275 | pointer-events: none;
276 | background-color: none;
277 | }
278 |
279 | .hide-names .rect, #screenpad.scheme-1.hide-names .rect, #screenpad.scheme-2.hide-names .rect {
280 | color: transparent;
281 | font-size: 1px;
282 | text-shadow: none;
283 | }
284 |
285 | #game-screenshot {
286 | position: absolute;
287 | background-color: #aac;
288 | width: 640px;
289 | height: 480px;
290 | top: 0;
291 | left: 107px;
292 | background-size: 100% 100%;
293 | }
294 |
295 | #screenpad.scheme-1 #game-screenshot, #screenpad.scheme-2 #game-screenshot {
296 | background-color: #667;
297 | }
298 |
299 | .hide-offscreen #game-screenshot {
300 | overflow: hidden;
301 | }
302 |
303 | .show-offscreen #game-screenshot {
304 | overflow: visible;
305 | }
306 |
307 | #editor {
308 | background-color: white;
309 | border-radius: 4px;
310 | padding: 1vw;
311 | white-space: nowrap;
312 | overflow: hidden;
313 | }
314 |
315 | #editor * {
316 | box-sizing: border-box;
317 | }
318 |
319 | #editor>div {
320 | padding-top: 2px;
321 | padding-bottom: 2px;
322 | overflow: hidden;
323 | display: flex;
324 | align-items: center;
325 | }
326 |
327 | #editor span {
328 | width: 4%;
329 | text-align: center;
330 | overflow-x: hidden;
331 | padding-top: 0.6vw;
332 | padding-bottom: 0.6vw;
333 | margin: 0;
334 | }
335 |
336 | #editor input[type=range] {
337 | width: 70%;
338 | }
339 |
340 | #editor input[type=number] {
341 | width: 16%;
342 | margin-left: 1%;
343 | margin-right: 1%;
344 | }
345 |
346 | #editor button {
347 | min-width: 0;
348 | width: 7%;
349 | text-overflow: ellipsis;
350 | overflow: hidden;
351 | }
352 |
353 | .dialog-container {
354 | position: fixed;
355 | top: 0;
356 | left: 0;
357 | width: 100vw;
358 | height: 100%;
359 | background-color: rgba(0, 0, 0, 0.5);
360 | display: flex;
361 | justify-content: center;
362 | align-items: center;
363 | }
364 |
365 | .modal-dialog {
366 | background-color: white;
367 | border-radius: 4px;
368 | max-height: 100%;
369 | min-width: 250px;
370 | overflow-x: hidden;
371 | overflow-y: auto;
372 | box-sizing: border-box;
373 | box-shadow: 0.5vw 0.5vw 0.5vw #444;
374 | }
375 |
376 | .modal-dialog__header-box {
377 | position: relative;
378 | padding: 2vh 3vw 2vh 3vw;
379 | background-color: #f7f7fa;
380 | color: #06b;
381 | font-weight: bold;
382 | border-bottom: 2px solid #06b;
383 | }
384 |
385 | .modal-dialog__content-box {
386 | padding: 2vh 3vw 2vh 3vw;
387 | position: relative;
388 | }
389 |
390 | .dialog-content-box__section:not(:last-child) {
391 | padding-bottom: 3vh;
392 | }
393 |
394 | .dialog-content-box__expandable {
395 | padding-top: 1vh;
396 | }
397 |
398 | .modal-dialog__content-box input[type=number] {
399 | min-width: 90px;
400 | }
401 |
402 | .modal-dialog__footer-box {
403 | padding: 1vh 3vw 1vh 3vw;
404 | overflow: hidden;
405 | border-top: 1px solid #ccd;
406 | }
407 |
408 | .modal-dialog__footer-box button {
409 | float: right;
410 | min-width: 30%;
411 | padding-left: 6px;
412 | padding-right: 6px;
413 | margin: 2px 2px 2px 6px;
414 | }
415 |
416 | .modal-dialog table {
417 | margin: -1vw;
418 | }
419 |
420 | #button-properties-table td {
421 | white-space: nowrap;
422 | }
423 |
424 | #button-properties-table input, #button-properties-table select {
425 | width: 85%;
426 | box-sizing: border-box;
427 | }
428 |
429 | #button-properties-table select.miniselect {
430 | width: 2em;
431 | overflow: hidden;
432 | min-width: 2em;
433 | color: transparent;
434 | background-position: center center;
435 | padding-right: 2em;
436 | }
437 |
438 | #button-properties-table textarea {
439 | width: 85%;
440 | box-sizing: border-box;
441 | margin-top: 6px;
442 | }
443 |
444 | #button-properties-table .vertical-align-top {
445 | vertical-align: top;
446 | padding-top: 6px;
447 | }
448 |
449 | #overlay-create-dialog textarea {
450 | box-sizing: border-box;
451 | width: 100%;
452 | height: 9em;
453 | }
454 |
455 | #download-help {
456 | display: inline-block;
457 | color: #777;
458 | font-size: 0.7em;
459 | padding-top: 3vh;
460 | }
461 |
462 | #image-name {
463 | padding-right: 1em;
464 | background-size: auto 150%;
465 | background-repeat: no-repeat;
466 | background-position: right center;
467 | box-shadow: inset 0 0 0 1px #f7f7fa;
468 | }
469 |
470 | #image-name:focus {
471 | box-shadow: inset 0 0 0 1px #f7f7fa, 0 0 0 2px rgba(180, 200, 220, 0.3);
472 | }
473 |
474 | #footer {
475 | position: relative;
476 | display: flex;
477 | justify-content: center;
478 | border-radius: 4px;
479 | background-color: white;
480 | margin: 0.5vw;
481 | box-shadow: 0.3vw 0.3vw 0.3vw #bbb;
482 | }
483 |
484 | #footer a {
485 | font-size: 0.9em;
486 | padding: 1vw;
487 | }
488 |
489 | #version {
490 | position: absolute;
491 | top: 1vw;
492 | right: 1vw;
493 | font-size: 0.9em;
494 | color: #bbb;
495 | }
496 |
497 | #aspect-hint {
498 | color: #bbb;
499 | }
500 |
501 | #aspect-hint-image {
502 | max-width: 100%;
503 | max-height: 50vh;
504 | }
505 |
506 | .aspect-hint-text-container {
507 | overflow: hidden;
508 | }
509 |
510 | .aspect-hint-text-container span {
511 | min-width: 36%;
512 | text-align: center;
513 | float: right;
514 | }
515 |
516 | #color-scheme-0-label {
517 | color: #000;
518 | text-shadow: 1px 1px #aac;
519 | background-color: #999;
520 | padding: 1px 6px;
521 | border-radius: 3px;
522 | }
523 |
524 | #color-scheme-1-label {
525 | color: #69e;
526 | text-shadow: 1px 1px #034;
527 | background-color: black;
528 | padding: 1px 6px;
529 | border-radius: 3px;
530 | }
531 |
532 | #color-scheme-2-label {
533 | color: #6e9;
534 | text-shadow: 1px 1px #043;
535 | background-color: black;
536 | padding: 1px 6px;
537 | border-radius: 3px;
538 | }
539 |
540 | .small-warning {
541 | font-size: 0.7em;
542 | color: #f75;
543 | }
544 |
545 | .hidden {
546 | display: none;
547 | }
--------------------------------------------------------------------------------
/img/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/1.png
--------------------------------------------------------------------------------
/img/2-button.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/2-button.png
--------------------------------------------------------------------------------
/img/2-button_gba.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/2-button_gba.png
--------------------------------------------------------------------------------
/img/2-button_gba_modified.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/2-button_gba_modified.png
--------------------------------------------------------------------------------
/img/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/2.png
--------------------------------------------------------------------------------
/img/3-button.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/3-button.png
--------------------------------------------------------------------------------
/img/4-button.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/4-button.png
--------------------------------------------------------------------------------
/img/6-button.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/6-button.png
--------------------------------------------------------------------------------
/img/6-button_genesis.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/6-button_genesis.png
--------------------------------------------------------------------------------
/img/A.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/A.png
--------------------------------------------------------------------------------
/img/A_nxengine.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/A_nxengine.png
--------------------------------------------------------------------------------
/img/A_right_nxengine.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/A_right_nxengine.png
--------------------------------------------------------------------------------
/img/A_wswan.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/A_wswan.png
--------------------------------------------------------------------------------
/img/B.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/B.png
--------------------------------------------------------------------------------
/img/B_wswan.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/B_wswan.png
--------------------------------------------------------------------------------
/img/C.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/C.png
--------------------------------------------------------------------------------
/img/C_pokemini.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/C_pokemini.png
--------------------------------------------------------------------------------
/img/C_pokemini_smaller.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/C_pokemini_smaller.png
--------------------------------------------------------------------------------
/img/D.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/D.png
--------------------------------------------------------------------------------
/img/E.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/E.png
--------------------------------------------------------------------------------
/img/F.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/F.png
--------------------------------------------------------------------------------
/img/I.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/I.png
--------------------------------------------------------------------------------
/img/II.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/II.png
--------------------------------------------------------------------------------
/img/III.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/III.png
--------------------------------------------------------------------------------
/img/IV.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/IV.png
--------------------------------------------------------------------------------
/img/L.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/L.png
--------------------------------------------------------------------------------
/img/L1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/L1.png
--------------------------------------------------------------------------------
/img/L1_down.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/L1_down.png
--------------------------------------------------------------------------------
/img/L1_down_smaller.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/L1_down_smaller.png
--------------------------------------------------------------------------------
/img/L1_smaller.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/L1_smaller.png
--------------------------------------------------------------------------------
/img/L2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/L2.png
--------------------------------------------------------------------------------
/img/L2_up.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/L2_up.png
--------------------------------------------------------------------------------
/img/L3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/L3.png
--------------------------------------------------------------------------------
/img/L3_smaller.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/L3_smaller.png
--------------------------------------------------------------------------------
/img/L_and_R.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/L_and_R.png
--------------------------------------------------------------------------------
/img/L_and_R_down.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/L_and_R_down.png
--------------------------------------------------------------------------------
/img/L_and_R_left.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/L_and_R_left.png
--------------------------------------------------------------------------------
/img/L_down.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/L_down.png
--------------------------------------------------------------------------------
/img/L_down_smaller_gba.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/L_down_smaller_gba.png
--------------------------------------------------------------------------------
/img/L_face.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/L_face.png
--------------------------------------------------------------------------------
/img/L_right.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/L_right.png
--------------------------------------------------------------------------------
/img/L_right_smaller.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/L_right_smaller.png
--------------------------------------------------------------------------------
/img/L_smaller.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/L_smaller.png
--------------------------------------------------------------------------------
/img/R.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/R.png
--------------------------------------------------------------------------------
/img/R1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/R1.png
--------------------------------------------------------------------------------
/img/R1_smaller.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/R1_smaller.png
--------------------------------------------------------------------------------
/img/R2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/R2.png
--------------------------------------------------------------------------------
/img/R2_up.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/R2_up.png
--------------------------------------------------------------------------------
/img/R3_smaller.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/R3_smaller.png
--------------------------------------------------------------------------------
/img/R_down.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/R_down.png
--------------------------------------------------------------------------------
/img/R_down_smaller.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/R_down_smaller.png
--------------------------------------------------------------------------------
/img/R_face.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/R_face.png
--------------------------------------------------------------------------------
/img/R_smaller.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/R_smaller.png
--------------------------------------------------------------------------------
/img/S_nxengine.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/S_nxengine.png
--------------------------------------------------------------------------------
/img/V.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/V.png
--------------------------------------------------------------------------------
/img/VI.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/VI.png
--------------------------------------------------------------------------------
/img/W_nxengine.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/W_nxengine.png
--------------------------------------------------------------------------------
/img/X.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/X.png
--------------------------------------------------------------------------------
/img/X1_wswan.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/X1_wswan.png
--------------------------------------------------------------------------------
/img/X2_wswan.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/X2_wswan.png
--------------------------------------------------------------------------------
/img/X3_wswan.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/X3_wswan.png
--------------------------------------------------------------------------------
/img/X4_wswan.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/X4_wswan.png
--------------------------------------------------------------------------------
/img/Y.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/Y.png
--------------------------------------------------------------------------------
/img/Y1_wswan.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/Y1_wswan.png
--------------------------------------------------------------------------------
/img/Y2_wswan.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/Y2_wswan.png
--------------------------------------------------------------------------------
/img/Y3_wswan.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/Y3_wswan.png
--------------------------------------------------------------------------------
/img/Y4_wswan.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/Y4_wswan.png
--------------------------------------------------------------------------------
/img/Z.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/Z.png
--------------------------------------------------------------------------------
/img/Z_down.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/Z_down.png
--------------------------------------------------------------------------------
/img/Z_down_smaller.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/Z_down_smaller.png
--------------------------------------------------------------------------------
/img/analog.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/analog.png
--------------------------------------------------------------------------------
/img/c_down.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/c_down.png
--------------------------------------------------------------------------------
/img/c_left.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/c_left.png
--------------------------------------------------------------------------------
/img/c_right.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/c_right.png
--------------------------------------------------------------------------------
/img/c_up.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/c_up.png
--------------------------------------------------------------------------------
/img/circle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/circle.png
--------------------------------------------------------------------------------
/img/coin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/coin.png
--------------------------------------------------------------------------------
/img/cross.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/cross.png
--------------------------------------------------------------------------------
/img/digital.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/digital.png
--------------------------------------------------------------------------------
/img/dpad-down.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/dpad-down.png
--------------------------------------------------------------------------------
/img/dpad-down_no-mark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/dpad-down_no-mark.png
--------------------------------------------------------------------------------
/img/dpad-down_psx.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/dpad-down_psx.png
--------------------------------------------------------------------------------
/img/dpad-down_psx_no-mark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/dpad-down_psx_no-mark.png
--------------------------------------------------------------------------------
/img/dpad-left.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/dpad-left.png
--------------------------------------------------------------------------------
/img/dpad-left_no-mark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/dpad-left_no-mark.png
--------------------------------------------------------------------------------
/img/dpad-left_psx.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/dpad-left_psx.png
--------------------------------------------------------------------------------
/img/dpad-left_psx_no-mark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/dpad-left_psx_no-mark.png
--------------------------------------------------------------------------------
/img/dpad-right.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/dpad-right.png
--------------------------------------------------------------------------------
/img/dpad-right_no-mark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/dpad-right_no-mark.png
--------------------------------------------------------------------------------
/img/dpad-right_psx.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/dpad-right_psx.png
--------------------------------------------------------------------------------
/img/dpad-right_psx_no-mark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/dpad-right_psx_no-mark.png
--------------------------------------------------------------------------------
/img/dpad-up.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/dpad-up.png
--------------------------------------------------------------------------------
/img/dpad-up_no-mark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/dpad-up_no-mark.png
--------------------------------------------------------------------------------
/img/dpad-up_psx.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/dpad-up_psx.png
--------------------------------------------------------------------------------
/img/dpad-up_psx_no-mark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/dpad-up_psx_no-mark.png
--------------------------------------------------------------------------------
/img/fast_forward.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/fast_forward.png
--------------------------------------------------------------------------------
/img/genesis_mode.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/genesis_mode.png
--------------------------------------------------------------------------------
/img/hide.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/hide.png
--------------------------------------------------------------------------------
/img/keyboard.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/keyboard.png
--------------------------------------------------------------------------------
/img/no-analog.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/no-analog.png
--------------------------------------------------------------------------------
/img/overlay-A.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/overlay-A.png
--------------------------------------------------------------------------------
/img/overlay-B.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/overlay-B.png
--------------------------------------------------------------------------------
/img/overlay-C.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/overlay-C.png
--------------------------------------------------------------------------------
/img/overlay-D.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/overlay-D.png
--------------------------------------------------------------------------------
/img/pause_square_text.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/pause_square_text.png
--------------------------------------------------------------------------------
/img/pokemini_power.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/pokemini_power.png
--------------------------------------------------------------------------------
/img/pokemini_shake.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/pokemini_shake.png
--------------------------------------------------------------------------------
/img/reset_square_text.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/reset_square_text.png
--------------------------------------------------------------------------------
/img/retroarch-icon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/img/rgui.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/rgui.png
--------------------------------------------------------------------------------
/img/rotate.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/rotate.png
--------------------------------------------------------------------------------
/img/select_psx.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/select_psx.png
--------------------------------------------------------------------------------
/img/select_rounded_big.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/select_rounded_big.png
--------------------------------------------------------------------------------
/img/select_square_text.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/select_square_text.png
--------------------------------------------------------------------------------
/img/show.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/show.png
--------------------------------------------------------------------------------
/img/square.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/square.png
--------------------------------------------------------------------------------
/img/start.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/start.png
--------------------------------------------------------------------------------
/img/start_dc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/start_dc.png
--------------------------------------------------------------------------------
/img/start_genesis.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/start_genesis.png
--------------------------------------------------------------------------------
/img/start_psx.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/start_psx.png
--------------------------------------------------------------------------------
/img/start_rounded.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/start_rounded.png
--------------------------------------------------------------------------------
/img/start_rounded_big.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/start_rounded_big.png
--------------------------------------------------------------------------------
/img/test.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/test.png
--------------------------------------------------------------------------------
/img/thumbstick-background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/thumbstick-background.png
--------------------------------------------------------------------------------
/img/thumbstick-pad-hollow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/thumbstick-pad-hollow.png
--------------------------------------------------------------------------------
/img/thumbstick-pad.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/thumbstick-pad.png
--------------------------------------------------------------------------------
/img/thumbstick-pad_arcade.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/thumbstick-pad_arcade.png
--------------------------------------------------------------------------------
/img/triangle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Valent-in/retropad-editor/9c25413af23018af787f410e6e5c83ad49092d20/img/triangle.png
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | RetroPad Editor
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
29 |
30 |
31 |
84 |
85 |
86 |
93 |
94 |
122 |
123 |
124 |
125 |
126 |
133 |
134 |
135 |
136 |
264 |
265 |
266 |
306 |
307 |
308 |
345 |
346 |
347 |
393 |
394 |
395 |
468 |
469 |
470 |
471 |
475 |
476 |
477 | RetroArch overlays are built from a collection of images and a text configuration file.
478 |
479 |
480 | The config file (.cfg) describes:
481 |
482 |
483 | - The hitbox for each input event, i.e. "size" of the button.
484 | - Which images (.png) should be shown over the input elements.
485 |
486 |
More information:
487 | Libretro docs.
489 |
490 |
To use default "flat" image set:
491 |
493 | download and unpack archive, place config in folder with images.
494 |
495 |
496 |
499 |
500 |
501 |
502 |
503 |
504 |
505 | Do you want to reset?
All unsaved editions will be lost!
506 |
507 |
511 |
512 |
513 |
514 |
515 |
516 |
517 | Delete current overlay?
518 |
519 |
523 |
524 |
525 |
526 |
537 |
538 |
539 |
540 |
541 | Overlay with this name already exist.
542 |
543 |
546 |
547 |
548 |
549 |
550 |
551 |
552 | Enter overlay name.
553 |
554 |
557 |
558 |
559 |
560 |
561 |
562 |
566 |
567 |
568 | Default aspect ratio fix
569 |
570 |

571 |
572 | Keep relative positions
573 |
574 |
575 |
578 |
579 |
580 |
581 |
608 |
609 |
610 |
611 |
612 |
--------------------------------------------------------------------------------
/js/config-handler.js:
--------------------------------------------------------------------------------
1 | function ConfigHandler() {
2 | let _strings;
3 |
4 | // Array index of line overlayXX_descYY = "..."
5 | let _currentLine = -1;
6 | let _currentOverlay = 0;
7 |
8 | let selectedButtonLineIndexes = [];
9 |
10 |
11 | this.convertCfgToArray = function (str, onFinshCallback, imagesObj) {
12 | _strings = str.split('\n');
13 | selectedButtonLineIndexes.length = 0;
14 |
15 | _cleanUp();
16 | _normalizeOverlays(onFinshCallback, imagesObj);
17 |
18 | _currentLine = -1;
19 | _currentOverlay = 0;
20 | }
21 |
22 |
23 | this.getConfigString = function () {
24 | _cleanUp();
25 | return _strings.join('\n');
26 | }
27 |
28 |
29 | this.setCurrentLine = function (index) {
30 | if (index == -1 || _isOverlayXX_descYY(_strings[index]))
31 | _currentLine = index;
32 | else
33 | throw new Error('Wrong config line number');
34 | }
35 |
36 |
37 | // sections are 'command', 'shape', 'x', 'y', 'w', 'h'
38 | this.getCurrentLineSectionValue = function (section) {
39 | if (_currentLine == -1)
40 | return null;
41 |
42 | return _getParamSectionValue(_strings[_currentLine], section)
43 | }
44 |
45 |
46 | this.setCurrentLineSectionValue = function (section, value) {
47 | if (_currentLine == -1) {
48 | throw new Error('no selection!');
49 | }
50 |
51 | _strings[_currentLine] = _editParamSection(_strings[_currentLine], section, value);
52 | }
53 |
54 |
55 | this.setSelectionSectionValue = function (section, sValue) {
56 | let value = Number(sValue);
57 |
58 | if (selectedButtonLineIndexes.length == 0)
59 | return;
60 |
61 | let center = this.getSelectionDimensions();
62 | if (!center)
63 | return;
64 |
65 | for (let i = 0; i < selectedButtonLineIndexes.length; i++) {
66 | let confValue;
67 | let index = selectedButtonLineIndexes[i];
68 |
69 | let x = Number(_getParamSectionValue(_strings[index], 'x'));
70 | let y = Number(_getParamSectionValue(_strings[index], 'y'));
71 | let w = Number(_getParamSectionValue(_strings[index], 'w'));
72 | let h = Number(_getParamSectionValue(_strings[index], 'h'));
73 |
74 | switch (section) {
75 | case 'x':
76 | confValue = x + value - center.x;
77 | _strings[index] = _editParamSection(_strings[index], section, confValue.toFixed(10));
78 | break;
79 | case 'y':
80 | confValue = y + value - center.y;
81 | _strings[index] = _editParamSection(_strings[index], section, confValue.toFixed(10));
82 | break;
83 | case 'w':
84 | value = Math.max(value, 0.00001);
85 | confValue = w * value / center.w;
86 | _strings[index] = _editParamSection(_strings[index], section, confValue.toFixed(10));
87 | confValue = center.x + (x - center.x) * (value / center.w);
88 | _strings[index] = _editParamSection(_strings[index], 'x', confValue.toFixed(10));
89 | break;
90 | case 'h':
91 | value = Math.max(value, 0.00001);
92 | confValue = h * value / center.h;
93 | _strings[index] = _editParamSection(_strings[index], section, confValue.toFixed(10));
94 | confValue = center.y + (y - center.y) * (value / center.h);
95 | _strings[index] = _editParamSection(_strings[index], 'y', confValue.toFixed(10));
96 | break;
97 | }
98 | }
99 | }
100 |
101 |
102 | // Extracts config data for gamepad visualisation (shape, position, size, image, for each button)
103 | this.buildPadFromConfig = function () {
104 | if (!_strings)
105 | return;
106 |
107 | let buttons = []
108 |
109 | for (let i = 0; i < _strings.length; i++) {
110 | let parameter = _isOverlayXX_descYY(_strings[i]);
111 | if (parameter) {
112 | let o = {
113 | command: _getParamSectionValue(_strings[i], 'command'),
114 | x: _getParamSectionValue(_strings[i], 'x'),
115 | y: _getParamSectionValue(_strings[i], 'y'),
116 | w: _getParamSectionValue(_strings[i], 'w'),
117 | h: _getParamSectionValue(_strings[i], 'h'),
118 | s: _getParamSectionValue(_strings[i], 'shape'),
119 | img: _getParamValue(parameter + '_overlay'),
120 | pct: _getParamValue(parameter + '_saturate_pct'),
121 | i: i
122 | };
123 |
124 | // remove surrounding quotemarks
125 | if (o.img && o.img.search(/^".+"$/) == 0)
126 | o.img = o.img.substr(1, o.img.length - 2);
127 |
128 | buttons.push(o);
129 | }
130 | }
131 |
132 | return buttons;
133 | }
134 |
135 |
136 | this.getCurrentOverlayBackground = function () {
137 | let bg = {};
138 | let fullscreen = _getParamValue('overlay' + _currentOverlay + '_full_screen');
139 | bg.fullscreen = (fullscreen == 'true');
140 |
141 | bg.image = _getParamValue('overlay' + _currentOverlay + '_overlay');
142 | let rect = _getParamValue('overlay' + _currentOverlay + '_rect');
143 |
144 | if (rect) {
145 | let coords = rect.split(',');
146 | bg.position = {};
147 | bg.position.x = coords[0];
148 | bg.position.y = coords[1];
149 | bg.position.w = coords[2];
150 | bg.position.h = coords[3];
151 | }
152 |
153 | return bg;
154 | }
155 |
156 |
157 | this.createOverlay = function (name, params) {
158 | if (this.isOverlayNameExist(name))
159 | return;
160 |
161 | if (name.trim() == '')
162 | return;
163 |
164 | let count = Number(_getParamValue('overlays'));
165 |
166 | _strings.push('overlay' + count + '_name = "' + name + '"');
167 |
168 | if (Array.isArray(params))
169 | params.forEach(line => _strings.push('overlay' + count + '_' + line));
170 |
171 | _strings.push('overlay' + count + '_descs = 0');
172 | _setParamValue('overlays', count + 1);
173 | }
174 |
175 |
176 | this.deleteCurrentOverlay = function () {
177 | let count = Number(_getParamValue('overlays'));
178 |
179 | if (count <= 1) {
180 | alert('Can not delete last overlay');
181 | return;
182 | }
183 |
184 | let name = _getParamValue('overlay' + _currentOverlay + '_name');
185 | console.log("DELETE OVERLAY", name);
186 |
187 | _deleteParamStrings('overlay' + _currentOverlay);
188 |
189 | for (let i = _currentOverlay; i < count - 1; i++) {
190 | _replaceParamNumbers('overlay', i + 1, i);
191 | }
192 |
193 | _updateLinkedButtons(name, null);
194 |
195 | _setParamValue('overlays', count - 1);
196 | _currentOverlay = 0;
197 | _currentLine = -1;
198 | }
199 |
200 |
201 | // copy only overlayXX_desc*
202 | this.duplicateCurrentOverlay = function (name, params) {
203 | if (this.isOverlayNameExist(name))
204 | return;
205 |
206 | if (name.trim() == '')
207 | return;
208 |
209 | let overlayXX = 'overlay' + _currentOverlay;
210 | let current = _getParamStrings(overlayXX);
211 |
212 | // last overlay index is count-1
213 | let count = Number(_getParamValue('overlays'));
214 | _setParamValue('overlays', count + 1);
215 |
216 | let result = ['overlay' + count + '_name = "' + name + '"'];
217 |
218 | if (params)
219 | for (let i = 0; i < params.length; i++) {
220 | result.push('overlay' + count + '_' + params[i]);
221 | }
222 |
223 | for (let i = 0; i < current.length; i++) {
224 | if (current[i].search('^' + overlayXX + '_desc') == -1)
225 | continue;
226 |
227 | result.push(current[i].replace(overlayXX, 'overlay' + count));
228 | }
229 |
230 | _strings = _strings.concat(result);
231 | }
232 |
233 |
234 | this.editCurrentOverlay = function (name, params) {
235 | if (name.trim() == '')
236 | return;
237 |
238 | let overlayXX = 'overlay' + _currentOverlay;
239 | let current = this.getCurrentOverlayParams();
240 |
241 | let currentName = _getParamValue(overlayXX + '_name');
242 | console.log('OLD NAME: ' + currentName);
243 |
244 | _setParamValue(overlayXX + '_name', '"' + name + '"');
245 | let insertAfter = _getParamIndex(overlayXX + '_name', name);
246 |
247 | for (let i = 0; i < current.length; i++) {
248 | let currentLine = overlayXX + '_' + current[i];
249 | // get parameter name without '=' and value
250 | let currentLineParam = currentLine.split('=')[0].trim();
251 | let index = _getParamIndex(currentLineParam);
252 | if (index >= 0) {
253 | console.log('DELETE: ' + currentLine);
254 | _strings.splice(index, 1);
255 | } else {
256 | console.log('NOT FOUND: ' + currentLine);
257 | }
258 | }
259 |
260 | console.log('NEW NAME: ' + name);
261 | let newParams = []
262 | for (let i = 0; i < params.length; i++) {
263 | newParams.push(overlayXX + '_' + params[i]);
264 | console.log('ADD: ' + overlayXX + '_' + params[i]);
265 | }
266 |
267 | _strings.splice(insertAfter + 1, 0, ...newParams);
268 |
269 | _updateLinkedButtons(currentName, name);
270 | }
271 |
272 |
273 | // without overlayXX_desc* and overlayXX_name
274 | this.getCurrentOverlayParams = function () {
275 | let overlayXX = 'overlay' + _currentOverlay;
276 | let current = _getParamStrings(overlayXX);
277 | let params = [];
278 |
279 | for (let i = 0; i < current.length; i++) {
280 | if (current[i].search('^' + overlayXX + '_desc') == 0)
281 | continue;
282 |
283 | if (current[i].search('^' + overlayXX + '_name') == 0) {
284 | continue;
285 | }
286 |
287 | params.push(current[i].substr(overlayXX.length + 1));
288 | }
289 |
290 | console.log(params);
291 | return params;
292 | }
293 |
294 |
295 | this.isOverlayNameExist = function (name) {
296 | let list = this.getOverlayList();
297 | for (let i = 0; i < list.length; i++) {
298 | if (name == list[i] || '"' + name + '"' == list[i]) {
299 | return true;
300 | }
301 | }
302 |
303 | return false;
304 | }
305 |
306 |
307 | this.createButton = function (command, shape, image, addLines) {
308 | let overlayXX = 'overlay' + _currentOverlay;
309 | let buttCount = Number(_getParamValue(`${overlayXX}_descs`));
310 |
311 | let last;
312 |
313 | let reg = new RegExp('^' + overlayXX + '_desc' + (buttCount - 1));
314 | for (let i = _strings.length - 1; i >= 0; i--) {
315 | if (_strings[i].split('=')[0].trim().search(reg) != -1) {
316 | last = i;
317 | break
318 | }
319 | }
320 |
321 | if (!last)
322 | last = _getParamIndex(`${overlayXX}_descs`);
323 |
324 | if (last == -1)
325 | throw new Error('can not find position to insert new line');
326 |
327 | let overlayXX_descYY = `${overlayXX}_desc${buttCount}`;
328 | console.log("NEW BUTTON", overlayXX_descYY, command, shape, image);
329 | let arr = [`${overlayXX_descYY} = "${command},0.50000,0.50000,${shape},0.05000,0.05000"`];
330 | _strings.splice(last + 1, 0, ...arr);
331 |
332 | this.setCurrentLine(last + 1);
333 | this.updateCurrentButton(command, shape, image, addLines);
334 |
335 | _setParamValue(`${overlayXX}_descs`, buttCount + 1);
336 | }
337 |
338 |
339 | this.deleteCurrentButton = function () {
340 | if (_currentLine == -1) {
341 | return false;
342 | }
343 |
344 | let parameter = _isOverlayXX_descYY(_strings[_currentLine]);
345 | console.log("DELETE BUTTON", parameter);
346 | let buttCount = Number(_getParamValue('overlay' + _currentOverlay + '_descs'));
347 |
348 | if (buttCount <= 1) {
349 | alert('Can not delete last button!');
350 | return;
351 | }
352 |
353 | let delNumber = Number(parameter.substr(('overlay' + _currentOverlay + '_desc').length));
354 |
355 | _deleteParamStrings(parameter);
356 |
357 | for (let i = delNumber; i < buttCount - 1; i++) {
358 | _replaceParamNumbers('overlay' + _currentOverlay + '_desc', i + 1, i);
359 | }
360 |
361 | _setParamValue('overlay' + _currentOverlay + '_descs', buttCount - 1);
362 |
363 | _currentLine = -1;
364 | return true;
365 | }
366 |
367 |
368 | this.fixAspect = function (iw, ih, ow, oh, portrait, keepRelativePositions) {
369 | let initial = iw / ih;
370 | let target = ow / oh;
371 | let axis = portrait ? 'y' : 'x';
372 |
373 | let coef = initial / target;
374 | if (portrait)
375 | coef = 1 / coef;
376 |
377 | for (let i = 0; i < _strings.length; i++) {
378 | let parameter = _isOverlayXX_descYY(_strings[i]);
379 | if (parameter) {
380 | if (keepRelativePositions)
381 | __scalePositioned(i, coef, axis);
382 | else
383 | __scaleSnapped(i, coef, axis);
384 | }
385 | }
386 |
387 | this.setOverlayAspectRatio(target);
388 |
389 |
390 | function __scaleSnapped(index, coef, ax) {
391 | let c = _getParamSectionValue(_strings[index], ax);
392 | if (c <= 0.45)
393 | _strings[index] = _editParamSection(_strings[index], ax, (c * coef).toFixed(5));
394 | else if (c >= 0.55)
395 | _strings[index] = _editParamSection(_strings[index], ax, (c * coef + (1 - coef)).toFixed(5));
396 |
397 | let direction = ax == 'x' ? 'w' : 'h';
398 | let s = _getParamSectionValue(_strings[index], direction);
399 | _strings[index] = _editParamSection(_strings[index], direction, (s * coef).toFixed(5));
400 | }
401 |
402 | function __scalePositioned(index, coef, ax) {
403 | let c = _getParamSectionValue(_strings[index], ax);
404 | _strings[index] = _editParamSection(_strings[index], ax, (c * coef + (1 - coef) / 2).toFixed(5));
405 |
406 | let direction = ax == 'x' ? 'w' : 'h';
407 | let s = _getParamSectionValue(_strings[index], direction);
408 | _strings[index] = _editParamSection(_strings[index], direction, (s * coef).toFixed(5));
409 | }
410 | }
411 |
412 |
413 | this.setCurrentOverlay = function (num) {
414 | selectedButtonLineIndexes.length = 0;
415 |
416 | let count = Number(_getParamValue('overlays'));
417 | if (num && num < count)
418 | _currentOverlay = num;
419 | else
420 | _currentOverlay = 0;
421 | }
422 |
423 |
424 | this.getCurrentOverlay = function () {
425 | return _currentOverlay;
426 | }
427 |
428 |
429 | this.getCurrentOverlayName = function () {
430 | return _getParamValue('overlay' + _currentOverlay + '_name') || '';
431 | }
432 |
433 |
434 | this.getOverlayList = function () {
435 | let count = Number(_getParamValue('overlays'));
436 | console.log('overlays:', count);
437 |
438 | let list = [];
439 |
440 | for (let i = 0; i < count; i++) {
441 | let name = _getParamValue('overlay' + i + '_name');
442 |
443 | if (name)
444 | list.push('"' + name + '"');
445 | else
446 | list.push('');
447 | }
448 |
449 | return list;
450 | }
451 |
452 |
453 | this.flipXcoord = function () {
454 | let x;
455 | if (selectedButtonLineIndexes.length > 0) {
456 | let c = this.getSelectionDimensions();
457 | x = (1 - c.x).toFixed(5);
458 | this.setSelectionSectionValue('x', x);
459 | } else {
460 | x = (1 - _getParamSectionValue(_strings[_currentLine], 'x')).toFixed(5);
461 | this.setCurrentLineSectionValue('x', x);
462 | }
463 | return x;
464 | }
465 |
466 |
467 | this.normalizeWidth = function (width, height) {
468 | let w;
469 | if (selectedButtonLineIndexes.length > 0) {
470 | let c = this.getSelectionDimensions();
471 | w = (height / width * c.h).toFixed(5);
472 | this.setSelectionSectionValue('w', w);
473 | } else {
474 | w = (height / width * _getParamSectionValue(_strings[_currentLine], 'h')).toFixed(5);
475 | this.setCurrentLineSectionValue('w', w);
476 | }
477 | return w;
478 | }
479 |
480 |
481 | this.normalizeHeight = function (width, height) {
482 | let h;
483 | if (selectedButtonLineIndexes.length > 0) {
484 | let c = this.getSelectionDimensions();
485 | h = (width / height * c.w).toFixed(5);
486 | this.setSelectionSectionValue('h', h);
487 | } else {
488 | h = (width / height * _getParamSectionValue(_strings[_currentLine], 'w')).toFixed(5);
489 | this.setCurrentLineSectionValue('h', h);
490 | }
491 | return h;
492 | }
493 |
494 |
495 | this.getCurrentButtonParams = function () {
496 | let param = _isOverlayXX_descYY(_strings[_currentLine]);
497 | let lines = _getParamStrings(param);
498 | let ret = {};
499 |
500 | ret.command = _getParamSectionValue(_strings[_currentLine], 'command');
501 | ret.shape = _getParamSectionValue(_strings[_currentLine], 'shape');
502 | ret.image = _getParamValue(param + '_overlay');
503 |
504 | // remove quotemarks
505 | if (ret.image && ret.image.search(/^".+"$/) == 0)
506 | ret.image = ret.image.substr(1, ret.image.length - 2);
507 |
508 | ret.addLines = [];
509 |
510 | for (let i = 0; i < lines.length; i++) {
511 | if (lines[i].search(param + '_') == 0 && lines[i].search(param + '_overlay') == -1) {
512 | ret.addLines.push(lines[i].substr(param.length + 1));
513 | }
514 | }
515 |
516 | console.log(ret);
517 | return ret;
518 | }
519 |
520 |
521 | this.updateCurrentButton = function (command, shape, image, addLines) {
522 | // Analog sticks with 'rect' shape are not valid
523 | if (command.search('analog_') == 0)
524 | shape = 'radial';
525 |
526 | _strings[_currentLine] = _editParamSection(_strings[_currentLine], 'command', command);
527 | _strings[_currentLine] = _editParamSection(_strings[_currentLine], 'shape', shape);
528 |
529 | let arr = [_strings[_currentLine]];
530 |
531 | let overlayXX_descYY = _isOverlayXX_descYY(_strings[_currentLine]);
532 | _deleteParamStrings(overlayXX_descYY);
533 |
534 | if (image)
535 | if (image.search(/\s/) == -1)
536 | arr.push(`${overlayXX_descYY}_overlay = ${image}`);
537 | else
538 | arr.push(`${overlayXX_descYY}_overlay = "${image}"`);
539 |
540 | if (Array.isArray(addLines))
541 | addLines.forEach(line => { arr.push(`${overlayXX_descYY}_${line}`) });
542 |
543 | _strings.splice(_currentLine, 0, ...arr);
544 | }
545 |
546 |
547 | this.getOverlayAspectRatio = function () {
548 | // overlayXX_aspect_ratio =
549 | let ratio = _getParamValue('overlay' + _currentOverlay + '_aspect_ratio');
550 |
551 | if (ratio)
552 | return _calculateAspect(ratio);
553 | }
554 |
555 |
556 | this.setOverlayAspectRatio = function (coef) {
557 | if (coef && !isNaN(coef))
558 | _setParamValue('overlay' + _currentOverlay + '_aspect_ratio', +Number(coef).toFixed(7));
559 | }
560 |
561 |
562 | this.getSelectionDimensions = function () {
563 | let xMax = -Infinity;
564 | let yMax = -Infinity;
565 | let xMin = Infinity;
566 | let yMin = Infinity;
567 |
568 | let len = selectedButtonLineIndexes.length;
569 |
570 | if (len == 0)
571 | return null;
572 |
573 | for (let i = 0; i < len; i++) {
574 | let index = selectedButtonLineIndexes[i];
575 |
576 | let x = Number(_getParamSectionValue(_strings[index], 'x'));
577 | let y = Number(_getParamSectionValue(_strings[index], 'y'));
578 | let w = Number(_getParamSectionValue(_strings[index], 'w'));
579 | let h = Number(_getParamSectionValue(_strings[index], 'h'));
580 |
581 | xMax = Math.max(xMax, x + w);
582 | yMax = Math.max(yMax, y + h);
583 | xMin = Math.min(xMin, x - w);
584 | yMin = Math.min(yMin, y - h);
585 | }
586 |
587 | return {
588 | x: (xMax + xMin) / 2,
589 | y: (yMax + yMin) / 2,
590 | w: (xMax - xMin) / 2,
591 | h: (yMax - yMin) / 2,
592 | };
593 | }
594 |
595 |
596 | this.selectButtonsInBounds = function (left, top, right, bottom) {
597 | let indexes = [];
598 |
599 | for (let i = 0; i < _strings.length; i++) {
600 | let param = _isOverlayXX_descYY(_strings[i]);
601 |
602 | if (param) {
603 | let x = _getParamSectionValue(_strings[i], 'x');
604 | let y = _getParamSectionValue(_strings[i], 'y');
605 |
606 | if (x >= left && x <= right && y >= top && y <= bottom)
607 | indexes.push(i)
608 | }
609 | }
610 |
611 | selectedButtonLineIndexes = indexes;
612 | return indexes;
613 | }
614 |
615 |
616 | this.isGroupSelected = function () {
617 | return selectedButtonLineIndexes.length > 0;
618 | }
619 |
620 |
621 | this.resetGroupSelection = function () {
622 | selectedButtonLineIndexes.length = 0;
623 | }
624 |
625 |
626 | this.isLineInSelection = function (index) {
627 | return selectedButtonLineIndexes.indexOf(index) != -1;
628 | }
629 |
630 |
631 | this.getSelectedIndexes = function () {
632 | return selectedButtonLineIndexes;
633 | }
634 |
635 |
636 | //PRIVATE
637 |
638 | function _cleanUp() {
639 | for (let i = _strings.length - 1; i >= 0; i--) {
640 | let str = _strings[i].trim();
641 | _strings[i] = str;
642 |
643 | // remove comments
644 | if (str[0] == '/' || str[0] == '#' || str == '') {
645 | _strings.splice(i, 1);
646 | continue;
647 | }
648 |
649 | // remove inline comments
650 | {
651 | let uncommentReg = /(^[^#]*".*")|(^[^#]+)/;
652 | let result = uncommentReg.exec(_strings[i]);
653 | _strings[i] = result ? result[0].trim() : '';
654 | }
655 |
656 | // remove path and inline comment from image filenames
657 | // search 'x_overlay ='
658 | if (_strings[i].search(/.\d_overlay\s*=/) != -1) {
659 | let param = _strings[i].split('=')[0].trim();
660 | let value = _strings[i].split('=')[1].trim();
661 |
662 | // search value part that in quotes
663 | let inQuotes = new RegExp(/^"(.+?)"/);
664 | let quot = inQuotes.exec(value);
665 | if (quot && quot.length == 2)
666 | value = quot[1];
667 |
668 | // remove path
669 | let lastShalshIndex = Math.max(value.lastIndexOf('\\'), value.lastIndexOf('/'));
670 | if (lastShalshIndex != -1)
671 | value = value.substr(lastShalshIndex + 1);
672 |
673 | // add quotemarks if spaces present in filename
674 | if (value.search(/\s/) == -1)
675 | _strings[i] = param + ' = ' + value;
676 | else
677 | _strings[i] = param + ' = "' + value + '"';
678 | }
679 |
680 | // surround overlay name with quotemarks
681 | if (_strings[i].search(/^overlay\d+_name\s*=/) == 0 ||
682 | _strings[i].search(/^overlay\d+_desc\d+_next_target\s*=/) == 0) {
683 |
684 | let param = _strings[i].split('=')[0].trim();
685 | let value = _strings[i].split('=')[1].trim();
686 |
687 | let valueBlocks = value.split('"')
688 | if (valueBlocks.length == 1)
689 | _strings[i] = param + ' = "' + valueBlocks[0] + '"';
690 | else
691 | _strings[i] = param + ' = "' + valueBlocks[0] + valueBlocks[1] + '"';
692 | }
693 | }
694 | }
695 |
696 |
697 | function _getParamIndex(param) {
698 | for (let i = 0; i < _strings.length; i++)
699 | if (_strings[i].split('=')[0].trim() == param)
700 | return i;
701 |
702 | return -1;
703 | }
704 |
705 |
706 | function _setParamValue(param, value) {
707 | let index = _getParamIndex(param);
708 |
709 | if (index == -1)
710 | _insertParam(param, value)
711 | else
712 | _strings[index] = param + ' = ' + value;
713 | }
714 |
715 |
716 | function _insertParam(param, value) {
717 | // insert near similar parameters
718 | let prefix = param.substr(0, param.indexOf('_'));
719 |
720 | if (prefix) {
721 | for (let i = 0; i < _strings.length; i++) {
722 | if (_strings[i].indexOf(prefix) == 0) {
723 | _strings.splice(i + 1, 0, param + ' = ' + value);
724 | return;
725 | }
726 | }
727 | }
728 |
729 | // append if no similar params found
730 | _strings.push(param + ' = ' + value)
731 | }
732 |
733 |
734 | function _getParamValue(param) {
735 | for (let i = 0; i < _strings.length; i++)
736 | if (_strings[i].split('=')[0].trim() == param) {
737 | let value = _strings[i].split('=')[1].trim();
738 |
739 | if (value[0] == '"')
740 | return value.split('"')[1];
741 | else
742 | return value;
743 | }
744 | }
745 |
746 |
747 | function _deleteParamStrings(param) {
748 | let reg = new RegExp('^' + param + '([^0-9]|$)');
749 | for (let i = _strings.length - 1; i >= 0; i--)
750 | if (_strings[i].trim().search(reg) != -1)
751 | _strings.splice(i, 1);
752 | }
753 |
754 |
755 | function _getParamStrings(param) {
756 | let ret = [];
757 | let reg = new RegExp('^' + param + '([^0-9]|$)');
758 | for (let i = 0; i < _strings.length; i++)
759 | if (_strings[i].trim().search(reg) != -1)
760 | ret.push(_strings[i])
761 |
762 | return ret;
763 | }
764 |
765 |
766 | function _replaceParamNumbers(searchStr, oldNum, newNum) {
767 | let reg = new RegExp('^' + searchStr + oldNum + '(?!\\d)');
768 |
769 | for (let i = 0; i < _strings.length; i++)
770 | _strings[i] = _strings[i].trim().replace(reg, searchStr + newNum);
771 | }
772 |
773 |
774 | function _isOverlayXX_descYY(str) {
775 | let param = str.split('=')[0].trim();
776 | let reg = new RegExp('^overlay' + _currentOverlay + '_desc\\d+$');
777 |
778 | return -1 == param.search(reg) ? null : param;
779 | }
780 |
781 |
782 | function _editParamSection(str, section, value) {
783 | let position = _getParamSectionValuePos(section);
784 | let blocks = str.split('"');
785 | let data = blocks[1].split(',');
786 | data[position] = value;
787 | blocks[1] = data.join(',');
788 |
789 | return blocks.join('"');
790 | }
791 |
792 |
793 | function _getParamSectionValue(str, section) {
794 | let position = _getParamSectionValuePos(section);
795 | let blocks = str.split('"');
796 | let data = blocks[1].split(',');
797 |
798 | return data[position];
799 | }
800 |
801 |
802 | function _getParamSectionValuePos(section) {
803 | switch (section) {
804 | case 'c':
805 | case 'command':
806 | return 0;
807 |
808 | case 'x':
809 | return 1;
810 |
811 | case 'y':
812 | return 2;
813 |
814 | case 's':
815 | case 'shape':
816 | return 3;
817 |
818 | case 'w':
819 | return 4;
820 |
821 | case 'h':
822 | return 5;
823 | }
824 | }
825 |
826 |
827 | function _updateLinkedButtons(currentName, newName) {
828 | // update name in overlayX_descY_next_target = currentName
829 | let regParam = new RegExp('^overlay\\d+_desc\\d+_next_target');
830 | let regValue = new RegExp('=\\s*\\"' + currentName + '\\"$');
831 |
832 | for (let i = _strings.length - 1; i >= 0; i--) {
833 |
834 | if (_strings[i].search(regParam) == -1)
835 | continue;
836 |
837 | if (_strings[i].search(regValue) == -1)
838 | continue;
839 |
840 | if (newName) {
841 | console.log('REPLACE: ' + _strings[i]);
842 | _strings[i] = _strings[i].split('=')[0].trim() + ' = "' + newName + '"';
843 | } else {
844 | console.log('DELETE: ' + _strings[i]);
845 | _strings.splice(i, 1);
846 | }
847 | }
848 | }
849 |
850 |
851 | function _calculateAspect(coef) {
852 | coef = Number(coef)
853 | if (isNaN(coef)) {
854 | console.log('Wrong aspect ratio in config');
855 | return { w: 1, h: 1 }
856 | }
857 |
858 | for (let i = 1; i <= 24; i++)
859 | for (let j = 1; j <= 24; j++)
860 | if (coef.toFixed(5) == (i / j).toFixed(5))
861 | return { w: i, h: j }
862 |
863 | return { w: +coef.toFixed(5), h: 1 }
864 | }
865 |
866 |
867 | function _normalizeOverlays(onFinshCallback, imagesObj) {
868 | imagesObj = imagesObj || {};
869 |
870 | let count = Number(_getParamValue('overlays'));
871 | let imgSizes = {};
872 | let toLoad = 0;
873 | let missingImages = [];
874 |
875 | for (let i = 0; i < count; i++) {
876 | let name = _getParamValue('overlay' + i + '_overlay');
877 |
878 | if (!__isOverlayNormalized(i)) {
879 | if (imagesObj[name]) {
880 | if (!imgSizes[name]) {
881 | imgSizes[name] = {};
882 | imgSizes[name].image = imagesObj[name];
883 | toLoad++;
884 | }
885 | } else {
886 | if (!missingImages.includes(name))
887 | missingImages.push(name);
888 | }
889 | }
890 | }
891 |
892 | if (missingImages.length > 0) {
893 | alert('Images are requiered for loading gamepad config:\n\n' + missingImages.join("\n") + '\n\nImport these files and click "Reset".')
894 | }
895 |
896 | if (toLoad == 0) {
897 | __normalizeIfNeeded(imgSizes);
898 | if (onFinshCallback)
899 | onFinshCallback();
900 |
901 | return;
902 | }
903 |
904 | let loaded = 0;
905 | for (let key in imgSizes) {
906 | let img = new Image();
907 |
908 | img.onload = function () {
909 | loaded++;
910 | imgSizes[key].w = img.naturalWidth;
911 | imgSizes[key].h = img.naturalHeight;
912 |
913 | if (loaded == toLoad) {
914 | __normalizeIfNeeded(imgSizes);
915 | if (onFinshCallback)
916 | onFinshCallback();
917 | }
918 | }
919 | img.src = imgSizes[key].image;
920 | }
921 |
922 |
923 | function __isOverlayNormalized(index) {
924 | let normParam = _getParamValue('overlay' + index + '_normalized');
925 | return (normParam && normParam == 'true');
926 | }
927 |
928 |
929 | function __normalizeIfNeeded(imgSizes) {
930 | let count = Number(_getParamValue('overlays'));
931 | let width = 1280;
932 | let height = 720;
933 |
934 | for (let i = 0; i < count; i++) {
935 | if (!__isOverlayNormalized(i)) {
936 | _setParamValue('overlay' + i + '_normalized', 'true');
937 |
938 | let name = _getParamValue('overlay' + i + '_overlay');
939 | if (imgSizes[name]) {
940 | width = imgSizes[name].w;
941 | height = imgSizes[name].h;
942 | }
943 |
944 | descs = _getParamValue('overlay' + i + '_descs');
945 | for (let j = 0; j < descs; j++) {
946 | let pi = _getParamIndex('overlay' + i + '_desc' + j);
947 |
948 | let x = Number(_getParamSectionValue(_strings[pi], 'x'));
949 | let y = Number(_getParamSectionValue(_strings[pi], 'y'));
950 | let w = Number(_getParamSectionValue(_strings[pi], 'w'));
951 | let h = Number(_getParamSectionValue(_strings[pi], 'h'));
952 |
953 | _strings[pi] = _editParamSection(_strings[pi], 'x', (x / width).toFixed(5));
954 | _strings[pi] = _editParamSection(_strings[pi], 'y', (y / height).toFixed(5));
955 | _strings[pi] = _editParamSection(_strings[pi], 'w', (w / width).toFixed(5));
956 | _strings[pi] = _editParamSection(_strings[pi], 'h', (h / height).toFixed(5));
957 | }
958 | }
959 | }
960 |
961 | }
962 |
963 | }
964 | }
965 |
--------------------------------------------------------------------------------
/js/defaults.js:
--------------------------------------------------------------------------------
1 | {
2 | let indent = new RegExp(/\n\s+/g);
3 |
4 | let imageNames = `1.png
5 | 2-button_gba_modified.png
6 | 2-button_gba.png
7 | 2-button.png
8 | 2.png
9 | 3-button.png
10 | 4-button.png
11 | 6-button_genesis.png
12 | 6-button.png
13 | analog.png
14 | A_nxengine.png
15 | A.png
16 | A_right_nxengine.png
17 | A_wswan.png
18 | B.png
19 | B_wswan.png
20 | c_down.png
21 | circle.png
22 | c_left.png
23 | coin.png
24 | C.png
25 | C_pokemini.png
26 | C_pokemini_smaller.png
27 | c_right.png
28 | cross.png
29 | c_up.png
30 | digital.png
31 | dpad-down_no-mark.png
32 | dpad-down.png
33 | dpad-down_psx_no-mark.png
34 | dpad-down_psx.png
35 | dpad-left_no-mark.png
36 | dpad-left.png
37 | dpad-left_psx_no-mark.png
38 | dpad-left_psx.png
39 | dpad-right_no-mark.png
40 | dpad-right.png
41 | dpad-right_psx_no-mark.png
42 | dpad-right_psx.png
43 | dpad-up_no-mark.png
44 | dpad-up.png
45 | dpad-up_psx_no-mark.png
46 | dpad-up_psx.png
47 | D.png
48 | E.png
49 | fast_forward.png
50 | F.png
51 | genesis_mode.png
52 | hide.png
53 | III.png
54 | II.png
55 | I.png
56 | IV.png
57 | keyboard.png
58 | L1_down.png
59 | L1_down_smaller.png
60 | L1.png
61 | L1_smaller.png
62 | L2.png
63 | L2_up.png
64 | L3.png
65 | L3_smaller.png
66 | L_and_R_down.png
67 | L_and_R_left.png
68 | L_and_R.png
69 | L_down.png
70 | L_down_smaller_gba.png
71 | L_face.png
72 | L.png
73 | L_right.png
74 | L_right_smaller.png
75 | L_smaller.png
76 | no-analog.png
77 | overlay-A.png
78 | overlay-B.png
79 | overlay-C.png
80 | overlay-D.png
81 | pause_square_text.png
82 | pokemini_power.png
83 | pokemini_shake.png
84 | R1.png
85 | R1_smaller.png
86 | R2.png
87 | R2_up.png
88 | R3_smaller.png
89 | R_down.png
90 | R_down_smaller.png
91 | reset_square_text.png
92 | R_face.png
93 | rgui.png
94 | rotate.png
95 | R.png
96 | R_smaller.png
97 | select_psx.png
98 | select_rounded_big.png
99 | select_square_text.png
100 | show.png
101 | S_nxengine.png
102 | square.png
103 | start_dc.png
104 | start_genesis.png
105 | start.png
106 | start_psx.png
107 | start_rounded_big.png
108 | start_rounded.png
109 | test.png
110 | thumbstick-background.png
111 | thumbstick-pad_arcade.png
112 | thumbstick-pad-hollow.png
113 | thumbstick-pad.png
114 | triangle.png
115 | VI.png
116 | V.png
117 | W_nxengine.png
118 | X1_wswan.png
119 | X2_wswan.png
120 | X3_wswan.png
121 | X4_wswan.png
122 | X.png
123 | Y1_wswan.png
124 | Y2_wswan.png
125 | Y3_wswan.png
126 | Y4_wswan.png
127 | Y.png
128 | Z_down.png
129 | Z_down_smaller.png
130 | Z.png`.replace(indent, '\n').split('\n');
131 |
132 | window.defaultImagesObj = {};
133 | imageNames.forEach((el) => { defaultImagesObj[el] = 'img/' + el });
134 |
135 | window.defaultConfigString = `overlays = 4
136 | overlay0_name = "landscape"
137 | overlay0_full_screen = true
138 | overlay0_normalized = true
139 | overlay0_range_mod = 1.5
140 | overlay0_alpha_mod = 2.0
141 | overlay0_aspect_ratio = 1.7777778
142 | overlay0_auto_x_separation = true
143 | overlay0_auto_y_separation = true
144 | overlay0_block_x_separation = false
145 | overlay0_block_y_separation = false
146 | overlay0_descs = 21
147 | overlay0_desc0 = "left,0.07188,0.77778,radial,0.04479,0.06852"
148 | overlay0_desc0_overlay = dpad-left.png
149 | overlay0_desc1 = "right,0.17813,0.77778,radial,0.04479,0.06852"
150 | overlay0_desc1_overlay = dpad-right.png
151 | overlay0_desc2 = "up,0.12500,0.68333,radial,0.03854,0.07963"
152 | overlay0_desc2_overlay = dpad-up.png
153 | overlay0_desc3 = "down,0.12500,0.87222,radial,0.03854,0.07963"
154 | overlay0_desc3_overlay = dpad-down.png
155 | overlay0_desc4 = "left|up,0.05625,0.65556,rect,0.03021,0.05370"
156 | overlay0_desc5 = "right|up,0.19375,0.65556,rect,0.03021,0.05370"
157 | overlay0_desc6 = "left|down,0.05625,0.90000,rect,0.03021,0.05370"
158 | overlay0_desc7 = "right|down,0.19375,0.90000,rect,0.03021,0.05370"
159 | overlay0_desc8 = "a,0.93750,0.77778,radial,0.04167,0.07407"
160 | overlay0_desc8_overlay = A.png
161 | overlay0_desc9 = "b,0.87500,0.88889,radial,0.04167,0.07407"
162 | overlay0_desc9_overlay = B.png
163 | overlay0_desc10 = "x,0.87500,0.66667,radial,0.04167,0.07407"
164 | overlay0_desc10_overlay = X.png
165 | overlay0_desc11 = "y,0.81250,0.77778,radial,0.04167,0.07407"
166 | overlay0_desc11_overlay = Y.png
167 | overlay0_desc12 = "start,0.60000,0.91852,rect,0.03958,0.04444"
168 | overlay0_desc12_overlay = start_psx.png
169 | overlay0_desc13 = "select,0.40000,0.91852,rect,0.04063,0.04259"
170 | overlay0_desc13_overlay = select_psx.png
171 | overlay0_desc14 = "l,0.02917,0.50000,rect,0.05208,0.09259"
172 | overlay0_desc14_overlay = L1.png
173 | overlay0_desc15 = "l2,0.02917,0.30000,rect,0.05208,0.09259"
174 | overlay0_desc15_overlay = L2.png
175 | overlay0_desc16 = "r,0.97083,0.50000,rect,0.05208,0.09259"
176 | overlay0_desc16_overlay = R1.png
177 | overlay0_desc17 = "r2,0.97083,0.30000,rect,0.05208,0.09259"
178 | overlay0_desc17_overlay = R2.png
179 | overlay0_desc18 = "menu_toggle,0.07800,0.08889,radial,0.02604,0.04629"
180 | overlay0_desc18_overlay = rgui.png
181 | overlay0_desc19 = "overlay_next,0.92200,0.08889,radial,0.02604,0.04629"
182 | overlay0_desc19_overlay = rotate.png
183 | overlay0_desc19_next_target = "portrait"
184 | overlay0_desc20 = "overlay_next,0.17800,0.08889,radial,0.02604,0.04629"
185 | overlay0_desc20_overlay = analog.png
186 | overlay0_desc20_next_target = "landscape-analog"
187 | overlay1_name = "portrait"
188 | overlay1_full_screen = true
189 | overlay1_normalized = true
190 | overlay1_range_mod = 1.5
191 | overlay1_alpha_mod = 2.0
192 | overlay1_aspect_ratio = 0.5625
193 | overlay1_auto_x_separation = true
194 | overlay1_auto_y_separation = false
195 | overlay1_block_x_separation = false
196 | overlay1_block_y_separation = false
197 | overlay1_descs = 21
198 | overlay1_desc0 = "left,0.12037,0.85417,radial,0.07963,0.03854"
199 | overlay1_desc0_overlay = dpad-left.png
200 | overlay1_desc1 = "right,0.30926,0.85417,radial,0.07963,0.03854"
201 | overlay1_desc1_overlay = dpad-right.png
202 | overlay1_desc2 = "up,0.21481,0.80104,radial,0.06852,0.04479"
203 | overlay1_desc2_overlay = dpad-up.png
204 | overlay1_desc3 = "down,0.21481,0.90729,radial,0.06852,0.04479"
205 | overlay1_desc3_overlay = dpad-down.png
206 | overlay1_desc4 = "left|up,0.09259,0.78542,rect,0.05370,0.03021"
207 | overlay1_desc5 = "right|up,0.33704,0.78542,rect,0.05370,0.03021"
208 | overlay1_desc6 = "left|down,0.09259,0.92292,rect,0.05370,0.03021"
209 | overlay1_desc7 = "right|down,0.33704,0.92292,rect,0.05370,0.03021"
210 | overlay1_desc8 = "a,0.88889,0.85417,radial,0.07407,0.04167"
211 | overlay1_desc8_overlay = A.png
212 | overlay1_desc9 = "b,0.77778,0.91667,radial,0.07407,0.04167"
213 | overlay1_desc9_overlay = B.png
214 | overlay1_desc10 = "x,0.77778,0.79167,radial,0.07407,0.04167"
215 | overlay1_desc10_overlay = X.png
216 | overlay1_desc11 = "y,0.66667,0.85417,radial,0.07407,0.04167"
217 | overlay1_desc11_overlay = Y.png
218 | overlay1_desc12 = "start,0.70000,0.65000,rect,0.07037,0.02500"
219 | overlay1_desc12_overlay = start_psx.png
220 | overlay1_desc13 = "select,0.30000,0.65000,rect,0.07222,0.02396"
221 | overlay1_desc13_overlay = select_psx.png
222 | overlay1_desc14 = "l,0.04815,0.68021,rect,0.09259,0.05208"
223 | overlay1_desc14_overlay = L1.png
224 | overlay1_desc15 = "l2,0.04815,0.56771,rect,0.09259,0.05208"
225 | overlay1_desc15_overlay = L2.png
226 | overlay1_desc16 = "r,0.95185,0.68021,rect,0.09259,0.05208"
227 | overlay1_desc16_overlay = R1.png
228 | overlay1_desc17 = "r2,0.95185,0.56771,rect,0.09259,0.05208"
229 | overlay1_desc17_overlay = R2.png
230 | overlay1_desc18 = "menu_toggle,0.5,0.65000,radial,0.04633,0.02604"
231 | overlay1_desc18_overlay = rgui.png
232 | overlay1_desc19 = "overlay_next,0.5,0.55000,radial,0.04633,0.02604"
233 | overlay1_desc19_overlay = rotate.png
234 | overlay1_desc19_next_target = "landscape"
235 | overlay1_desc20 = "overlay_next,0.30000,0.55000,radial,0.04629,0.02604"
236 | overlay1_desc20_overlay = analog.png
237 | overlay1_desc20_next_target = "portrait-analog"
238 | overlay2_name = "landscape-analog"
239 | overlay2_full_screen = true
240 | overlay2_normalized = true
241 | overlay2_range_mod = 1.5
242 | overlay2_alpha_mod = 2.0
243 | overlay2_aspect_ratio = 1.7777778
244 | overlay2_auto_x_separation = true
245 | overlay2_auto_y_separation = true
246 | overlay2_block_x_separation = false
247 | overlay2_block_y_separation = false
248 | overlay2_descs = 15
249 | overlay2_desc0 = "a,0.93750,0.77778,radial,0.04167,0.07407"
250 | overlay2_desc0_overlay = A.png
251 | overlay2_desc1 = "b,0.87500,0.88889,radial,0.04167,0.07407"
252 | overlay2_desc1_overlay = B.png
253 | overlay2_desc2 = "x,0.87500,0.66667,radial,0.04167,0.07407"
254 | overlay2_desc2_overlay = X.png
255 | overlay2_desc3 = "y,0.81250,0.77778,radial,0.04167,0.07407"
256 | overlay2_desc3_overlay = Y.png
257 | overlay2_desc4 = "start,0.60000,0.91852,rect,0.03958,0.04444"
258 | overlay2_desc4_overlay = start_psx.png
259 | overlay2_desc5 = "select,0.40000,0.91852,rect,0.04063,0.04259"
260 | overlay2_desc5_overlay = select_psx.png
261 | overlay2_desc6 = "l,0.02917,0.50000,rect,0.05208,0.09259"
262 | overlay2_desc6_overlay = L1.png
263 | overlay2_desc7 = "l2,0.02917,0.30000,rect,0.05208,0.09259"
264 | overlay2_desc7_overlay = L2.png
265 | overlay2_desc8 = "r,0.97083,0.50000,rect,0.05208,0.09259"
266 | overlay2_desc8_overlay = R1.png
267 | overlay2_desc9 = "r2,0.97083,0.30000,rect,0.05208,0.09259"
268 | overlay2_desc9_overlay = R2.png
269 | overlay2_desc10 = "menu_toggle,0.07800,0.08889,radial,0.02604,0.04629"
270 | overlay2_desc10_overlay = rgui.png
271 | overlay2_desc11 = "overlay_next,0.92200,0.08889,radial,0.02604,0.04629"
272 | overlay2_desc11_overlay = rotate.png
273 | overlay2_desc11_next_target = "portrait-analog"
274 | overlay2_desc12 = "overlay_next,0.82200,0.08889,radial,0.02604,0.04629"
275 | overlay2_desc12_overlay = digital.png
276 | overlay2_desc12_next_target = "landscape"
277 | overlay2_desc13 = "null,0.12500,0.77778,rect,0.09000,0.16"
278 | overlay2_desc13_overlay = thumbstick-background.png
279 | overlay2_desc14 = "analog_left,0.12500,0.77778,radial,0.09000,0.16"
280 | overlay2_desc14_overlay = thumbstick-pad_arcade.png
281 | overlay2_desc14_range_mod = 2.0
282 | overlay2_desc14_saturate_pct = 0.65
283 | overlay2_desc14_movable = true
284 | overlay3_name = "portrait-analog"
285 | overlay3_full_screen = true
286 | overlay3_normalized = true
287 | overlay3_range_mod = 1.5
288 | overlay3_alpha_mod = 2.0
289 | overlay3_aspect_ratio = 0.5625
290 | overlay3_auto_x_separation = true
291 | overlay3_auto_y_separation = false
292 | overlay3_block_x_separation = false
293 | overlay3_block_y_separation = false
294 | overlay3_descs = 15
295 | overlay3_desc0 = "a,0.88889,0.85417,radial,0.07407,0.04167"
296 | overlay3_desc0_overlay = A.png
297 | overlay3_desc1 = "b,0.77778,0.91667,radial,0.07407,0.04167"
298 | overlay3_desc1_overlay = B.png
299 | overlay3_desc2 = "x,0.77778,0.79167,radial,0.07407,0.04167"
300 | overlay3_desc2_overlay = X.png
301 | overlay3_desc3 = "y,0.66667,0.85417,radial,0.07407,0.04167"
302 | overlay3_desc3_overlay = Y.png
303 | overlay3_desc4 = "start,0.70000,0.65000,rect,0.07037,0.02500"
304 | overlay3_desc4_overlay = start_psx.png
305 | overlay3_desc5 = "select,0.30000,0.65000,rect,0.07222,0.02396"
306 | overlay3_desc5_overlay = select_psx.png
307 | overlay3_desc6 = "l,0.04815,0.68021,rect,0.09259,0.05208"
308 | overlay3_desc6_overlay = L1.png
309 | overlay3_desc7 = "l2,0.04815,0.56771,rect,0.09259,0.05208"
310 | overlay3_desc7_overlay = L2.png
311 | overlay3_desc8 = "r,0.95185,0.68021,rect,0.09259,0.05208"
312 | overlay3_desc8_overlay = R1.png
313 | overlay3_desc9 = "r2,0.95185,0.56771,rect,0.09259,0.05208"
314 | overlay3_desc9_overlay = R2.png
315 | overlay3_desc10 = "menu_toggle,0.5,0.65000,radial,0.04633,0.02604"
316 | overlay3_desc10_overlay = rgui.png
317 | overlay3_desc11 = "overlay_next,0.5,0.55000,radial,0.04633,0.02604"
318 | overlay3_desc11_overlay = rotate.png
319 | overlay3_desc11_next_target = "landscape-analog"
320 | overlay3_desc12 = "overlay_next,0.70000,0.55000,radial,0.04629,0.02604"
321 | overlay3_desc12_overlay = digital.png
322 | overlay3_desc12_next_target = "portrait"
323 | overlay3_desc13 = "null,0.21481,0.85417,rect,0.16000,0.09000"
324 | overlay3_desc13_overlay = thumbstick-background.png
325 | overlay3_desc14 = "analog_left,0.21481,0.85417,radial,0.16000,0.09000"
326 | overlay3_desc14_overlay = thumbstick-pad_arcade.png
327 | overlay3_desc14_range_mod = 2.0
328 | overlay3_desc14_saturate_pct = 0.65
329 | overlay3_desc14_movable = true`.replace(indent, '\n');
330 |
331 | window.buttonCommandList = `up
332 | down
333 | left
334 | right
335 | a
336 | b
337 | x
338 | y
339 | l
340 | l2
341 | l3
342 | r
343 | r2
344 | r3
345 | select
346 | start
347 | analog_left
348 | analog_right
349 | l_x_minus
350 | l_x_plus
351 | l_y_minus
352 | l_y_plus
353 | abxy_area
354 | dpad_area
355 | #
356 | menu_toggle
357 | overlay_next
358 | load_state
359 | save_state
360 | state_slot_increase
361 | state_slot_decrease
362 | shader_next
363 | shader_prev
364 | rewind
365 | hold_fast_forward
366 | toggle_fast_forward
367 | hold_slowmotion
368 | toggle_slowmotion
369 | audio_mute
370 | pause_toggle
371 | screenshot
372 | reset
373 | exit_emulator`.replace(indent, '\n').replace('#', '');
374 | }
--------------------------------------------------------------------------------
/js/editor.js:
--------------------------------------------------------------------------------
1 | //Default screen dimensions (16:9 - compatible with built-in overlays)
2 | const DEF_WIDTH = 800;
3 | const DEF_HEIGHT = 450;
4 | const DEF_SCR_WIDTH = 600;
5 | const DEF_SCR_HEIGHT = 450;
6 |
7 | const defaultParamsForNewOverlay = 'full_screen = true\nnormalized = true\nrange_mod = 1.5\nalpha_mod = 2.0';
8 | const autoScaleParams = 'auto_x_separation = true\n'; //auto_y_separation = ?
9 | const manualScaleParams = 'block_x_separation = false\nblock_y_separation = false';
10 |
11 | let importedFilename = 'retropad.cfg';
12 | let currentRect;
13 |
14 | let screen = {
15 | _width: DEF_WIDTH,
16 | _height: DEF_HEIGHT,
17 |
18 | isSetByUser: false,
19 | isPortrait: false,
20 |
21 | scale: 1,
22 |
23 | get longSide() { return Math.max(this._height, this._width) },
24 | get shortSide() { return Math.min(this._height, this._width) },
25 |
26 | set width(value) { this._width = Number(value || screen._width || DEF_WIDTH) },
27 | get enteredWidth() { return this.isPortrait ? this.shortSide : this.longSide },
28 | get width() { return this.enteredWidth * this.scale },
29 |
30 | set height(value) { this._height = Number(value || screen._height || DEF_HEIGHT) },
31 | get enteredHeight() { return this.isPortrait ? this.longSide : this.shortSide },
32 | get height() { return this.enteredHeight * this.scale },
33 |
34 | shotFrameWidth: DEF_SCR_WIDTH,
35 | shotFrameHeight: DEF_SCR_HEIGHT,
36 |
37 | shotImage: null,
38 | // screenshot image dimensions
39 | shotWidth: 0,
40 | shotHeight: 0,
41 |
42 | shotShow: true,
43 | shotMode: 'fit', // fit, set, match
44 | }
45 |
46 | let images = {};
47 | if (defaultImagesObj) // defaults.js
48 | images = defaultImagesObj;
49 |
50 | let userImages = [];
51 |
52 | fillCommandSelector(buttonCommandList);
53 | fillImageSelector();
54 |
55 | let conf = new ConfigHandler();
56 | let configStr = defaultConfigString; // defaults.js
57 | renderConfig(configStr);
58 |
59 | 'xywh'.split('').forEach(elem => {
60 | let range = document.getElementById(elem + '-range');
61 | let text = document.getElementById(elem + '-number');
62 |
63 | range.addEventListener('input', (e) => {
64 | applyButtonParam(elem, e.target.value);
65 | text.value = e.target.value;
66 | });
67 |
68 | text.addEventListener('input', (e) => {
69 | applyButtonParam(elem, e.target.value);
70 | range.value = e.target.value;
71 | });
72 | });
73 |
74 | document.getElementById('chk-show-shapes').addEventListener('change', toggleShapes);
75 | document.getElementById('chk-show-names').addEventListener('change', toggleNames);
76 | document.getElementById('chk-show-portrait').addEventListener('change', toggleOrientation);
77 | document.getElementById('chk-show-offscreen').addEventListener('change', toggleOffscreen);
78 | document.getElementById('overlay-selector').addEventListener('change', selectOverlay);
79 |
80 | document.getElementById('command-select').addEventListener('change', fillCommandField);
81 | document.getElementById('image-select').addEventListener('change', fillImageNameField);
82 | document.getElementById('image-name').addEventListener('input', e => showImagePreview(e.target.value));
83 |
84 | document.getElementById('load-config').addEventListener('change', loadConfigFromFile);
85 | document.getElementById('load-button-images').addEventListener('change', loadImageFiles);
86 | document.getElementById('load-screenshot').addEventListener('change', loadScreenshotFile);
87 | document.getElementById('chk-show-screenshot').addEventListener('change', toggleScreenshot);
88 |
89 |
90 | function applyButtonParam(section, sValue) {
91 | let value = Number(sValue);
92 |
93 | if (conf.isGroupSelected()) {
94 | conf.setSelectionSectionValue(section, value);
95 | syncSelectedButtons();
96 | } else {
97 | updateCurrentLine(section, value);
98 | }
99 | }
100 |
101 |
102 | function createPadView() {
103 | let background = createPadBackground();
104 | let rects = conf.buildPadFromConfig();
105 | if (!rects)
106 | return;
107 |
108 | for (let i = 0; i < rects.length; i++) {
109 | let r = rects[i];
110 | let b = createRect(background, r.command, r.x, r.y, r.w, r.h, r.pct);
111 |
112 | if (r.img)
113 | b.style['background-image'] = 'url(' + images[r.img] + ')';
114 |
115 | if (r.s == 'radial')
116 | b.classList.add('radial');
117 |
118 | b.dataset.lineIndex = r.i;
119 | if (conf.isLineInSelection(r.i))
120 | b.classList.add('selected');
121 |
122 | b.addEventListener('click', () => {
123 | if (currentRect)
124 | currentRect.classList.remove('selected');
125 |
126 | conf.setCurrentLine(r.i);
127 | currentRect = b;
128 |
129 | b.classList.add('selected');
130 |
131 | 'xywh'.split('').forEach(elem => {
132 | let range = document.getElementById(elem + '-range');
133 | let text = document.getElementById(elem + '-number');
134 | text.value = conf.getCurrentLineSectionValue(elem);
135 | range.value = conf.getCurrentLineSectionValue(elem);
136 | });
137 |
138 | enableEditor(true);
139 | document.activeElement.blur();
140 | });
141 | }
142 | }
143 |
144 |
145 | function createPadBackground() {
146 | let backgroundDiv = document.createElement('DIV');
147 | backgroundDiv.classList.add('screenpad-background');
148 |
149 | let bg = conf.getCurrentOverlayBackground();
150 | if (bg.image) {
151 | backgroundDiv.style['background-image'] = 'url(' + images[bg.image] + ')';
152 | }
153 |
154 | if (bg.position) {
155 | backgroundDiv.style.left = (bg.position.x * 100) + '%';
156 | backgroundDiv.style.top = (bg.position.y * 100) + '%';
157 | backgroundDiv.style.width = (bg.position.w * 100) + '%';
158 | backgroundDiv.style.height = (bg.position.h * 100) + '%';
159 | }
160 |
161 | let padFrame;
162 | if (bg.fullscreen)
163 | padFrame = document.getElementById('screenpad');
164 | else
165 | padFrame = document.getElementById('game-screenshot');
166 |
167 | padFrame.appendChild(backgroundDiv);
168 |
169 | let startX = 0;
170 | let startY = 0;
171 | let isMouseDown = false;
172 |
173 | let select = document.createElement('DIV');
174 | select.classList.add('selection-box');
175 | backgroundDiv.appendChild(select);
176 |
177 | let padContianer = document.getElementById('gamepad-container');
178 | padContianer.onmouseup = cancelSelection;
179 | padContianer.onpointerleave = cancelSelection;
180 |
181 | function cancelSelection() {
182 | select.style.display = 'none';
183 | isMouseDown = false;
184 |
185 | let indexes = conf.getSelectedIndexes();
186 | if (indexes.length == 0 && conf.getCurrentLineSectionValue('shape') === null)
187 | enableEditor(false);
188 |
189 | if (indexes.length == 1) {
190 | let index = indexes[0];
191 | conf.resetGroupSelection();
192 | conf.setCurrentLine(index);
193 | let elem = document.querySelectorAll('.rect[data-line-index="' + index + '"]')[0];
194 |
195 | setTimeout(() => {
196 | elem.dispatchEvent(new Event('click'));
197 | }, 0);
198 | }
199 | }
200 |
201 | backgroundDiv.onmousedown = (event) => {
202 | if (event.button != 0)
203 | return;
204 |
205 | let bgRect = backgroundDiv.getBoundingClientRect();
206 | let tx = bgRect.left;
207 | let ty = bgRect.top;
208 | startX = event.clientX - tx;
209 | startY = event.clientY - ty;
210 |
211 | isMouseDown = true;
212 | deselectAll();
213 | conf.setCurrentLine(-1);
214 | currentRect = null;
215 | event.preventDefault();
216 | }
217 |
218 | padContianer.onmousemove = (event) => {
219 | if (event.buttons != 1 || !isMouseDown)
220 | return;
221 |
222 | select.style.display = 'block';
223 |
224 | let bgRect = backgroundDiv.getBoundingClientRect();
225 | let tx = bgRect.left;
226 | let ty = bgRect.top;
227 |
228 | let endX = event.clientX - tx;
229 | let endY = event.clientY - ty;
230 |
231 |
232 | setControls(startX, startY, endX, endY);
233 | }
234 |
235 | // empty event listener (fix for old FF)
236 | document.getElementById('editor').ontouchstart = () => { };
237 |
238 | backgroundDiv.ontouchstart = (event) => {
239 | let touches = event.touches;
240 | if (touches.length != 2) {
241 | select.style.display = 'none';
242 | return;
243 | }
244 |
245 | select.style.display = 'block';
246 |
247 | let bgRect = backgroundDiv.getBoundingClientRect();
248 | let tx = bgRect.left;
249 | let ty = bgRect.top;
250 |
251 | let startX = touches[0].clientX - tx;
252 | let startY = touches[0].clientY - ty;
253 |
254 | let endX = touches[1].clientX - tx;
255 | let endY = touches[1].clientY - ty;
256 |
257 | setControls(startX, startY, endX, endY);
258 | }
259 |
260 | function setControls(sX, sY, eX, eY) {
261 | let left = Math.min(sX, eX);
262 | let top = Math.min(sY, eY);
263 | let right = (backgroundDiv.clientWidth - Math.max(sX, eX));
264 | let bottom = (backgroundDiv.clientHeight - Math.max(sY, eY));
265 |
266 | select.style.left = left + 'px';
267 | select.style.top = top + 'px';
268 | select.style.right = right + 'px';
269 | select.style.bottom = bottom + 'px';
270 |
271 | getButtonsInRect(left, top, right, bottom, backgroundDiv);
272 | setEditorControls();
273 | }
274 |
275 | return backgroundDiv;
276 | }
277 |
278 |
279 | function getButtonsInRect(left, top, right, bottom, container) {
280 | let bgRect = container.getBoundingClientRect();
281 | let cWidth = bgRect.width;
282 | let cHeight = bgRect.height;
283 |
284 | let rectLeft = left / cWidth;
285 | let rectTop = top / cHeight;
286 | let rectRight = (cWidth - right) / cWidth;
287 | let rectBottom = (cHeight - bottom) / cHeight;
288 |
289 | let indexes = conf.selectButtonsInBounds(rectLeft, rectTop, rectRight, rectBottom);
290 |
291 | let rects = document.querySelectorAll('.rect');
292 | rects.forEach(e => e.classList.remove('selected'));
293 |
294 | indexes.forEach((e) => {
295 | let elem = document.querySelectorAll('.rect[data-line-index="' + e + '"]');
296 | if (elem[0])
297 | elem[0].classList.add('selected');
298 | });
299 | }
300 |
301 |
302 | function syncSelectedButtons() {
303 | let indexes = conf.getSelectedIndexes();
304 |
305 | indexes.forEach((e) => {
306 | let elem = document.querySelectorAll('.rect[data-line-index="' + e + '"]');
307 | if (elem[0]) {
308 | currentRect = elem[0];
309 | conf.setCurrentLine(e);
310 | updateCurrentLine(null);
311 | } else {
312 | console.log('wrong selection index', e)
313 | }
314 | });
315 | }
316 |
317 |
318 | function deselectAll() {
319 | let rects = document.querySelectorAll('.rect');
320 | rects.forEach(e => e.classList.remove('selected'));
321 | conf.resetGroupSelection();
322 | }
323 |
324 |
325 | function setEditorControls() {
326 | enableEditor(false);
327 | let size = conf.getSelectionDimensions();
328 | if (size)
329 | enableEditorSliders(true);
330 | else
331 | return;
332 |
333 | 'xywh'.split('').forEach(elem => {
334 | let range = document.getElementById(elem + '-range');
335 | let text = document.getElementById(elem + '-number');
336 | text.value = Number(size[elem].toFixed(10));
337 | range.value = size[elem];
338 | });
339 | }
340 |
341 |
342 | function loadConfigFromFile(e) {
343 | let file = e.target.files[0];
344 | if (!file)
345 | return;
346 |
347 | importedFilename = file.name;
348 |
349 | let reader = new FileReader();
350 | reader.onload = function (ev) {
351 | configStr = ev.target.result;
352 | try {
353 | renderConfig(ev.target.result);
354 | } catch {
355 | let errMsg = 'FILE PARSING ERROR!';
356 | console.log(errMsg);
357 | alert(errMsg + '\nReload page and try again.')
358 | }
359 | };
360 | reader.readAsText(file);
361 | }
362 |
363 |
364 | function renderConfig(str) {
365 | conf.convertCfgToArray(str, () => {
366 | buildAndSetOverlaySelectors(0);
367 |
368 | screen.isPortrait = -1 != conf.getOverlayList()[0].search('portrait');
369 | document.getElementById('chk-show-portrait').checked = screen.isPortrait;
370 |
371 | setScreenDimensions();
372 | redrawPad();
373 | },
374 | images);
375 | }
376 |
377 |
378 | function loadImageFiles(e) {
379 | let imgCounter = 0;
380 | let loadCounter = 0;
381 |
382 | for (let i = 0; i < e.target.files.length; i++) {
383 | let file = e.target.files[i];
384 |
385 | let ext = e.target.files[i].name.substr(-4);
386 |
387 | if (!file || (ext != '.png' && ext != '.jpg'))
388 | continue;
389 |
390 | imgCounter++;
391 | let name = e.target.files[i].name;
392 | console.log(name);
393 |
394 | let reader = new FileReader();
395 |
396 | reader.onload = function (ev) {
397 | images[name] = ev.target.result;
398 |
399 | if (!userImages.includes(name)) {
400 | userImages.push(name);
401 | console.log(name);
402 | }
403 |
404 | // onload is async function so loop ends BEFORE it's first launch
405 | if (++loadCounter == imgCounter) {
406 | redrawPad();
407 | fillImageSelector();
408 | }
409 | };
410 |
411 | reader.readAsDataURL(file);
412 | }
413 | }
414 |
415 |
416 | function loadScreenshotFile(e) {
417 | let file = e.target.files[0];
418 |
419 | let name = file.name;
420 | console.log(name);
421 |
422 | let reader = new FileReader();
423 |
424 | reader.onload = function (ev) {
425 | screen.shotImage = ev.target.result;
426 | screen.shotShow = true;
427 | refreshScreenshot();
428 |
429 | //get image dimensions;
430 | if (screen.shotImage) {
431 | let im = document.createElement('IMG');
432 | im.onload = function () {
433 | screen.shotWidth = im.naturalWidth;
434 | screen.shotHeight = im.naturalHeight;
435 | console.log('Size', im.naturalWidth, im.naturalHeight);
436 |
437 | setScreenDimensions();
438 | redrawPad();
439 | }
440 | im.src = screen.shotImage;
441 | }
442 | }
443 |
444 | reader.readAsDataURL(file);
445 | }
446 |
447 |
448 | function refreshScreenshot() {
449 | let shot = document.getElementById('game-screenshot');
450 |
451 | let screenCheckbox = document.getElementById('chk-show-screenshot')
452 | screenCheckbox.checked = screen.shotShow;
453 | screenCheckbox.disabled = !screen.shotImage;
454 |
455 | if (screen.shotShow && screen.shotImage)
456 | shot.style['background-image'] = 'url(' + screen.shotImage + ')';
457 | else
458 | shot.style['background-image'] = 'none';
459 | }
460 |
461 |
462 | function createRect(target, name, x, y, w, h, pct) {
463 | let rect = document.createElement('DIV');
464 | let text = document.createTextNode(name);
465 | rect.appendChild(text);
466 | rect.classList.add('rect');
467 |
468 | if (pct) {
469 | // visualize thumbstick saturate_pct property
470 | let inner = document.createElement('DIV');
471 | let perc = Math.round(pct * 70);
472 | inner.style['background-image'] = 'radial-gradient(transparent, rgba(100,100,200,0.4) ' + perc + '%, transparent ' + (perc + 1) + '%)';
473 | rect.appendChild(inner);
474 | }
475 |
476 | let bw = 100 * w * 2;
477 | let bh = 100 * h * 2;
478 |
479 | let bx = 100 * x - bw / 2;
480 | let by = 100 * y - bh / 2;
481 |
482 | rect.style.left = bx + '%';
483 | rect.style.top = by + '%';
484 |
485 | rect.style.width = bw + '%';
486 | rect.style.height = bh + '%';
487 |
488 | target.appendChild(rect);
489 | return rect;
490 | }
491 |
492 |
493 | function redrawPad() {
494 | resetScreen();
495 | refreshScreenshot();
496 | createPadView();
497 | enableEditor(false);
498 | }
499 |
500 |
501 | function resetScreen() {
502 | let s = document.getElementById('screenpad');
503 |
504 | s.style.width = screen.width + 'px';
505 | s.style.height = screen.height + 'px';
506 |
507 | s.innerHTML = '';
508 |
509 | let d = document.createElement('DIV');
510 | d.classList.add('inner');
511 | d.id = 'game-screenshot'
512 |
513 | let shotWidth = screen.shotFrameWidth * screen.scale;
514 | let shotHeight = screen.shotFrameHeight * screen.scale;
515 |
516 | d.style.width = shotWidth + 'px';
517 | d.style.height = shotHeight + 'px';
518 |
519 | d.style.left = (screen.width - shotWidth) / 2 + 'px';
520 |
521 | if (screen.isPortrait)
522 | d.style.top = 0;
523 | else
524 | d.style.top = (screen.height - shotHeight) / 2 + 'px';
525 |
526 | s.appendChild(d);
527 | }
528 |
529 |
530 | function setScreenDimensions(width, height, screenshotWidth, screenshotHeight) {
531 | screen.width = width;
532 | screen.height = height;
533 |
534 | let ratio = 16 / 9;
535 | let aspect = conf.getOverlayAspectRatio();
536 | if (aspect)
537 | ratio = aspect.w / aspect.h
538 |
539 | // Reverse ratio if it does not match overlay name or orientation checkbox
540 | if ((screen.isPortrait && ratio > 1) ||
541 | (!screen.isPortrait && ratio < 1))
542 | ratio = 1 / ratio;
543 |
544 | if (!screen.isSetByUser)
545 | if (screen.isPortrait) {
546 | screen.width = DEF_HEIGHT;
547 | screen.height = Math.round(DEF_HEIGHT / ratio);
548 | } else {
549 | screen.height = DEF_HEIGHT;
550 | screen.width = Math.round(DEF_HEIGHT * ratio);
551 | }
552 |
553 | // Swap sides if height > width
554 | let ewidth = screen.enteredWidth;
555 | let eheight = screen.enteredHeight;
556 |
557 | let sw = Number(screenshotWidth || screen.shotFrameWidth || DEF_SCR_WIDTH);
558 | let sh = Number(screenshotHeight || screen.shotFrameHeight || DEF_SCR_HEIGHT);
559 |
560 | if (screen.shotImage && screen.shotShow) {
561 | switch (screen.shotMode) {
562 | case 'match':
563 | sw = screen.shotWidth;
564 | sh = screen.shotHeight;
565 | break;
566 |
567 | case 'fit':
568 | if (ewidth / eheight > screen.shotWidth / screen.shotHeight) {
569 | sw = eheight * (screen.shotWidth / screen.shotHeight);
570 | sh = eheight;
571 | } else {
572 | sw = ewidth;
573 | sh = ewidth / (screen.shotWidth / screen.shotHeight);
574 | }
575 | }
576 | } else if (screen.shotMode == 'fit') {
577 | if (ewidth / eheight > sw / sh) {
578 | sw = eheight * (sw / sh);
579 | sh = eheight;
580 | } else {
581 | sh = ewidth / (sw / sh);
582 | sw = ewidth;
583 | }
584 | }
585 |
586 | screen.shotFrameWidth = sw;
587 | screen.shotFrameHeight = sh;
588 | }
589 |
590 |
591 | function applyScreenDimensions() {
592 | let w = document.getElementById('display-width').value;
593 | let h = document.getElementById('display-height').value;
594 | let sw = document.getElementById('screenshot-width').value;
595 | let sh = document.getElementById('screenshot-height').value;
596 |
597 | let fit = document.getElementById('radio-screenshot-fit').checked;
598 | let match = document.getElementById('radio-screenshot-match').checked;
599 | let setSize = document.getElementById('radio-screenshot-set').checked;
600 |
601 | screen.isSetByUser = true;
602 | screen.shotMode = fit ? 'fit' : match ? 'match' : setSize ? 'set' : 'fit';
603 |
604 | hideScreenSizeDialog();
605 |
606 | if (document.getElementById('chk-rescale-to-fit').checked)
607 | screen.scale = calculateScreenSizeToFit(w, h);
608 | else
609 | screen.scale = 1;
610 |
611 | setScreenDimensions(w, h, sw, sh);
612 |
613 | redrawPad();
614 | }
615 |
616 |
617 | function createDownloadLink() {
618 | let file = new Blob([conf.getConfigString()], { type: 'text/cfg' });
619 | let a = document.getElementById('export-link');
620 | a.href = URL.createObjectURL(file);
621 | a.download = 'new-' + importedFilename;
622 | }
623 |
624 |
625 | function updateCurrentLine(section, value) {
626 | if (conf.getCurrentLineSectionValue('shape') === null)
627 | return;
628 |
629 | if (section)
630 | conf.setCurrentLineSectionValue(section, value);
631 |
632 | let rw = 100 * conf.getCurrentLineSectionValue('w') * 2;
633 | let rh = 100 * conf.getCurrentLineSectionValue('h') * 2;
634 |
635 | let rx = 100 * conf.getCurrentLineSectionValue('x') - rw / 2;
636 | let ry = 100 * conf.getCurrentLineSectionValue('y') - rh / 2;
637 |
638 | if (currentRect) {
639 | currentRect.style.height = rh + '%';
640 | currentRect.style.width = rw + '%';
641 | currentRect.style.left = rx + '%';
642 | currentRect.style.top = ry + '%';
643 | }
644 | }
645 |
646 |
647 | function buildAndSetOverlaySelectors(selectIndex) {
648 | let list = conf.getOverlayList();
649 |
650 | let select = document.getElementById('overlay-selector');
651 | select.innerHTML = '';
652 |
653 | for (let i = 0; i < list.length; i++) {
654 | let name = (i + 1) + ' - ' + (list[i] ? list[i] : '[unnamed]');
655 | let o = document.createElement('OPTION');
656 | o.appendChild(document.createTextNode(name));
657 | select.appendChild(o);
658 | }
659 |
660 | selectIndex = Math.min(selectIndex, list.length - 1);
661 | select.selectedIndex = selectIndex;
662 | conf.setCurrentOverlay(selectIndex);
663 | screen.isPortrait = list[selectIndex].search('portrait') != -1;
664 |
665 | document.getElementById('chk-show-portrait').checked = screen.isPortrait;
666 |
667 | let selectNext = document.getElementById('next_target_property');
668 | selectNext.innerHTML = '';
669 |
670 | selectNext.appendChild(document.createElement('OPTION'));
671 |
672 | for (let i = 0; i < list.length; i++) {
673 | if (list[i]) {
674 | let o = document.createElement('OPTION');
675 | o.appendChild(document.createTextNode(list[i]));
676 | selectNext.appendChild(o);
677 | }
678 | }
679 | }
680 |
681 |
682 | function fillButtonEditor(command, shape, image, addLines) {
683 | document.getElementById('command-name').value = command;
684 | document.getElementById('button-shape').selectedIndex = shape == 'rect' ? 0 : 1;
685 |
686 | if (image)
687 | document.getElementById('image-name').value = image;
688 | else
689 | document.getElementById('image-name').value = '';
690 |
691 | showImagePreview(image);
692 |
693 | setImageSelectorOption(image);
694 | setCommandSelectorOption(command);
695 |
696 | fillAdditionalPropsFields(addLines.split('\n'));
697 | }
698 |
699 |
700 | function fillImageSelector() {
701 | let selector = document.getElementById('image-select');
702 | selector.innerHTML = '';
703 |
704 | userImages.sort();
705 |
706 | let listAll = [];
707 | if (userImages.length > 0)
708 | listAll = listAll.concat(userImages);
709 |
710 | let defImages = [''];
711 | for (let f in images)
712 | if (!userImages.includes(f))
713 | defImages.push(f);
714 |
715 | listAll = listAll.concat(defImages);
716 |
717 | for (let name of listAll) {
718 | o = document.createElement('OPTION');
719 | o.appendChild(document.createTextNode(name));
720 | selector.appendChild(o);
721 | }
722 | }
723 |
724 |
725 | function setImageSelectorOption(value) {
726 | let s = document.getElementById('image-select');
727 | s.value = '';
728 |
729 | for (let i = 0; i < s.options.length; i++) {
730 | if (s.options[i].text == value) {
731 | s.selectedIndex = i;
732 | break;
733 | }
734 | }
735 | }
736 |
737 |
738 | function fillCommandSelector(commands) {
739 | commands = commands.split('\n');
740 | let s = document.getElementById('command-select');
741 |
742 | commands.forEach((e) => {
743 | let o = document.createElement('OPTION');
744 | o.appendChild(document.createTextNode(e));
745 | s.appendChild(o);
746 | })
747 | }
748 |
749 |
750 | function setCommandSelectorOption(value) {
751 | let s = document.getElementById('command-select');
752 | s.selectedIndex = 0;
753 |
754 | for (let i = 0; i < s.options.length; i++) {
755 | if (s.options[i].text == value) {
756 | s.selectedIndex = i;
757 | break;
758 | }
759 | }
760 | }
761 |
762 |
763 | function showAdditionalParametersForCommand(command) {
764 | let parameters = {
765 | analog_left: 'movable = true\nrange_mod = 2.0\nsaturate_pct = 0.65',
766 | get analog_right() { return this.analog_left },
767 |
768 | get overlay_next() {
769 | let list = conf.getOverlayList();
770 | let current = conf.getCurrentOverlay();
771 |
772 | if (list.length <= 1)
773 | return '';
774 |
775 | if (current < list.length - 1)
776 | return 'next_target = ' + list[current + 1]
777 | else
778 | return 'next_target = ' + list[0];
779 | },
780 |
781 | dpad_area: 'range_mod_exclusive = true',
782 | abxy_area: 'range_mod_exclusive = true',
783 | }
784 |
785 | return parameters[command];
786 | }
787 |
788 |
789 | function enableEditor(enable) {
790 | enableEditorSliders(enable)
791 | document.getElementById('show-button-editor').disabled = !enable;
792 | document.getElementById('del-current-button').disabled = !enable;
793 | }
794 |
795 |
796 | function enableEditorSliders(enable) {
797 | let editor = document.getElementById('editor');
798 | let inputs = editor.querySelectorAll('input,button');
799 | inputs.forEach(e => { e.disabled = !enable })
800 | }
801 |
802 |
803 | function fillAdditionalPropsFields(data) {
804 | clearAdditionalPropsFields();
805 |
806 | if (!Array.isArray(data) || data.length == 0 || data[0] == '')
807 | return;
808 |
809 | let others = document.getElementById('raw-button-properties');
810 | let othData = '';
811 |
812 | data.forEach(e => {
813 | let earr = e.split('=');
814 | let prop = earr[0].trim();
815 | let val = earr[1] ? earr[1].trim() : '';
816 | let fields = [];
817 |
818 | try {
819 | fields = document.querySelectorAll('.js-additional-button-property #' + prop + '_property');
820 | } catch {
821 | console.log('probably wrong property name', prop);
822 | }
823 |
824 | switch (fields.length) {
825 | case 1:
826 | fields[0].value = val;
827 | break;
828 |
829 | case 0:
830 | othData += e + '\n';
831 | break;
832 |
833 | default:
834 | console.log('More than one ui element found!');
835 | }
836 |
837 | });
838 |
839 | others.value = othData.trim();
840 | }
841 |
842 |
843 | function clearAdditionalPropsFields() {
844 | let v = document.querySelectorAll('.js-additional-button-property input, .js-additional-button-property select');
845 |
846 | v.forEach(e => e.value = '');
847 | document.getElementById('raw-button-properties').value = '';
848 | }
849 |
850 |
851 | function readAdditionalPropsFields() {
852 | let v = document.querySelectorAll('.js-additional-button-property input, .js-additional-button-property select');
853 | let result = [];
854 |
855 | v.forEach(e => {
856 | if (e.value != '') {
857 | let propName = e.id.substr(0, e.id.search(/_property$/));
858 | result.push(propName + ' = ' + e.value);
859 | }
860 | });
861 |
862 | let raw = document.getElementById('raw-button-properties').value;
863 |
864 | return result.concat(processRawProperties(raw));
865 | }
866 |
867 |
868 | function processRawProperties(str) {
869 | let arr = str.trim().split('\n');
870 | let ret = [];
871 |
872 | arr.forEach(e => {
873 | let line = e.trim();
874 | if (line == '')
875 | return;
876 |
877 | let eqPos = line.indexOf('=');
878 |
879 | if (eqPos <= 0) {
880 | alert('Error in line "' + line + '"\nProperty removed');
881 | return;
882 | }
883 |
884 | let prop = line.substr(0, eqPos).trim();
885 | let value = line.substr(eqPos + 1).trim();
886 |
887 | ret.push(prop + ' = ' + value);
888 | });
889 |
890 | return ret;
891 | }
892 |
893 |
894 | function resetButtonDialog() {
895 | document.getElementById('command-select').value = 'a';
896 | document.getElementById('image-select').value = 'A.png';
897 |
898 | document.getElementById('command-name').value = 'a';
899 | document.getElementById('image-name').value = 'A.png';
900 | showImagePreview('A.png');
901 |
902 | document.getElementById('button-shape').value = 'radial';
903 |
904 | clearAdditionalPropsFields();
905 | }
906 |
907 |
908 | function showDialog(elementId, isShow) {
909 | let dialog = document.getElementById(elementId);
910 |
911 | if (!dialog)
912 | return;
913 |
914 | if (isShow) {
915 | dialog.classList.remove('hidden');
916 | } else {
917 | dialog.classList.add('hidden');
918 | return;
919 | }
920 |
921 | let focusCandidates = document.querySelectorAll('#' + elementId + ' .js-dialog__focus');
922 | if (focusCandidates.length > 0)
923 | focusCandidates[0].focus();
924 | }
925 |
926 |
927 | function showImagePreview(imgName) {
928 | let image = images[imgName];
929 | let gradient = 'linear-gradient(90deg, rgba(0,0,0,0) 0%, rgba(0,0,0,0) 80%, #aac 90%)';
930 | let box = document.getElementById('image-name');
931 |
932 | if (image)
933 | box.style['background-image'] = 'url(' + image + '), ' + gradient;
934 | else
935 | box.style['background-image'] = 'none';
936 | }
937 |
938 |
939 | function generateOverlayName(isPortrait) {
940 | let prefix = isPortrait ? 'portrait' : 'landscape';
941 | let index = conf.getOverlayList().length + 1;
942 |
943 | while (conf.isOverlayNameExist(prefix + '-' + index)) {
944 | index++;
945 | }
946 |
947 | document.getElementById('overlay-name').value = prefix + '-' + index;
948 | }
949 |
950 |
951 | function calculateScreenSizeToFit(width, height) {
952 | let vw = window.innerWidth;
953 | let _width = Math.max(width, height);
954 | let _height = Math.min(width, height);
955 | let scale;
956 |
957 | if (vw < 600) {
958 | let coef = 0.85;
959 | let theight = vw * coef;
960 | scale = theight / _height;
961 | console.log('[RESCALE] viewport width ' + vw + 'px - screen height ' + theight + 'px (' + coef + ')');
962 | } else {
963 | let coef = vw <= 1280 ? 0.7 : 0.55;
964 | let twidth = vw * coef;
965 | scale = twidth / _width;
966 | console.log('[RESCALE] viewport width ' + vw + 'px - screen width ' + twidth + 'px (' + coef + ')');
967 | }
968 |
969 | let swidth = +(width * scale).toFixed(2);
970 | let sheight = +(height * scale).toFixed(2);
971 | console.log('scale factor ' + scale + ' (from ' + width + 'x' + height + ' to ' + swidth + 'x' + sheight + ')');
972 |
973 | return scale;
974 | }
975 |
976 |
977 | // Inline event listeners
978 |
979 | function resetPad() {
980 | showDialog('reset-dialog', false);
981 | renderConfig(configStr);
982 | }
983 |
984 |
985 | function toggleShapes(event) {
986 | let s = document.getElementById('screenpad');
987 |
988 | if (event.target.checked)
989 | s.classList.add('show-borders');
990 | else
991 | s.classList.remove('show-borders');
992 | }
993 |
994 |
995 | function toggleNames(event) {
996 | let s = document.getElementById('screenpad');
997 |
998 | if (event.target.checked)
999 | s.classList.remove('hide-names');
1000 | else
1001 | s.classList.add('hide-names');
1002 | }
1003 |
1004 |
1005 | function flipXcoord() {
1006 | let x = conf.flipXcoord()
1007 | document.getElementById('x-range').value = x;
1008 | document.getElementById('x-number').value = x;
1009 | updateCurrentLine();
1010 | syncSelectedButtons();
1011 | }
1012 |
1013 |
1014 | function normalizeHeight() {
1015 | let h = conf.normalizeHeight(screen.width, screen.height)
1016 | document.getElementById('h-range').value = h;
1017 | document.getElementById('h-number').value = h;
1018 | updateCurrentLine();
1019 | syncSelectedButtons();
1020 | }
1021 |
1022 |
1023 | function normalizeWidth() {
1024 | let w = conf.normalizeWidth(screen.width, screen.height)
1025 | document.getElementById('w-range').value = w;
1026 | document.getElementById('w-number').value = w;
1027 | updateCurrentLine();
1028 | syncSelectedButtons();
1029 | }
1030 |
1031 |
1032 | function fixAspect() {
1033 | let iw = document.getElementById('initial-aspect-width').value;
1034 | let ih = document.getElementById('initial-aspect-height').value;
1035 |
1036 | let ow = document.getElementById('target-display-width').value;
1037 | let oh = document.getElementById('target-display-height').value;
1038 |
1039 | let mode = document.getElementById('chk-keep-relative').checked;
1040 |
1041 | conf.fixAspect(iw, ih, ow, oh, screen.isPortrait, mode);
1042 |
1043 | hideAspectFixer();
1044 |
1045 | // Do not rescale if aspect ratio has been set instesd of target resolutoin
1046 | if (ow >= 96 && oh >= 64)
1047 | setScreenDimensions(ow, oh);
1048 |
1049 | deselectAll();
1050 | redrawPad();
1051 | }
1052 |
1053 |
1054 | function getButtonDataFromDialog() {
1055 | let d = {};
1056 |
1057 | d.command = document.getElementById('command-name').value.trim() || 'null';
1058 | if (d.command.search(/\s/) != -1) {
1059 | d.warn = true;
1060 | alert('Button command should not contain spaces');
1061 | }
1062 |
1063 | d.shape = ['rect', 'radial'][document.getElementById('button-shape').selectedIndex];
1064 | d.image = document.getElementById('image-name').value;
1065 |
1066 | d.lines = readAdditionalPropsFields();
1067 | console.log(d.lines);
1068 |
1069 | return d;
1070 | }
1071 |
1072 |
1073 | function addButton() {
1074 | let d = getButtonDataFromDialog();
1075 | if (d.warn)
1076 | return;
1077 |
1078 | hideButtonEditor();
1079 | conf.createButton(d.command, d.shape, d.image, d.lines);
1080 | redrawPad();
1081 | }
1082 |
1083 |
1084 | function editButton() {
1085 | let d = getButtonDataFromDialog();
1086 | if (d.warn)
1087 | return;
1088 |
1089 | hideButtonEditor();
1090 | conf.updateCurrentButton(d.command, d.shape, d.image, d.lines);
1091 | conf.setCurrentLine(-1);
1092 | redrawPad();
1093 | }
1094 |
1095 |
1096 | function addOverlay() {
1097 | let name = document.getElementById('overlay-name').value.trim();
1098 | let raw = document.getElementById('raw-overlay-properties').value;
1099 | let props = processRawProperties(raw);
1100 |
1101 | if (conf.isOverlayNameExist(name)) {
1102 | showDialog('name-exist-dialog', true);
1103 | return;
1104 | }
1105 |
1106 | if (name == '') {
1107 | showDialog('name-empty-dialog', true);
1108 | return;
1109 | }
1110 |
1111 | if (document.getElementById('chk-duplicate-overlay').checked)
1112 | conf.duplicateCurrentOverlay(name, props);
1113 | else
1114 | conf.createOverlay(name, props);
1115 |
1116 | hideOverlayEditor();
1117 | buildAndSetOverlaySelectors(1000);
1118 | setScreenDimensions();
1119 | redrawPad();
1120 | }
1121 |
1122 |
1123 | function editOverlay() {
1124 | let name = document.getElementById('overlay-name').value.trim();
1125 | let raw = document.getElementById('raw-overlay-properties').value;
1126 |
1127 | if (conf.getCurrentOverlayName() != name && conf.isOverlayNameExist(name)) {
1128 | showDialog('name-exist-dialog', true);
1129 | return;
1130 | }
1131 |
1132 | if (name == '') {
1133 | showDialog('name-empty-dialog', true);
1134 | return;
1135 | }
1136 |
1137 | conf.editCurrentOverlay(name, processRawProperties(raw));
1138 |
1139 | hideOverlayEditor();
1140 | buildAndSetOverlaySelectors(conf.getCurrentOverlay());
1141 | setScreenDimensions();
1142 | redrawPad();
1143 | }
1144 |
1145 |
1146 | function delCurrentButton() {
1147 | showDialog('button-delete-dialog', false);
1148 |
1149 | if (!conf.deleteCurrentButton())
1150 | alert('No selection!');
1151 | redrawPad();
1152 | }
1153 |
1154 |
1155 | function delCurrentOverlay() {
1156 | showDialog('overlay-delete-dialog', false);
1157 |
1158 | conf.deleteCurrentOverlay();
1159 | buildAndSetOverlaySelectors(0);
1160 | setScreenDimensions();
1161 | redrawPad();
1162 | }
1163 |
1164 |
1165 | function showButtonEditor() {
1166 | let values = conf.getCurrentButtonParams();
1167 |
1168 | fillButtonEditor(values.command, values.shape, values.image, values.addLines.join('\n'));
1169 |
1170 | document.getElementById('button-create-button').classList.add('hidden');
1171 | document.getElementById('button-edit-button').classList.remove('hidden');
1172 | showDialog('button-create-dialog', true);
1173 | }
1174 |
1175 |
1176 | function showButtonCreator() {
1177 | resetButtonDialog();
1178 | document.getElementById('button-create-button').classList.remove('hidden');
1179 | document.getElementById('button-edit-button').classList.add('hidden');
1180 | showDialog('button-create-dialog', true);
1181 | }
1182 |
1183 |
1184 | function hideButtonEditor() {
1185 | showDialog('button-create-dialog', false);
1186 | }
1187 |
1188 |
1189 | function showOverlayEditor() {
1190 | updateNewOverlayFields();
1191 | showDialog('overlay-create-dialog', true);
1192 | }
1193 |
1194 |
1195 | function hideOverlayEditor() {
1196 | showDialog('overlay-create-dialog', false);
1197 | }
1198 |
1199 |
1200 | function showAspectFixer() {
1201 | let aspect = conf.getOverlayAspectRatio();
1202 | if (aspect) {
1203 | document.getElementById('initial-aspect-width').value = aspect.w;
1204 | document.getElementById('initial-aspect-height').value = aspect.h;
1205 | } else {
1206 | document.getElementById('initial-aspect-width').value = screen.isPortrait ? 9 : 16;
1207 | document.getElementById('initial-aspect-height').value = screen.isPortrait ? 16 : 9;
1208 | }
1209 |
1210 | let hint = document.getElementById('aspect-hint');
1211 | if (screen.isPortrait)
1212 | hint.classList.remove('hidden');
1213 | else
1214 | hint.classList.add('hidden');
1215 |
1216 | document.getElementById('target-display-width').value = screen.enteredWidth;
1217 | document.getElementById('target-display-height').value = screen.enteredHeight;
1218 | showDialog('aspect-fixer-dialog', true);
1219 | }
1220 |
1221 |
1222 | function hideAspectFixer() {
1223 | showDialog('aspect-fixer-dialog', false);
1224 | }
1225 |
1226 |
1227 | function showScreenSizeDialog() {
1228 | document.getElementById('display-width').value = screen.longSide;
1229 | document.getElementById('display-height').value = screen.shortSide;
1230 |
1231 | document.getElementById('screenshot-width').value = screen.shotFrameWidth;
1232 | document.getElementById('screenshot-height').value = screen.shotFrameHeight;
1233 |
1234 | document.getElementById('radio-screenshot-' + screen.shotMode).checked = true;
1235 |
1236 | document.getElementById('chk-rescale-to-fit').checked = screen.scale != 1;
1237 |
1238 | let screenshotMatch = document.getElementById('radio-screenshot-match');
1239 | screenshotMatch.disabled = (!screen.shotImage || !screen.shotShow);
1240 |
1241 | onScreenshotModeChange();
1242 |
1243 | showDialog('screen-size-dialog', true);
1244 | }
1245 |
1246 |
1247 | function onScreenshotModeChange() {
1248 | let screenshotWidth = document.getElementById('screenshot-width');
1249 | let screenshotHeight = document.getElementById('screenshot-height');
1250 |
1251 | let screenshotFit = document.getElementById('radio-screenshot-fit');
1252 | let screenshotMatch = document.getElementById('radio-screenshot-match');
1253 |
1254 | let disableSizeSet = (screen.shotImage && screen.shotShow) && (screenshotFit.checked || screenshotMatch.checked);
1255 | screenshotWidth.disabled = disableSizeSet;
1256 | screenshotHeight.disabled = disableSizeSet;
1257 | }
1258 |
1259 |
1260 | function hideScreenSizeDialog() {
1261 | showDialog('screen-size-dialog', false);
1262 | }
1263 |
1264 |
1265 | function showFileDialog() {
1266 | document.getElementById('chk-show-screenshot').disabled = !screen.shotImage;
1267 | showDialog('import-export-dialog', true);
1268 | }
1269 |
1270 |
1271 | function hideFileDialog() {
1272 | showDialog('import-export-dialog', false);
1273 | }
1274 |
1275 |
1276 | function fillImageNameField(event) {
1277 | document.getElementById('image-name').value = event.target.value;
1278 | showImagePreview(event.target.value);
1279 | }
1280 |
1281 |
1282 | function fillCommandField(event) {
1283 | let command = event.target.value;
1284 |
1285 | clearAdditionalPropsFields();
1286 |
1287 | document.getElementById('command-name').value = command;
1288 | let lines = showAdditionalParametersForCommand(command);
1289 | if (lines) {
1290 | fillAdditionalPropsFields(lines.split('\n'));
1291 | toggleAdditionalButtonProperties(true);
1292 | }
1293 | }
1294 |
1295 |
1296 | function toggleOrientation(event) {
1297 | screen.isPortrait = event.target.checked;
1298 | setScreenDimensions();
1299 | redrawPad();
1300 | }
1301 |
1302 |
1303 | function toggleScreenshot(event) {
1304 | screen.shotShow = event.target.checked;
1305 | setScreenDimensions();
1306 | refreshScreenshot();
1307 | redrawPad();
1308 | }
1309 |
1310 |
1311 | function toggleOffscreen(event) {
1312 | let screenDiv = document.getElementById('screenpad');
1313 |
1314 | if (event.target.checked) {
1315 | screenDiv.classList.add('show-offscreen');
1316 | screenDiv.classList.remove('hide-offscreen');
1317 | } else {
1318 | screenDiv.classList.remove('show-offscreen');
1319 | screenDiv.classList.add('hide-offscreen');
1320 | }
1321 | }
1322 |
1323 |
1324 | function toggleAdditionalButtonProperties(show) {
1325 | let adds = document.getElementsByClassName('js-additional-button-property');
1326 | let addBtn = document.getElementById('btn-additional-button');
1327 |
1328 | if (show || adds[0].classList.contains('hidden')) {
1329 | addBtn.classList.add('expanded');
1330 | for (let i = 0; i < adds.length; i++)
1331 | adds[i].classList.remove('hidden');
1332 | } else {
1333 | addBtn.classList.remove('expanded');
1334 | for (let i = 0; i < adds.length; i++)
1335 | adds[i].classList.add('hidden');
1336 | }
1337 | }
1338 |
1339 |
1340 | function toggleAdditionalOverlayProperties(show) {
1341 | let add = document.getElementById('overlay-properties-container');
1342 | let addBtn = document.getElementById('overlay-additional-button');
1343 |
1344 | if (show || add.classList.contains('hidden')) {
1345 | add.classList.remove('hidden');
1346 | addBtn.classList.add('expanded');
1347 | } else {
1348 | add.classList.add('hidden');
1349 | addBtn.classList.remove('expanded');
1350 | }
1351 | }
1352 |
1353 |
1354 | function toggleScreenshotSettings() {
1355 | let settings = document.getElementById('screenshot-area-settings');
1356 | let expander = document.getElementById('screenshot-settings-expander')
1357 |
1358 | if (settings.classList.contains('hidden')) {
1359 | settings.classList.remove('hidden');
1360 | expander.classList.add('expanded');
1361 | } else {
1362 | settings.classList.add('hidden');
1363 | expander.classList.remove('expanded');
1364 | }
1365 | }
1366 |
1367 |
1368 | function updateNewOverlayFields() {
1369 | let box = document.getElementById('raw-overlay-properties');
1370 | let duplicateChk = document.getElementById('chk-duplicate-overlay');
1371 | let portraitChk = document.getElementById('chk-portrait-overlay');
1372 | let editChk = document.getElementById('chk-edit-overlay');
1373 |
1374 | let isDuplicate = duplicateChk.checked;
1375 | let isPortrait = portraitChk.checked;
1376 | let isEdit = editChk.checked;
1377 |
1378 | if (isEdit)
1379 | toggleAdditionalOverlayProperties(true);
1380 |
1381 | let aspect = screen.longSide / screen.shortSide;
1382 |
1383 | let createBtn = document.getElementById('overlay-create-button');
1384 | let editBtn = document.getElementById('overlay-edit-button');
1385 | if (isEdit) {
1386 | editBtn.classList.remove('hidden')
1387 | createBtn.classList.add('hidden');
1388 | duplicateChk.disabled = true;
1389 | duplicateChk.checked = false;
1390 | document.getElementById('overlay-name').value = conf.getCurrentOverlayName();
1391 | _fillCurrentOverlay();
1392 | return;
1393 | } else {
1394 | editBtn.classList.add('hidden')
1395 | createBtn.classList.remove('hidden');
1396 | duplicateChk.disabled = false;
1397 | }
1398 |
1399 | if (isDuplicate) {
1400 | _fillCurrentOverlay();
1401 | } else {
1402 | portraitChk.disabled = false;
1403 | let ratio = 'aspect_ratio = ' + +(isPortrait ? 1 / aspect : aspect).toFixed(7);
1404 | box.value = defaultParamsForNewOverlay + '\n' + ratio;
1405 | box.value += '\n' + autoScaleParams + 'auto_y_separation = ' + (isPortrait ? 'false' : 'true');
1406 | box.value += '\n' + manualScaleParams;
1407 | }
1408 |
1409 | generateOverlayName(isPortrait);
1410 |
1411 |
1412 | function _fillCurrentOverlay() {
1413 | box.value = conf.getCurrentOverlayParams().join('\n');
1414 | isPortrait = document.getElementById('overlay-selector').value.search('portrait') != -1;
1415 | portraitChk.checked = isPortrait;
1416 | portraitChk.disabled = true;
1417 | }
1418 | }
1419 |
1420 |
1421 | function selectOverlay(event) {
1422 | conf.setCurrentOverlay(event.target.selectedIndex);
1423 | conf.setCurrentLine(-1);
1424 |
1425 | if (event.target.value.search('portrait') != -1)
1426 | screen.isPortrait = true;
1427 |
1428 | if (event.target.value.search('landscape') != -1)
1429 | screen.isPortrait = false;
1430 |
1431 | document.getElementById('chk-show-portrait').checked = screen.isPortrait;
1432 |
1433 | setScreenDimensions();
1434 | redrawPad();
1435 | }
1436 |
1437 |
1438 | function showColorsDialog() {
1439 | showDialog('colors-dialog', true);
1440 | }
1441 |
1442 |
1443 | function setColorScheme(index) {
1444 | let screenpad = document.getElementById('screenpad');
1445 | screenpad.classList.remove('scheme-1');
1446 | screenpad.classList.remove('scheme-2');
1447 |
1448 | if (index > 0)
1449 | screenpad.classList.add('scheme-' + index);
1450 | }
--------------------------------------------------------------------------------
/js/file-input-helper.js:
--------------------------------------------------------------------------------
1 | {
2 | let fileInputs = document.querySelectorAll('input[type=file]');
3 | fileInputs.forEach(elem => {
4 | let fspan = document.getElementById(elem.id + '-filename');
5 |
6 | if (fspan)
7 | elem.addEventListener('change', event => {
8 | let name = '';
9 | switch (event.target.files.length) {
10 | case 0:
11 | name = "Browse";
12 | break;
13 |
14 | case 1:
15 | name = event.target.files[0].name;
16 | break;
17 |
18 | default:
19 | name = event.target.files.length + ' files';
20 | }
21 | fspan.innerHTML = name;
22 |
23 | });
24 | });
25 | }
26 |
--------------------------------------------------------------------------------