├── 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 | 13 | 15 | 21 | 26 | 27 | 33 | 38 | 39 | 45 | 50 | 51 | 57 | 62 | 63 | 69 | 74 | 75 | 81 | 86 | 87 | 88 | 90 | 91 | 93 | image/svg+xml 94 | 96 | 97 | 98 | 99 | 100 | 107 | 113 | 119 | 125 | 131 | 135 | 142 | 149 | 156 | 163 | 170 | 174 | 181 | 188 | 194 | 200 | 206 | 212 | 216 | 223 | 230 | 234 | 241 | 247 | 253 | 259 | 265 | 269 | 273 | 277 | 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 |
32 | 33 |
34 | TOOLS 35 |
36 | 37 | 38 |
39 |
40 |
41 |
42 | 43 | 44 |
45 | 46 |
47 | 48 | 49 |
50 | 51 |
52 | 53 | 54 |
55 | 56 |
57 | 58 | 59 |
60 |
61 |
62 | 63 |
64 | OVERLAY 65 | 66 |
67 | 68 | 69 | 70 |
71 |
72 | 73 |
74 | BUTTON 75 |
76 | 77 | 79 | 81 |
82 |
83 |
84 | 85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 | 94 |
95 |
96 | X 97 | 98 | 99 | 100 |
101 | 102 |
103 | Y 104 | 105 | 106 |
107 | 108 |
109 | W 110 | 111 | 112 | 113 |
114 | 115 |
116 | H 117 | 118 | 119 | 120 |
121 |
122 | 123 |
124 |
125 | 126 | 133 | 134 | 135 | 136 | 264 | 265 | 266 | 306 | 307 | 308 | 345 | 346 | 347 | 393 | 394 | 395 | 468 | 469 | 501 | 502 | 513 | 514 | 525 | 526 | 537 | 538 | 548 | 549 | 559 | 560 | 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 | --------------------------------------------------------------------------------