1import QtQuick 1.1 2import Qt.labs.shaders 1.0 3import "ToxicWaste.js" as ToxicWaste 4import "./animations" 5import "./components" 6import "./effects" 7import Wheel 1.0 8 9Rectangle { 10 id: toxicWasteMain 11 12 property int fps: 0 13 property bool ignoreLaunch: false 14 property bool invertFlip: false 15 property bool horizontalFlip: true 16 property bool flipDirectionChanged: false 17 property bool iconsReady: false 18 property bool icoSevenZipped: viewer.isSevenZippedImageType("ico") && !viewer.iconCacheDatabaseEnabled() 19 property int currentMachineIndex: 0 20 21 // restored properties 22 property bool fpsVisible: false 23 property bool showBackgroundAnimation: false 24 property bool showShaderEffect: false 25 property bool animateInForeground: false 26 property bool fullScreen: false 27 property string cabinetImageType: "preview" 28 property string secondaryImageType: "preview" 29 property bool cabinetFlipped: false 30 property int lastIndex: 0 31 property bool menuHidden: false 32 property string version: "" 33 property bool confirmQuit: true 34 property int preferencesTab: 0 35 property int machineCardPage: 0 36 property bool autoPositionOverlay: true 37 property real overlayScale: 1.0 38 property real overlayOffsetX: 0 39 property real overlayOffsetY: 0 40 property real overlayOpacity: 1 41 property real backgroundOpacity: 0.7 42 property real machineListOpacity: 1 43 property bool autoStopAnimations: true 44 45 // delayed init 46 Timer { 47 id: initTimer 48 interval: 1 49 running: false 50 repeat: false 51 onTriggered: { 52 viewer.log("ToxicWaste: " + qsTr("Starting initialization")); 53 ToxicWaste.init(); 54 restoreLastIndexTimer.start(); 55 } 56 } 57 58 Timer { 59 id: restoreLastIndexTimer 60 interval: 1 61 running: false 62 repeat: false 63 onTriggered: ToxicWaste.restoreLastIndex() 64 } 65 66 Component.onCompleted: initTimer.start() 67 68 Timer { 69 id: launchButtonFlashTimer 70 interval: 100 71 running: false 72 onTriggered: launchButton.opacity = 0.8 73 } 74 75 Timer { 76 id: resetIgnoreLaunchTimer 77 interval: 100 78 running: false 79 repeat: false 80 onTriggered: toxicWasteMain.ignoreLaunch = false 81 } 82 83 Timer { 84 id: flipDirectionResetTimer 85 interval: 200 86 running: false 87 repeat: false 88 onTriggered: toxicWasteMain.flipDirectionChanged = false 89 } 90 91 Timer { 92 id: detailUpdateTimer 93 interval: 10 94 running: false 95 repeat: false 96 onTriggered: toxicWasteMain.currentMachineIndex = machineListView.currentIndex; 97 } 98 99 Connections { 100 target: viewer 101 onEmulatorStarted: ToxicWaste.emulatorStarted() 102 onEmulatorFinished: ToxicWaste.emulatorStopped() 103 } 104 105 width: ToxicWaste.baseWidth 106 height: ToxicWaste.baseHeight 107 z: 0 108 Image { 109 id: backgroundImage 110 anchors.fill: parent 111 fillMode: Image.PreserveAspectFit 112 source: "images/shadereffectsource.png" 113 smooth: true 114 z: 0 115 } 116 Image { 117 id: toxicImage 118 anchors.fill: parent 119 fillMode: Image.PreserveAspectFit 120 source: "images/toxic.png" 121 z: 2 122 smooth: true 123 opacity: toxicWasteMain.backgroundOpacity 124 } 125 Item { 126 id: overlayItem 127 z: 3 128 anchors.top: parent.top 129 anchors.topMargin: 10 * ToxicWaste.scaleFactorY() 130 anchors.bottom: parent.bottom 131 anchors.bottomMargin: 10 * ToxicWaste.scaleFactorY() 132 anchors.left: parent.left 133 anchors.leftMargin: 20 * ToxicWaste.scaleFactorX() 134 anchors.right: parent.right 135 anchors.rightMargin: 320 * ToxicWaste.scaleFactorX() 136 Flipable { 137 id: overlayFlip 138 anchors.fill: parent 139 front: Item { 140 id: frontItem 141 anchors.fill: parent 142 scale: toxicWasteMain.overlayScale 143 property bool shiftPressed: false 144 property bool altPressed: false 145 property bool ctrlPressed: false 146 Image { 147 id: overlayImage 148 source: "images/overlay.png" 149 opacity: toxicWasteMain.overlayOpacity 150 fillMode: Image.PreserveAspectFit 151 width: 380 152 scale: ToxicWaste.scaleFactorX() 153 anchors.centerIn: parent 154 anchors.verticalCenterOffset: toxicWasteMain.autoPositionOverlay ? ToxicWaste.overlayOffset(height) : toxicWasteMain.overlayOffsetY 155 anchors.horizontalCenterOffset: toxicWasteMain.autoPositionOverlay ? 0 : (toxicWasteMain.overlayOffsetX + toxicWasteMain.mapToItem(parent, toxicWasteMain.width/2, 0).x - parent.width/2) * toxicWasteMain.overlayScale / toxicWasteMain.overlayScale // <= this is funny, I know, but seems to be the only way to trigger a recalculation when overlayScale changes 156 smooth: true 157 } 158 Rectangle { 159 id: previewRect 160 width: 239 161 height: 165 162 opacity: toxicWasteMain.overlayOpacity 163 anchors.centerIn: overlayImage 164 anchors.verticalCenterOffset: -124 * ToxicWaste.scaleFactorX() 165 scale: overlayImage.scale 166 color: "#202020" 167 smooth: true 168 z: -1 169 Image { 170 id: previewImage 171 cache: false 172 source: ToxicWaste.imageUrl(toxicWasteMain.cabinetImageType) 173 smooth: true 174 anchors.fill: parent 175 anchors.centerIn: parent 176 anchors.margins: 1 177 fillMode: Image.PreserveAspectFit 178 asynchronous: viewer.isSevenZippedImageType(toxicWasteMain.cabinetImageType) ? false : true 179 Connections { 180 target: viewer 181 onImageDataUpdated: { 182 if ( cachePrefix === ToxicWaste.cachePrefix(toxicWasteMain.cabinetImageType) ) 183 previewImage.cache = true; 184 } 185 } 186 onStatusChanged: { 187 if ( status == Image.Ready ) 188 previewOverlay.source = source 189 } 190 } 191 Image { 192 id: previewOverlay 193 cache: previewImage.cache 194 smooth: true 195 anchors.fill: parent 196 anchors.centerIn: parent 197 anchors.margins: 1 198 fillMode: Image.PreserveAspectFit 199 opacity: videoSnap.playing ? 0 : 1 200 asynchronous: previewImage.asynchronous 201 } 202 } 203 WheelArea { 204 anchors.fill: overlayImage 205 onWheel: { 206 var wheelEventHandled = false; 207 if ( !toxicWasteMain.autoPositionOverlay ) { 208 frontItem.focus = true; 209 if ( frontItem.altPressed ) { 210 wheelEventHandled = true; 211 toxicWasteMain.overlayOffsetX += (frontItem.shiftPressed ? 0.05 : 0.25) * delta 212 } 213 if ( frontItem.ctrlPressed ) { 214 wheelEventHandled = true; 215 toxicWasteMain.overlayOffsetY += (frontItem.shiftPressed ? 0.05 : 0.25) * delta 216 } 217 } 218 if ( !wheelEventHandled ) { 219 var newScale = toxicWasteMain.overlayScale * (1 + (frontItem.shiftPressed ? 0.01 : 0.1) * (delta / Math.abs(delta))); 220 if ( newScale > 10.0 ) 221 newScale = 10.0; 222 else if ( newScale < 0.01 ) 223 newScale = 0.01; 224 toxicWasteMain.overlayScale = newScale; 225 preferencesSlidersTab.cabinetZoomValue = toxicWasteMain.overlayScale; 226 } 227 } 228 } 229 focus: true 230 Keys.onPressed: { 231 frontItem.shiftPressed = event.modifiers & Qt.ShiftModifier 232 frontItem.altPressed = event.modifiers & Qt.AltModifier 233 frontItem.ctrlPressed = event.modifiers & Qt.ControlModifier 234 } 235 Keys.onReleased: { 236 frontItem.shiftPressed = event.modifiers & Qt.ShiftModifier 237 frontItem.altPressed = event.modifiers & Qt.AltModifier 238 frontItem.ctrlPressed = event.modifiers & Qt.ControlModifier 239 } 240 } 241 back: Rectangle { 242 id: overlayRectBack 243 anchors.fill: parent 244 border.color: "black" 245 border.width: 2 * ToxicWaste.scaleFactorX() 246 radius: 5 247 smooth: true 248 scale: toxicWasteMain.flipDirectionChanged ? -1 : 1 249 gradient: Gradient { 250 GradientStop { position: 0.00; color: "#ffffff" } 251 GradientStop { position: 0.50; color: "#007bff" } 252 } 253 Text { 254 id: itemDescription 255 text: ToxicWaste.machineCardHeader() 256 font.pixelSize: 12 * ToxicWaste.scaleFactorY() 257 anchors.top: parent.top 258 anchors.topMargin: 10 * ToxicWaste.scaleFactorX() 259 anchors.horizontalCenter: parent.horizontalCenter 260 width: parent.width - 76 * ToxicWaste.scaleFactorX() 261 horizontalAlignment: Text.AlignHCenter 262 verticalAlignment: Text.AlignVCenter 263 wrapMode: Text.WordWrap 264 } 265 TabWidget { 266 id: machineCardTabWidget 267 anchors.fill: parent 268 anchors.leftMargin: 10 * ToxicWaste.scaleFactorX() 269 anchors.rightMargin: anchors.leftMargin 270 anchors.bottom: parent.bottom 271 anchors.topMargin: itemDescription.height + 25 * ToxicWaste.scaleFactorY() 272 anchors.horizontalCenter: parent.horizontalCenter 273 current: toxicWasteMain.machineCardPage 274 onCurrentChanged: toxicWasteMain.machineCardPage = current 275 baseColor: "#55a5ff" 276 activeBorderColor: "black" 277 inactiveBorderColor: "#202020" 278 activeTextColor: "black" 279 inactiveTextColor: "#202020" 280 fontSize: 12 * ToxicWaste.scaleFactorX() 281 smooth: true 282 scaleFactor: ToxicWaste.scaleFactorX() 283 Rectangle { 284 id: imageViewerRect 285 property string title: qsTr("Images") 286 anchors.fill: parent 287 anchors.topMargin: 5 * ToxicWaste.scaleFactorY() 288 anchors.bottomMargin: 30 * ToxicWaste.scaleFactorY() 289 smooth: true 290 border.color: "black" 291 border.width: ToxicWaste.scaleFactorX() 292 radius: 5 293 color: "#202020" 294 Image { 295 id: imageViewer 296 cache: false 297 source: ToxicWaste.imageUrl(toxicWasteMain.secondaryImageType) 298 smooth: true 299 anchors.fill: parent 300 anchors.centerIn: parent 301 anchors.margins: 2 302 fillMode: Image.PreserveAspectFit 303 asynchronous: viewer.isSevenZippedImageType(toxicWasteMain.secondaryImageType) ? false : true 304 Connections { 305 target: viewer 306 onImageDataUpdated: { 307 if ( cachePrefix === ToxicWaste.cachePrefix(toxicWasteMain.secondaryImageType) ) 308 imageViewer.cache = true; 309 } 310 } 311 onStatusChanged: { 312 if ( status == Image.Ready ) 313 imageViewerOverlay.source = source 314 } 315 } 316 Image { 317 id: imageViewerOverlay 318 cache: imageViewer.cache 319 smooth: true 320 anchors.fill: parent 321 anchors.centerIn: parent 322 anchors.margins: 1 323 fillMode: Image.PreserveAspectFit 324 asynchronous: imageViewer.asynchronous 325 } 326 Rectangle { 327 id: imageTypeSelector 328 gradient: Gradient { 329 GradientStop { position: 0.00; color: "black" } 330 GradientStop { position: 0.75; color: "white" } 331 GradientStop { position: 1.00; color: "black" } 332 } 333 radius: height/2 334 anchors.horizontalCenter: parent.horizontalCenter 335 anchors.bottom: parent.bottom 336 anchors.bottomMargin: -25 * ToxicWaste.scaleFactorY() 337 height: 20 * ToxicWaste.scaleFactorY() 338 width: 300 * ToxicWaste.scaleFactorX() 339 smooth: true 340 Text { 341 id: imageTypeText 342 text: ToxicWaste.machineImageType(toxicWasteMain.secondaryImageType) 343 color: "black" 344 font.bold: true 345 font.pixelSize: 12 * ToxicWaste.scaleFactorY() 346 anchors.fill: parent 347 anchors.centerIn: parent 348 anchors.leftMargin: nextImageButton.width 349 anchors.rightMargin: previousImageButton.width 350 horizontalAlignment: Text.AlignHCenter 351 verticalAlignment: Text.AlignVCenter 352 smooth: true 353 elide: Text.ElideRight 354 } 355 Image { 356 id: nextImageButton 357 opacity: 0.5 358 source: "images/arrow.png" 359 anchors.horizontalCenter: parent.horizontalCenter 360 anchors.horizontalCenterOffset: 150 * ToxicWaste.scaleFactorX() - width/2 361 anchors.verticalCenter: parent.verticalCenter 362 height: parent.height - 2 363 fillMode: Image.PreserveAspectFit 364 smooth: true 365 MouseArea { 366 anchors.fill: parent 367 hoverEnabled: true 368 onContainsMouseChanged: containsMouse ? parent.opacity = 1.0 : parent.opacity = 0.5 369 onClicked: { 370 toxicWasteMain.secondaryImageType = ToxicWaste.nextImageType(toxicWasteMain.secondaryImageType); 371 searchTextInput.focus = false; 372 if ( toxicWasteMain.cabinetImageType == toxicWasteMain.secondaryImageType ) 373 miniCabinetImage.opacity = 1.0; 374 else 375 miniCabinetImage.opacity = 0.3; 376 } 377 } 378 } 379 Image { 380 id: previousImageButton 381 opacity: 0.5 382 source: "images/arrow.png" 383 mirror: true 384 anchors.horizontalCenter: parent.horizontalCenter 385 anchors.horizontalCenterOffset: -150 * ToxicWaste.scaleFactorX() + width/2 386 anchors.verticalCenter: parent.verticalCenter 387 height: parent.height - 2 388 fillMode: Image.PreserveAspectFit 389 smooth: true 390 MouseArea { 391 anchors.fill: parent 392 hoverEnabled: true 393 onContainsMouseChanged: containsMouse ? parent.opacity = 1.0 : parent.opacity = 0.5 394 onClicked: { 395 toxicWasteMain.secondaryImageType = ToxicWaste.previousImageType(toxicWasteMain.secondaryImageType); 396 searchTextInput.focus = false; 397 if ( toxicWasteMain.cabinetImageType == toxicWasteMain.secondaryImageType ) 398 miniCabinetImage.opacity = 1.0; 399 else 400 miniCabinetImage.opacity = 0.3; 401 } 402 } 403 } 404 } 405 Image { 406 id: miniCabinetImage 407 source: "images/cabinet_small.png" 408 anchors.left: parent.left 409 anchors.verticalCenter: imageTypeSelector.verticalCenter 410 smooth: true 411 fillMode: Image.PreserveAspectFit 412 height: imageTypeSelector.height 413 opacity: toxicWasteMain.cabinetImageType == toxicWasteMain.secondaryImageType ? 1.0 : 0.3 414 MouseArea { 415 anchors.fill: parent 416 hoverEnabled: true 417 onEntered: miniCabinetImage.opacity = 1.0 418 onExited: { 419 if ( toxicWasteMain.cabinetImageType == toxicWasteMain.secondaryImageType ) 420 miniCabinetImage.opacity = 1.0; 421 else 422 miniCabinetImage.opacity = 0.3; 423 } 424 onClicked: toxicWasteMain.cabinetImageType = toxicWasteMain.secondaryImageType 425 } 426 } 427 } 428 Rectangle { 429 id: emuInfoViewer 430 property string title: qsTr("Emu info") 431 anchors.fill: parent 432 anchors.topMargin: 5 * ToxicWaste.scaleFactorY() 433 anchors.bottomMargin: 10 * ToxicWaste.scaleFactorY() 434 smooth: true 435 border.color: "black" 436 border.width: ToxicWaste.scaleFactorX() 437 radius: 5 438 color: "#202020" 439 TextArea { 440 anchors.fill: parent 441 anchors.margins: 5 442 color: "transparent" 443 fontSize: 12 * ToxicWaste.scaleFactorX() 444 fontColor: "white" 445 arrowIcon: "images/down_arrow_white.png" 446 displayText: viewer.requestInfo(machineListModel[toxicWasteMain.currentMachineIndex].id, "emuinfo"); 447 } 448 } 449 Rectangle { 450 id: machineInfoViewer 451 property string title: qsTr("Machine info") 452 anchors.fill: parent 453 anchors.topMargin: 5 * ToxicWaste.scaleFactorY() 454 anchors.bottomMargin: 10 * ToxicWaste.scaleFactorY() 455 smooth: true 456 border.color: "black" 457 border.width: ToxicWaste.scaleFactorX() 458 radius: 5 459 color: "#202020" 460 TextArea { 461 anchors.fill: parent 462 anchors.margins: 5 463 color: "transparent" 464 fontSize: 12 * ToxicWaste.scaleFactorX() 465 fontColor: "white" 466 arrowIcon: "images/down_arrow_white.png" 467 displayText: viewer.requestInfo(machineListModel[toxicWasteMain.currentMachineIndex].id, "sysinfo"); 468 } 469 } 470 } 471 } 472 transform: Rotation { 473 id: overlayRotation 474 origin.x: overlayFlip.width/2 475 origin.y: overlayFlip.height/2 476 axis.x: toxicWasteMain.horizontalFlip ? 0 : (toxicWasteMain.invertFlip ? -1 : 1) 477 axis.y: toxicWasteMain.horizontalFlip ? (toxicWasteMain.invertFlip ? -1 : 1) : 0 478 axis.z: 0 479 angle: 0 480 } 481 states: [ 482 State { 483 name: "back" 484 PropertyChanges { target: overlayRotation; angle: 180 } 485 PropertyChanges { target: toxicWasteMain; focus: true } 486 when: toxicWasteMain.cabinetFlipped 487 }, 488 State { 489 name: "front" 490 PropertyChanges { target: overlayRotation; angle: 0 } 491 PropertyChanges { target: toxicWasteMain; focus: true } 492 when: !toxicWasteMain.cabinetFlipped 493 } 494 ] 495 transitions: Transition { 496 NumberAnimation { target: overlayRotation; property: "angle"; duration: 400 } 497 } 498 MouseArea { // right 499 anchors.fill: parent 500 anchors.leftMargin: parent.width/2 501 anchors.bottomMargin: parent.height/4 502 anchors.topMargin: parent.height/4 503 onClicked: { 504 toxicWasteMain.flipDirectionChanged = !toxicWasteMain.horizontalFlip; 505 toxicWasteMain.invertFlip = flipDirectionChanged ? toxicWasteMain.cabinetFlipped : false; 506 toxicWasteMain.horizontalFlip = true; 507 toxicWasteMain.focus = true; 508 toxicWasteMain.cabinetFlipped = !toxicWasteMain.cabinetFlipped; 509 flipDirectionResetTimer.restart(); 510 } 511 z: -1 512 } 513 MouseArea { // left 514 anchors.fill: parent 515 anchors.rightMargin: parent.width/2 516 anchors.bottomMargin: parent.height/4 517 anchors.topMargin: parent.height/4 518 onClicked: { 519 toxicWasteMain.flipDirectionChanged = !toxicWasteMain.horizontalFlip; 520 toxicWasteMain.invertFlip = flipDirectionChanged ? !toxicWasteMain.cabinetFlipped : true; 521 toxicWasteMain.horizontalFlip = true; 522 toxicWasteMain.focus = true; 523 toxicWasteMain.cabinetFlipped = !toxicWasteMain.cabinetFlipped; 524 flipDirectionResetTimer.restart(); 525 } 526 z: -1 527 } 528 MouseArea { // top 529 anchors.fill: parent 530 anchors.bottomMargin: parent.height - parent.height/4 531 onClicked: { 532 toxicWasteMain.flipDirectionChanged = toxicWasteMain.horizontalFlip; 533 toxicWasteMain.invertFlip = flipDirectionChanged ? toxicWasteMain.cabinetFlipped : false; 534 toxicWasteMain.horizontalFlip = false; 535 toxicWasteMain.focus = true; 536 toxicWasteMain.cabinetFlipped = !toxicWasteMain.cabinetFlipped; 537 flipDirectionResetTimer.restart(); 538 } 539 z: -1 540 } 541 MouseArea { // bottom 542 anchors.fill: parent 543 anchors.topMargin: parent.height - parent.height/4 544 onClicked: { 545 toxicWasteMain.flipDirectionChanged = toxicWasteMain.horizontalFlip; 546 toxicWasteMain.invertFlip = flipDirectionChanged ? !toxicWasteMain.cabinetFlipped : true; 547 toxicWasteMain.horizontalFlip = false; 548 toxicWasteMain.focus = true; 549 toxicWasteMain.cabinetFlipped = !toxicWasteMain.cabinetFlipped; 550 flipDirectionResetTimer.restart(); 551 } 552 z: -1 553 } 554 } 555 } 556 Rectangle { 557 anchors.verticalCenter: parent.verticalCenter 558 anchors.verticalCenterOffset: 29 * ToxicWaste.scaleFactorX() - toxicWasteMain.height/2 559 anchors.horizontalCenter: parent.horizontalCenter 560 anchors.horizontalCenterOffset: 29 * ToxicWaste.scaleFactorX() - toxicWasteMain.width/2 561 scale: ToxicWaste.scaleFactorX() 562 width: 48 563 height: 48 564 z: 4 565 color: "darkgrey" 566 radius: 26 567 smooth: true 568 Image { 569 id: launchButton 570 source: ToxicWaste.launchButtonSource() 571 opacity: 0.8 572 smooth: true 573 anchors.fill: parent 574 fillMode: Image.PreserveAspectFit 575 z: 4 576 MouseArea { 577 anchors.fill: parent 578 hoverEnabled: true 579 onEntered: parent.opacity = 1.0 580 onExited: parent.opacity = 0.8 581 onClicked: { 582 viewer.launchEmulator(machineListModel[machineListView.currentIndex].id); 583 searchTextInput.focus = false; 584 } 585 } 586 } 587 } 588 ListView { 589 id: machineListView 590 opacity: toxicWasteMain.machineListOpacity 591 scale: ToxicWaste.scaleFactorX() 592 flickDeceleration: 3500 593 maximumFlickVelocity: 4000 594 snapMode: ListView.NoSnap 595 interactive: true 596 keyNavigationWraps: false 597 z: 3 598 width: 280 599 height: parent.height / scale - 20 600 anchors.horizontalCenterOffset: 240 * scale 601 anchors.horizontalCenter: parent.horizontalCenter 602 anchors.verticalCenter: parent.verticalCenter 603 spacing: 10 604 orientation: ListView.Vertical 605 flickableDirection: Flickable.AutoFlickDirection 606 smooth: true 607 preferredHighlightBegin: 0 608 preferredHighlightEnd: 72 609 highlight: Rectangle { 610 id: itemHighlighter 611 smooth: true 612 color: "white" 613 radius: 10 614 border.color: "black" 615 border.width: 2 616 width: 280 617 height: 72 618 opacity: 1.0 619 z: 0 620 } 621 highlightRangeMode: ListView.StrictlyEnforceRange 622 highlightFollowsCurrentItem: true 623 highlightMoveDuration: 500 624 cacheBuffer: 72 * itemsPerPage() // 72 = machineListItemDelegate.height 625 delegate: Item { 626 property string gameId: id 627 id: machineListItemDelegate 628 width: 280 629 height: 72 630 Rectangle { 631 id: machineListItemBackground 632 smooth: true 633 anchors.fill: machineListItemDelegate 634 gradient: Gradient { 635 GradientStop { position: 0.0; color: "lightgrey" } 636 GradientStop { position: 0.5; color: "white" } 637 GradientStop { position: 1.0; color: "lightgrey" } 638 } 639 opacity: 0.8 640 radius: 10 641 border.color: "black" 642 border.width: 2 643 Image { 644 id: machineListItemIcon 645 cache: icoSevenZipped ? toxicWasteMain.iconsReady : true 646 source: "image://qmc2/ico/" + model.modelData.id + "/" + model.modelData.parentId 647 anchors.left: machineListItemBackground.left 648 anchors.verticalCenter: machineListItemBackground.verticalCenter 649 anchors.margins: 10 650 smooth: true 651 fillMode: Image.PreserveAspectFit 652 height: 24 653 asynchronous: !icoSevenZipped 654 Connections { 655 target: viewer 656 onImageDataUpdated: { 657 if ( cachePrefix === "ico" ) 658 toxicWasteMain.iconsReady = true; 659 } 660 } 661 } 662 Text { 663 property bool fontResized: false 664 id: machineListItemText 665 text: model.modelData.description 666 color: "black" 667 font.bold: true 668 font.italic: true 669 font.pixelSize: machineListItemBackground.height / 3 670 elide: Text.ElideRight 671 wrapMode: Text.NoWrap 672 anchors.centerIn: machineListItemBackground 673 anchors.horizontalCenterOffset: machineListItemIcon.width > 1 ? (paintedWidth + machineListItemIcon.width + 45 < machineListItemBackground.width ? 0 : machineListItemIcon.width - 10) : 0 674 width: machineListItemIcon.width > 1 ? machineListItemBackground.width - machineListItemIcon.width - 20 : machineListItemBackground.width - 20 675 smooth: true 676 verticalAlignment: Text.AlignVCenter 677 horizontalAlignment: Text.AlignHCenter 678 } 679 MouseArea { 680 id: machineListItemMouseArea 681 anchors.fill: machineListItemBackground 682 hoverEnabled: true 683 acceptedButtons: Qt.LeftButton 684 onContainsMouseChanged: { 685 if ( mapToItem(menuAndStatusBar, mouseX, mouseY).y < 0 ) { 686 if ( containsMouse ) 687 ToxicWaste.itemEntered(machineListItemText, machineListItemBackground, machineListItemIcon); 688 else 689 ToxicWaste.itemExited(machineListItemText, machineListItemBackground, machineListItemIcon); 690 } 691 } 692 onDoubleClicked: { 693 machineListView.currentIndex = index; 694 ToxicWaste.itemClicked(machineListItemText, machineListItemBackground, machineListItemIcon); 695 searchTextInput.focus = false; 696 launchButton.opacity = 1.0; 697 viewer.launchEmulator(machineListModel[machineListView.currentIndex].id); 698 launchButtonFlashTimer.start(); 699 } 700 onClicked: { 701 machineListView.currentIndex = index; 702 ToxicWaste.itemClicked(machineListItemText, machineListItemBackground, machineListItemIcon); 703 searchTextInput.focus = false; 704 } 705 } 706 } 707 } 708 model: machineListModel 709 function firstVisibleItem() { return indexAt(contentX + 10, contentY + 10); } 710 function lastVisibleItem() { return indexAt(contentX + width - 10, contentY + height - 10); } 711 function itemsPerPage() { return Math.floor(height / 82); } 712 Keys.onPressed: { 713 switch ( event.key ) { 714 case Qt.Key_PageUp: 715 if ( currentIndex - itemsPerPage() > 0 ) { 716 incrementCurrentIndex(); 717 contentY = contentY - height + 70; 718 if ( currentIndex > 0 ) 719 decrementCurrentIndex(); 720 else { 721 contentY = 0; 722 currentIndex = 0; 723 } 724 } else 725 currentIndex = 0; 726 event.accepted = true; 727 break; 728 case Qt.Key_PageDown: 729 if ( currentIndex + itemsPerPage() < machineListModelCount - 1 ) { 730 decrementCurrentIndex(); 731 contentY = contentY + height - 70; 732 if ( currentIndex < machineListModelCount - 1 ) 733 incrementCurrentIndex(); 734 else { 735 contentY = contentHeight - 82; 736 currentIndex = machineListModelCount - 1; 737 } 738 } else 739 currentIndex = machineListModelCount - 1; 740 event.accepted = true; 741 break; 742 case Qt.Key_End: 743 positionViewAtEnd(); 744 event.accepted = true; 745 break; 746 case Qt.Key_Home: 747 positionViewAtBeginning(); 748 event.accepted = true; 749 break; 750 case Qt.Key_Enter: 751 case Qt.Key_Return: 752 if ( !searchTextInput.focus && !(event.modifiers & Qt.AltModifier) && !toxicWasteMain.ignoreLaunch ) { 753 launchButton.opacity = 1.0; 754 viewer.launchEmulator(machineListModel[machineListView.currentIndex].id); 755 launchButtonFlashTimer.start(); 756 event.accepted = true; 757 } 758 break; 759 } 760 } 761 onCurrentIndexChanged: { 762 toxicWasteMain.lastIndex = currentIndex; 763 if ( !flicking ) 764 detailUpdateTimer.restart(); 765 } 766 onFlickingChanged: { 767 if ( !flicking ) 768 detailUpdateTimer.restart(); 769 } 770 } 771 Rectangle { 772 id: confirmQuitDialog 773 smooth: true 774 radius: 10 775 scale: ToxicWaste.scaleFactorX() 776 x: parent.width / 2 - width / 2 777 y: parent.height / 2 - height / 2 778 width: 120 779 height: 80 780 border.color: "black" 781 border.width: 2 782 color: "#007bff" 783 opacity: 0.0 784 state: "hidden" 785 z: 5 786 MouseArea { 787 anchors.fill: parent 788 } 789 Rectangle { 790 anchors.fill: parent 791 anchors.margins: 5 792 color: "#55a5ff" 793 border.color: "black" 794 border.width: 1 795 radius: 5 796 smooth: true 797 } 798 Text { 799 text: qsTr("Really quit?") 800 anchors.top: parent.top 801 anchors.topMargin: 15 802 anchors.horizontalCenter: parent.horizontalCenter 803 anchors.horizontalCenterOffset: 0 804 font.pixelSize: 12 805 color: "black" 806 smooth: true 807 } 808 Row { 809 anchors.bottom: parent.bottom 810 anchors.bottomMargin: 10 811 anchors.horizontalCenterOffset: 0 812 anchors.horizontalCenter: parent.horizontalCenter 813 spacing: 10 814 Button { 815 id: buttonYes 816 text: qsTr("Yes") 817 onClicked: { 818 toxicWasteMain.ignoreLaunch = true; 819 Qt.quit(); 820 } 821 onFocusChanged: { 822 if ( !focus ) 823 toxicWasteMain.focus = true; 824 } 825 KeyNavigation.tab: buttonNo 826 KeyNavigation.backtab: buttonNo 827 KeyNavigation.left: buttonNo 828 KeyNavigation.right: buttonNo 829 } 830 Button { 831 id: buttonNo 832 text: qsTr("No") 833 onClicked: { 834 toxicWasteMain.ignoreLaunch = true; 835 confirmQuitDialog.state = "hidden"; 836 resetIgnoreLaunchTimer.restart(); 837 } 838 onFocusChanged: { 839 if ( !focus ) 840 toxicWasteMain.focus = true; 841 } 842 KeyNavigation.tab: buttonYes 843 KeyNavigation.backtab: buttonYes 844 KeyNavigation.left: buttonYes 845 KeyNavigation.right: buttonYes 846 } 847 } 848 onStateChanged: { 849 if ( state == "shown" ) 850 buttonNo.focus = true; 851 else { 852 buttonNo.focus = false; 853 buttonYes.focus = false; 854 if ( preferencesDialog.state == "shown" ) 855 closeButton.focus = true; 856 } 857 } 858 states: [ 859 State { 860 name: "hidden" 861 PropertyChanges { target: confirmQuitDialog; opacity: 0.0 } 862 }, 863 State { 864 name: "shown" 865 PropertyChanges { target: confirmQuitDialog; opacity: 1.0 } 866 } 867 ] 868 transitions: Transition { 869 from: "hidden" 870 to: "shown" 871 reversible: true 872 PropertyAnimation { property: "opacity"; duration: 250 } 873 } 874 onOpacityChanged: { 875 if ( opacity > 0 ) 876 searchTextInput.focus = false; 877 } 878 } 879 Rectangle { 880 id: preferencesDialog 881 smooth: true 882 radius: 10 883 scale: ToxicWaste.scaleFactorX() 884 x: parent.width / 2 - width / 2 885 y: parent.height / 2 - height / 2 886 width: 300 887 height: 250 888 border.color: "black" 889 border.width: 2 890 color: "#007bff" 891 opacity: 0.0 892 state: "hidden" 893 z: 4 894 MouseArea { 895 anchors.fill: parent 896 } 897 TabWidget { 898 id: preferencesTabWidget 899 anchors.fill: parent 900 anchors.margins: 5 901 baseColor: "#55a5ff" 902 activeBorderColor: "black" 903 inactiveBorderColor: "#202020" 904 activeTextColor: "black" 905 inactiveTextColor: "#202020" 906 fontSize: 12 907 smooth: true 908 current: toxicWasteMain.preferencesTab 909 onCurrentChanged: toxicWasteMain.preferencesTab = current 910 scaleFactor: ToxicWaste.scaleFactorX() 911 Rectangle { 912 id: preferencesSwitchesTab 913 property string title: qsTr("Switches") 914 property alias firstKeyNavItem: showBgAnimCheckBox 915 property alias lastKeyNavItem: autoPositionOverlayCheckBox 916 anchors.fill: parent 917 color: preferencesTabWidget.baseColor 918 radius: 5 919 smooth: true 920 border.color: preferencesTabWidget.activeBorderColor 921 CheckBox { 922 id: showBgAnimCheckBox 923 anchors.top: preferencesSwitchesTab.top 924 anchors.topMargin: 10 925 anchors.bottom: preferencesSwitchesTab.top 926 anchors.bottomMargin: -26 927 anchors.left: parent.left 928 anchors.leftMargin: 5 929 anchors.right: parent.right 930 anchors.rightMargin: 10 931 checked: toxicWasteMain.showBackgroundAnimation 932 text: qsTr("Show floating-bubbles animation?") 933 textColor: "black" 934 onClicked: { 935 toxicWasteMain.showBackgroundAnimation = checked; 936 toxicWasteMain.ignoreLaunch = true; 937 resetIgnoreLaunchTimer.restart(); 938 if ( checked ) 939 backgroundAnim.opacity = 1.0; 940 else 941 backgroundAnim.opacity = 0.0; 942 focus = true; 943 } 944 onFocusChanged: { 945 if ( !focus ) 946 toxicWasteMain.focus = true; 947 } 948 KeyNavigation.tab: preferencesTabWidget.nextKeyNavItem(animInFgCheckBox, preferencesSwitchesTab) 949 KeyNavigation.backtab: preferencesTabWidget.previousKeyNavItem(closeButton, preferencesSwitchesTab) 950 KeyNavigation.right: preferencesTabWidget.nextKeyNavItem(animInFgCheckBox, preferencesSwitchesTab) 951 KeyNavigation.left: preferencesTabWidget.previousKeyNavItem(closeButton, preferencesSwitchesTab) 952 smooth: true 953 } 954 CheckBox { 955 id: animInFgCheckBox 956 anchors.top: showBgAnimCheckBox.bottom 957 anchors.topMargin: 10 958 anchors.bottom: showBgAnimCheckBox.bottom 959 anchors.bottomMargin: -26 960 anchors.left: parent.left 961 anchors.leftMargin: 5 962 anchors.right: parent.right 963 anchors.rightMargin: 10 964 checked: toxicWasteMain.animateInForeground 965 text: qsTr("Draw animation in the foreground?") 966 textColor: "black" 967 onClicked: { 968 toxicWasteMain.animateInForeground = checked; 969 toxicWasteMain.ignoreLaunch = true; 970 resetIgnoreLaunchTimer.restart(); 971 focus = true; 972 } 973 onFocusChanged: { 974 if ( !focus ) 975 toxicWasteMain.focus = true; 976 } 977 KeyNavigation.tab: preferencesTabWidget.nextKeyNavItem(showShaderEffectCheckBox, preferencesSwitchesTab) 978 KeyNavigation.backtab: preferencesTabWidget.previousKeyNavItem(showBgAnimCheckBox, preferencesSwitchesTab) 979 KeyNavigation.right: preferencesTabWidget.nextKeyNavItem(showShaderEffectCheckBox, preferencesSwitchesTab) 980 KeyNavigation.left: preferencesTabWidget.previousKeyNavItem(showBgAnimCheckBox, preferencesSwitchesTab) 981 smooth: true 982 } 983 CheckBox { 984 id: showShaderEffectCheckBox 985 anchors.top: animInFgCheckBox.bottom 986 anchors.topMargin: 10 987 anchors.bottom: animInFgCheckBox.bottom 988 anchors.bottomMargin: -26 989 anchors.left: parent.left 990 anchors.leftMargin: 5 991 anchors.right: parent.right 992 anchors.rightMargin: 10 993 checked: toxicWasteMain.showShaderEffect 994 text: qsTr("Show radial wave effect on background?") 995 textColor: "black" 996 onClicked: { 997 toxicWasteMain.showShaderEffect = checked; 998 waveEffect.running = checked; 999 toxicWasteMain.ignoreLaunch = true; 1000 resetIgnoreLaunchTimer.restart(); 1001 focus = true; 1002 } 1003 onFocusChanged: { 1004 if ( !focus ) 1005 toxicWasteMain.focus = true; 1006 } 1007 KeyNavigation.tab: preferencesTabWidget.nextKeyNavItem(autoStopAnimationsCheckBox, preferencesSwitchesTab) 1008 KeyNavigation.backtab: preferencesTabWidget.previousKeyNavItem(animInFgCheckBox, preferencesSwitchesTab) 1009 KeyNavigation.right: preferencesTabWidget.nextKeyNavItem(autoStopAnimationsCheckBox, preferencesSwitchesTab) 1010 KeyNavigation.left: preferencesTabWidget.previousKeyNavItem(animInFgCheckBox, preferencesSwitchesTab) 1011 smooth: true 1012 } 1013 CheckBox { 1014 id: autoStopAnimationsCheckBox 1015 anchors.top: showShaderEffectCheckBox.bottom 1016 anchors.topMargin: 10 1017 anchors.bottom: showShaderEffectCheckBox.bottom 1018 anchors.bottomMargin: -26 1019 anchors.left: parent.left 1020 anchors.leftMargin: 5 1021 anchors.right: parent.right 1022 anchors.rightMargin: 10 1023 checked: toxicWasteMain.autoStopAnimations 1024 text: qsTr("Auto-stop animation and wave effect?") 1025 textColor: "black" 1026 onClicked: { 1027 toxicWasteMain.autoStopAnimations = checked; 1028 toxicWasteMain.ignoreLaunch = true; 1029 resetIgnoreLaunchTimer.restart(); 1030 focus = true; 1031 } 1032 onFocusChanged: { 1033 if ( !focus ) 1034 toxicWasteMain.focus = true; 1035 } 1036 KeyNavigation.tab: preferencesTabWidget.nextKeyNavItem(showFpsCheckBox, preferencesSwitchesTab) 1037 KeyNavigation.backtab: preferencesTabWidget.previousKeyNavItem(showShaderEffectCheckBox, preferencesSwitchesTab) 1038 KeyNavigation.right: preferencesTabWidget.nextKeyNavItem(showFpsCheckBox, preferencesSwitchesTab) 1039 KeyNavigation.left: preferencesTabWidget.previousKeyNavItem(showShaderEffectCheckBox, preferencesSwitchesTab) 1040 smooth: true 1041 } 1042 CheckBox { 1043 id: showFpsCheckBox 1044 anchors.top: autoStopAnimationsCheckBox.bottom 1045 anchors.topMargin: 10 1046 anchors.bottom: autoStopAnimationsCheckBox.bottom 1047 anchors.bottomMargin: -26 1048 anchors.left: parent.left 1049 anchors.leftMargin: 5 1050 anchors.right: parent.right 1051 anchors.rightMargin: 10 1052 checked: toxicWasteMain.fpsVisible 1053 text: qsTr("Show FPS counter in the menu-bar?") 1054 textColor: "black" 1055 onClicked: { 1056 toxicWasteMain.fpsVisible = checked; 1057 toxicWasteMain.ignoreLaunch = true; 1058 resetIgnoreLaunchTimer.restart(); 1059 focus = true; 1060 } 1061 onFocusChanged: { 1062 if ( !focus ) 1063 toxicWasteMain.focus = true; 1064 } 1065 KeyNavigation.tab: preferencesTabWidget.nextKeyNavItem(confirmQuitCheckBox, preferencesSwitchesTab) 1066 KeyNavigation.backtab: preferencesTabWidget.previousKeyNavItem(autoStopAnimationsCheckBox, preferencesSwitchesTab) 1067 KeyNavigation.right: preferencesTabWidget.nextKeyNavItem(confirmQuitCheckBox, preferencesSwitchesTab) 1068 KeyNavigation.left: preferencesTabWidget.previousKeyNavItem(autoStopAnimationsCheckBox, preferencesSwitchesTab) 1069 smooth: true 1070 } 1071 CheckBox { 1072 id: confirmQuitCheckBox 1073 anchors.top: showFpsCheckBox.bottom 1074 anchors.topMargin: 10 1075 anchors.bottom: showFpsCheckBox.bottom 1076 anchors.bottomMargin: -26 1077 anchors.left: parent.left 1078 anchors.leftMargin: 5 1079 anchors.right: parent.right 1080 anchors.rightMargin: 10 1081 checked: toxicWasteMain.confirmQuit 1082 text: qsTr("Confirm when quitting the application?") 1083 textColor: "black" 1084 onClicked: { 1085 toxicWasteMain.confirmQuit = checked; 1086 toxicWasteMain.ignoreLaunch = true; 1087 resetIgnoreLaunchTimer.restart(); 1088 focus = true; 1089 } 1090 onFocusChanged: { 1091 if ( !focus ) 1092 toxicWasteMain.focus = true; 1093 } 1094 KeyNavigation.tab: preferencesTabWidget.nextKeyNavItem(autoPositionOverlayCheckBox, preferencesSwitchesTab) 1095 KeyNavigation.backtab: preferencesTabWidget.previousKeyNavItem(showFpsCheckBox, preferencesSwitchesTab) 1096 KeyNavigation.right: preferencesTabWidget.nextKeyNavItem(autoPositionOverlayCheckBox, preferencesSwitchesTab) 1097 KeyNavigation.left: preferencesTabWidget.previousKeyNavItem(showFpsCheckBox, preferencesSwitchesTab) 1098 smooth: true 1099 } 1100 CheckBox { 1101 id: autoPositionOverlayCheckBox 1102 anchors.top: confirmQuitCheckBox.bottom 1103 anchors.topMargin: 10 1104 anchors.bottom: confirmQuitCheckBox.bottom 1105 anchors.bottomMargin: -26 1106 anchors.left: parent.left 1107 anchors.leftMargin: 5 1108 anchors.right: parent.right 1109 anchors.rightMargin: 10 1110 checked: toxicWasteMain.autoPositionOverlay 1111 text: qsTr("Position the cabinet automatically?") 1112 textColor: "black" 1113 onClicked: { 1114 toxicWasteMain.autoPositionOverlay = checked; 1115 toxicWasteMain.ignoreLaunch = true; 1116 resetIgnoreLaunchTimer.restart(); 1117 focus = true; 1118 } 1119 onFocusChanged: { 1120 if ( !focus ) 1121 toxicWasteMain.focus = true; 1122 } 1123 KeyNavigation.tab: preferencesTabWidget.nextKeyNavItem(closeButton, preferencesSwitchesTab) 1124 KeyNavigation.backtab: preferencesTabWidget.previousKeyNavItem(confirmQuitCheckBox, preferencesSwitchesTab) 1125 KeyNavigation.right: preferencesTabWidget.nextKeyNavItem(closeButton, preferencesSwitchesTab) 1126 KeyNavigation.left: preferencesTabWidget.previousKeyNavItem(confirmQuitCheckBox, preferencesSwitchesTab) 1127 smooth: true 1128 } 1129 } 1130 Rectangle { 1131 id: preferencesSlidersTab 1132 property string title: qsTr("Sliders") 1133 property alias firstKeyNavItem: cliOptionCombo 1134 property alias lastKeyNavItem: cliValueCombo 1135 property alias cabinetZoomValue: cabinetZoomSlider.value 1136 anchors.fill: parent 1137 color: preferencesTabWidget.baseColor 1138 border.color: preferencesTabWidget.activeBorderColor 1139 radius: 5 1140 smooth: true 1141 Slider { 1142 id: cabinetZoomSlider 1143 anchors.top: parent.top 1144 anchors.left: parent.left 1145 anchors.right: parent.right 1146 anchors.margins: 5 1147 sliderText: qsTr("Cabinet zoom") 1148 minimum: 0.01 1149 maximum: 10 1150 value: toxicWasteMain.overlayScale 1151 defaultValue: 1 1152 onValueChanged: toxicWasteMain.overlayScale = value 1153 } 1154 Slider { 1155 id: cabinetOffsetXSlider 1156 anchors.top: cabinetZoomSlider.bottom 1157 anchors.left: parent.left 1158 anchors.right: parent.right 1159 anchors.margins: 5 1160 sliderText: qsTr("Cabinet X center offset") 1161 minimum: (-toxicWasteMain.width/2 - 514 * toxicWasteMain.overlayScale) / toxicWasteMain.overlayScale 1162 maximum: (toxicWasteMain.width/2 + 514 * toxicWasteMain.overlayScale) / toxicWasteMain.overlayScale 1163 showAsPercent: false 1164 value: toxicWasteMain.overlayOffsetX 1165 defaultValue: 0 1166 onValueChanged: toxicWasteMain.overlayOffsetX = value 1167 } 1168 Slider { 1169 id: cabinetOffsetYSlider 1170 anchors.top: cabinetOffsetXSlider.bottom 1171 anchors.left: parent.left 1172 anchors.right: parent.right 1173 anchors.margins: 5 1174 sliderText: qsTr("Cabinet Y center offset") 1175 minimum: (-toxicWasteMain.height/2 - 1104 * toxicWasteMain.overlayScale) / toxicWasteMain.overlayScale 1176 maximum: (toxicWasteMain.height/2 + 1104 * toxicWasteMain.overlayScale) / toxicWasteMain.overlayScale 1177 showAsPercent: false 1178 value: toxicWasteMain.overlayOffsetY 1179 defaultValue: 0 1180 onValueChanged: toxicWasteMain.overlayOffsetY = value 1181 } 1182 Slider { 1183 id: cabinetOpacitySlider 1184 anchors.top: cabinetOffsetYSlider.bottom 1185 anchors.left: parent.left 1186 anchors.right: parent.right 1187 anchors.margins: 5 1188 sliderText: qsTr("Cabinet opacity") 1189 minimum: 0 1190 maximum: 1 1191 value: toxicWasteMain.overlayOpacity 1192 defaultValue: 1 1193 onValueChanged: toxicWasteMain.overlayOpacity = value 1194 } 1195 Slider { 1196 id: backgroundOpacitySlider 1197 anchors.top: cabinetOpacitySlider.bottom 1198 anchors.left: parent.left 1199 anchors.right: parent.right 1200 anchors.margins: 5 1201 sliderText: qsTr("Background opacity") 1202 minimum: 0 1203 maximum: 1 1204 value: toxicWasteMain.backgroundOpacity 1205 defaultValue: 0.7 1206 onValueChanged: toxicWasteMain.backgroundOpacity = value 1207 } 1208 Slider { 1209 id: machineListOpacitySlider 1210 anchors.top: backgroundOpacitySlider.bottom 1211 anchors.left: parent.left 1212 anchors.right: parent.right 1213 anchors.margins: 5 1214 sliderText: qsTr("Machine list opacity") 1215 minimum: 0 1216 maximum: 1 1217 value: toxicWasteMain.machineListOpacity 1218 defaultValue: 1 1219 onValueChanged: toxicWasteMain.machineListOpacity = value 1220 } 1221 } 1222 Rectangle { 1223 id: preferencesBackendTab 1224 property string title: qsTr("Backend") 1225 property alias firstKeyNavItem: cliOptionCombo 1226 property alias lastKeyNavItem: cliValueCombo 1227 anchors.fill: parent 1228 color: preferencesTabWidget.baseColor 1229 border.color: preferencesTabWidget.activeBorderColor 1230 radius: 5 1231 smooth: true 1232 Text { 1233 id: cliOptionText 1234 anchors.top: parent.top 1235 anchors.left: parent.left 1236 anchors.right: parent.right 1237 anchors.margins: 5 1238 anchors.rightMargin: parent.width/2 + 2 1239 smooth: true 1240 font.pixelSize: 12 1241 text: qsTr("Option") 1242 horizontalAlignment: Text.Center 1243 } 1244 ComboBox { 1245 id: cliOptionCombo 1246 anchors.top: cliOptionText.bottom 1247 anchors.left: parent.left 1248 anchors.right: parent.right 1249 anchors.margins: 5 1250 anchors.rightMargin: parent.width/2 + 2 1251 smooth: true 1252 model: ListModel {} 1253 arrowIcon: "images/down_arrow.png" 1254 font.pixelSize: 12 1255 z: +1 1256 onFocusChanged: { 1257 if ( !focus ) 1258 toxicWasteMain.focus = true; 1259 } 1260 onIndexChosen: { 1261 cliValueCombo.ready = false; 1262 cliValueCombo.model.clear(); 1263 var cliParamName = viewer.cliParamNames()[index]; 1264 var cliParamOptions = viewer.cliParamAllowedValues(cliParamName); 1265 var cliParamValue = viewer.cliParamValue(cliParamName); 1266 var indexToSelect = 0; 1267 for (var i = 0; i < cliParamOptions.length; i++) { 1268 var cliParamOption = cliParamOptions[i]; 1269 cliValueCombo.model.append({'name': cliParamOption}); 1270 if ( cliParamOption === cliParamValue ) 1271 indexToSelect = i; 1272 } 1273 cliValueCombo.close(true); 1274 cliValueCombo.positionAtTop(); 1275 cliValueCombo.setCurrentIndex(indexToSelect); 1276 cliValueCombo.ready = true; 1277 } 1278 KeyNavigation.tab: preferencesTabWidget.nextKeyNavItem(cliValueCombo, preferencesBackendTab); 1279 KeyNavigation.backtab: preferencesTabWidget.previousKeyNavItem(closeButton, preferencesBackendTab); 1280 KeyNavigation.right: preferencesTabWidget.nextKeyNavItem(cliValueCombo, preferencesBackendTab); 1281 KeyNavigation.left: preferencesTabWidget.previousKeyNavItem(closeButton, preferencesBackendTab); 1282 } 1283 Text { 1284 id: cliValueText 1285 anchors.top: parent.top 1286 anchors.left: parent.left 1287 anchors.right: parent.right 1288 anchors.margins: 5 1289 anchors.leftMargin: parent.width/2 + 2 1290 smooth: true 1291 font.pixelSize: 12 1292 horizontalAlignment: Text.Center 1293 text: qsTr("Value") 1294 } 1295 ComboBox { 1296 id: cliValueCombo 1297 property bool ready: false 1298 anchors.left: parent.left 1299 anchors.right: parent.right 1300 anchors.top: cliValueText.bottom 1301 anchors.margins: 5 1302 anchors.leftMargin: parent.width/2 + 2 1303 smooth: true 1304 model: ListModel {} 1305 arrowIcon: "images/down_arrow.png" 1306 font.pixelSize: 12 1307 z: +1 1308 onFocusChanged: { 1309 if ( !focus ) 1310 toxicWasteMain.focus = true; 1311 } 1312 onIndexChosen: { 1313 if ( ready ) { 1314 var cliParamName = viewer.cliParamNames()[cliOptionCombo.currentIndex()]; 1315 var cliParamValue = viewer.cliParamAllowedValues(cliParamName)[cliValueCombo.currentIndex()]; 1316 if ( cliParamName !== "" && cliParamValue !== "" ) 1317 viewer.setCliParamValue(cliParamName, cliParamValue); 1318 } 1319 } 1320 KeyNavigation.tab: preferencesTabWidget.nextKeyNavItem(closeButton, preferencesBackendTab); 1321 KeyNavigation.backtab: preferencesTabWidget.previousKeyNavItem(cliOptionCombo, preferencesBackendTab); 1322 KeyNavigation.right: preferencesTabWidget.nextKeyNavItem(closeButton, preferencesBackendTab); 1323 KeyNavigation.left: preferencesTabWidget.previousKeyNavItem(cliOptionCombo, preferencesBackendTab); 1324 } 1325 Text { 1326 id: cliInfoText 1327 anchors.top: cliValueCombo.bottom 1328 anchors.left: parent.left 1329 anchors.right: parent.right 1330 anchors.margins: 5 1331 anchors.topMargin: 20 1332 smooth: true 1333 font.pixelSize: 12 1334 horizontalAlignment: Text.Center 1335 text: qsTr("For customized default backend options to\ntake effect, please restart QMC2 Arcade!") 1336 } 1337 Component.onCompleted: { 1338 var cliParamNames = viewer.cliParamNames(); 1339 var cliParamOptions = viewer.cliParamAllowedValues(cliParamNames[0]); 1340 var cliParamValue = viewer.cliParamValue(cliParamNames[0]); 1341 var indexToSelect = 0; 1342 for (var i = 0; i < cliParamOptions.length; i++) { 1343 var cliParamOption = cliParamOptions[i]; 1344 cliValueCombo.model.append({'name': cliParamOption}); 1345 if ( cliParamOption === cliParamValue ) 1346 indexToSelect = i; 1347 } 1348 for (var i = 0; i < cliParamNames.length; i++) 1349 cliOptionCombo.model.append({'name': viewer.cliParamDescription(cliParamNames[i])}); 1350 cliValueCombo.setCurrentIndex(indexToSelect); 1351 cliValueCombo.ready = true; 1352 } 1353 } 1354 } 1355 Button { 1356 id: closeButton 1357 anchors.bottom: parent.bottom 1358 anchors.bottomMargin: 10 1359 anchors.horizontalCenterOffset: 0 1360 anchors.horizontalCenter: parent.horizontalCenter 1361 text: qsTr("Close") 1362 onClicked: { 1363 toxicWasteMain.ignoreLaunch = true; 1364 preferencesDialog.state = "hidden"; 1365 toxicWasteMain.focus = true; 1366 resetIgnoreLaunchTimer.restart(); 1367 } 1368 onFocusChanged: { 1369 if ( !focus ) 1370 toxicWasteMain.focus = true; 1371 } 1372 KeyNavigation.tab: preferencesTabWidget.firstKeyNavItem() 1373 KeyNavigation.backtab: preferencesTabWidget.lastKeyNavItem() 1374 KeyNavigation.right: preferencesTabWidget.firstKeyNavItem() 1375 KeyNavigation.left: preferencesTabWidget.lastKeyNavItem() 1376 } 1377 onStateChanged: { 1378 if ( state == "shown" && confirmQuitDialog.state == "hidden" ) 1379 closeButton.focus = true; 1380 else { 1381 closeButton.focus = false; 1382 showBgAnimCheckBox.focus = false; 1383 showFpsCheckBox = false; 1384 } 1385 } 1386 states: [ 1387 State { 1388 name: "hidden" 1389 PropertyChanges { target: preferencesDialog; opacity: 0.0 } 1390 }, 1391 State { 1392 name: "shown" 1393 PropertyChanges { target: preferencesDialog; opacity: 1.0 } 1394 } 1395 ] 1396 transitions: Transition { 1397 from: "hidden" 1398 to: "shown" 1399 reversible: true 1400 PropertyAnimation { property: "opacity"; duration: 250 } 1401 } 1402 } 1403 Image { 1404 id: showHideMenuBarButton 1405 source: "images/hide_show_menu.png" 1406 height: menuAndStatusBar.height 1407 anchors.left: parent.left 1408 anchors.leftMargin: 2 * ToxicWaste.scaleFactorX() 1409 anchors.bottom: parent.bottom 1410 fillMode: Image.PreserveAspectFit 1411 opacity: 0.5 1412 rotation: toxicWasteMain.menuHidden ? 0 : 180 1413 smooth: true 1414 z: 5 1415 MouseArea { 1416 anchors.fill: parent 1417 hoverEnabled: true 1418 onEntered: parent.opacity = 1.0 1419 onMouseXChanged: parent.opacity = 1.0 1420 onMouseYChanged: parent.opacity = 1.0 1421 onExited: parent.opacity = 0.5 1422 onClicked: { 1423 if ( !menuAndStatusBar.showHideAnim.running ) { 1424 parent.opacity = 0.5; 1425 if ( parent.rotation == 0 ) { 1426 parent.rotation = 180; 1427 toxicWasteMain.menuHidden = false; 1428 } else { 1429 parent.rotation = 0; 1430 toxicWasteMain.menuHidden = true; 1431 } 1432 searchTextInput.focus = false; 1433 } 1434 } 1435 } 1436 } 1437 onMenuHiddenChanged: { 1438 if ( menuHidden ) { 1439 menuAndStatusBar.anchors.bottomMargin -= 64; 1440 searchTextInput.focus = false; 1441 } else 1442 menuAndStatusBar.anchors.bottomMargin += 64; 1443 } 1444 Rectangle { 1445 id: menuAndStatusBar 1446 property alias showHideAnim: menuAndStatusBarAnimation 1447 x: 0 1448 z: 4 1449 Behavior on anchors.bottomMargin { 1450 NumberAnimation { id: menuAndStatusBarAnimation; duration: 250 } 1451 } 1452 width: parent.width 1453 height: ToxicWaste.scaleFactorY() > 1 ? 24 : (ToxicWaste.scaleFactorY() < 0.5 ? 12 : 24 * ToxicWaste.scaleFactorY()) 1454 anchors.horizontalCenter: parent.horizontalCenter 1455 anchors.bottom: parent.bottom 1456 anchors.bottomMargin: 0 1457 transformOrigin: Rectangle.Bottom 1458 opacity: 1.0 1459 smooth: true 1460 gradient: Gradient { 1461 GradientStop { position: 0.00; color: "#40AAAAAA" } 1462 GradientStop { position: 0.33; color: "#60AAAAAA" } 1463 GradientStop { position: 0.66; color: "#60AAAAAA" } 1464 GradientStop { position: 1.00; color: "#40AAAAAA" } 1465 } 1466 Text { 1467 id: fpsText 1468 anchors.left: parent.left 1469 anchors.leftMargin: showHideMenuBarButton.width + 10 * ToxicWaste.scaleFactorX() 1470 anchors.verticalCenter: menuAndStatusBar.verticalCenter 1471 opacity: 0.8 1472 smooth: true 1473 color: "white" 1474 text: qsTr("FPS") + ": " + toxicWasteMain.fps.toString() 1475 font.pixelSize: parent.height - 4 * ToxicWaste.scaleFactorY() 1476 visible: toxicWasteMain.fpsVisible 1477 } 1478 Image { 1479 id: exitButton 1480 anchors.top: parent.top 1481 anchors.topMargin: 2 * ToxicWaste.scaleFactorY() 1482 anchors.bottom: parent.bottom 1483 anchors.bottomMargin: 2 * ToxicWaste.scaleFactorY() 1484 anchors.right: parent.right 1485 anchors.rightMargin: 2 * ToxicWaste.scaleFactorX() 1486 source: "images/exit.png" 1487 smooth: true 1488 fillMode: Image.PreserveAspectFit 1489 opacity: 0.5 1490 MouseArea { 1491 anchors.fill: parent 1492 hoverEnabled: true 1493 onEntered: parent.opacity = 1.0 1494 onMouseXChanged: parent.opacity = 1.0 1495 onMouseYChanged: parent.opacity = 1.0 1496 onExited: parent.opacity = 0.5 1497 onClicked: { 1498 if ( toxicWasteMain.confirmQuit ) { 1499 parent.opacity = 1.0; 1500 confirmQuitDialog.state = "shown"; 1501 searchTextInput.focus = false; 1502 } else 1503 Qt.quit(); 1504 } 1505 } 1506 } 1507 Image { 1508 id: fullScreenToggleButton 1509 anchors.top: parent.top 1510 anchors.topMargin: 2 * ToxicWaste.scaleFactorY() 1511 anchors.bottom: parent.bottom 1512 anchors.bottomMargin: 2 * ToxicWaste.scaleFactorY() 1513 anchors.right: exitButton.left 1514 anchors.rightMargin: 5 * ToxicWaste.scaleFactorX() 1515 source: "images/fullscreen.png" 1516 state: toxicWasteMain.fullScreen ? "fullscreen" : "windowed" 1517 smooth: true 1518 fillMode: Image.PreserveAspectFit 1519 opacity: 0.5 1520 MouseArea { 1521 anchors.fill: parent 1522 hoverEnabled: true 1523 onEntered: parent.opacity = 1.0 1524 onMouseXChanged: parent.opacity = 1.0 1525 onMouseYChanged: parent.opacity = 1.0 1526 onExited: parent.opacity = 0.5 1527 onClicked: { 1528 if ( fullScreenToggleButton.state == "windowed" ) { 1529 fullScreenToggleButton.state = "fullscreen" 1530 toxicWasteMain.fullScreen = true; 1531 } else { 1532 fullScreenToggleButton.state = "windowed" 1533 toxicWasteMain.fullScreen = false; 1534 } 1535 parent.opacity = 1.0; 1536 searchTextInput.focus = false; 1537 } 1538 } 1539 states: [ 1540 State { 1541 name: "fullscreen" 1542 PropertyChanges { target: fullScreenToggleButton; source: "images/windowed.png" } 1543 }, 1544 State { 1545 name: "windowed" 1546 PropertyChanges { target: fullScreenToggleButton; source: "images/fullscreen.png" } 1547 } 1548 ] 1549 } 1550 Image { 1551 id: preferencesButton 1552 anchors.top: parent.top 1553 anchors.topMargin: 2 * ToxicWaste.scaleFactorY() 1554 anchors.bottom: parent.bottom 1555 anchors.bottomMargin: 2 * ToxicWaste.scaleFactorY() 1556 anchors.right: fullScreenToggleButton.left 1557 anchors.rightMargin: 5 * ToxicWaste.scaleFactorX() 1558 source: "images/preferences.png" 1559 smooth: true 1560 fillMode: Image.PreserveAspectFit 1561 opacity: 0.5 1562 MouseArea { 1563 anchors.fill: parent 1564 hoverEnabled: true 1565 onEntered: parent.opacity = 1.0 1566 onMouseXChanged: parent.opacity = 1.0 1567 onMouseYChanged: parent.opacity = 1.0 1568 onExited: parent.opacity = 0.5 1569 onClicked: { 1570 parent.opacity = 1.0; 1571 preferencesDialog.state = "shown"; 1572 searchTextInput.focus = false; 1573 } 1574 } 1575 } 1576 Item { 1577 id: searchBox 1578 anchors.centerIn: parent 1579 width: 230 * ToxicWaste.scaleFactorX() 1580 height: menuAndStatusBar.height - 2 * ToxicWaste.scaleFactorY() 1581 Image { 1582 id: searchImage 1583 source: "images/find.png" 1584 height: parent.height - 4 * ToxicWaste.scaleFactorY() 1585 fillMode: Image.PreserveAspectFit 1586 smooth: true 1587 anchors.verticalCenter: parent.verticalCenter 1588 opacity: 0.5 1589 MouseArea { 1590 anchors.fill: parent 1591 hoverEnabled: true 1592 onEntered: parent.opacity = 1.0 1593 onMouseXChanged: parent.opacity = 1.0 1594 onMouseYChanged: parent.opacity = 1.0 1595 onExited: parent.opacity = 0.5 1596 onClicked: { 1597 parent.opacity = 1.0; 1598 machineListView.positionViewAtIndex(viewer.findIndex(searchTextInput.text, machineListView.currentIndex), ListView.Beginning); 1599 searchTextInput.focus = false; 1600 } 1601 } 1602 } 1603 Rectangle { 1604 id: searchTextInputBox 1605 height: parent.height - 2 * ToxicWaste.scaleFactorY() 1606 width: 200 * ToxicWaste.scaleFactorX() 1607 radius: height/2 1608 smooth: true 1609 anchors.left: searchImage.right 1610 anchors.leftMargin: 5 * ToxicWaste.scaleFactorX() 1611 anchors.verticalCenter: searchImage.verticalCenter 1612 TextInput { 1613 id: searchTextInput 1614 anchors.verticalCenter: parent.verticalCenter 1615 anchors.left: parent.left 1616 anchors.leftMargin: 2 * ToxicWaste.scaleFactorX() 1617 anchors.right: parent.right 1618 anchors.rightMargin: 2 * ToxicWaste.scaleFactorX() 1619 font.pointSize: parent.height - 2 1620 smooth: true 1621 focus: false 1622 autoScroll: true 1623 clip: true 1624 selectByMouse: true 1625 opacity: 0.8 1626 cursorDelegate: Rectangle { 1627 id: searchTextCursorDelegate 1628 color: "black" 1629 width: 1 1630 height: parent.height 1631 anchors.verticalCenter: parent.verticalCenter 1632 visible: parent.activeFocus 1633 SequentialAnimation on opacity { 1634 loops: Animation.Infinite 1635 running: searchTextCursorDelegate.visible 1636 PropertyAction { target: searchTextCursorDelegate; property: "opacity"; value: 1.0 } 1637 PauseAnimation { duration: 500 } 1638 PropertyAction { target: searchTextCursorDelegate; property: "opacity"; value: 0.0 } 1639 PauseAnimation { duration: 500 } 1640 } 1641 } 1642 onAccepted: machineListView.positionViewAtIndex(viewer.findIndex(searchTextInput.text, machineListView.currentIndex), ListView.Beginning); 1643 onFocusChanged: { 1644 if ( !focus ) 1645 toxicWasteMain.focus = true; 1646 } 1647 } 1648 } 1649 Image { 1650 id: clearImage 1651 source: "images/clear.png" 1652 height: parent.height + 2 * ToxicWaste.scaleFactorY() 1653 fillMode: Image.PreserveAspectFit 1654 smooth: true 1655 anchors.verticalCenter: parent.verticalCenter 1656 anchors.left: searchTextInputBox.right 1657 anchors.leftMargin: 5 * ToxicWaste.scaleFactorY() 1658 opacity: 0.5 1659 MouseArea { 1660 anchors.fill: parent 1661 hoverEnabled: true 1662 onEntered: parent.opacity = 1.0 1663 onMouseXChanged: parent.opacity = 1.0 1664 onMouseYChanged: parent.opacity = 1.0 1665 onExited: parent.opacity = 0.5 1666 onClicked: { 1667 parent.opacity = 1.0; 1668 searchTextInput.text = ""; 1669 searchTextInput.focus = false; 1670 } 1671 } 1672 } 1673 } 1674 } 1675 BackgroundAnimation { 1676 id: backgroundAnim 1677 opacity: toxicWasteMain.showBackgroundAnimation ? 1.0 : 0.0 1678 Behavior on opacity { 1679 NumberAnimation { properties: "opacity"; duration: 1000 } 1680 } 1681 z: toxicWasteMain.animateInForeground ? 5 : 2 1682 } 1683 ShaderEffectSource { 1684 id: effectSource 1685 anchors.fill: parent 1686 sourceItem: Image { 1687 source: "images/shadereffectsource.png" 1688 anchors.fill: parent 1689 } 1690 live: false 1691 hideSource: true 1692 } 1693 RadialWaveEffect { 1694 id: waveEffect 1695 anchors.fill: parent 1696 source: effectSource 1697 wave: 0.0 1698 waveOriginX: 0.5 1699 waveOriginY: 0.5 1700 waveWidth: 0.01 1701 z: 1 1702 property alias running: waveAnim.running 1703 NumberAnimation on wave { 1704 id: waveAnim 1705 running: toxicWasteMain.showShaderEffect 1706 loops: Animation.Infinite 1707 easing.type: Easing.Linear 1708 from: 0.0000; 1709 to: 2.0000; 1710 duration: 3000 1711 onRunningChanged: { 1712 if ( running ) 1713 viewer.log("ToxicWaste: " + qsTr("Starting shader effect")); 1714 else 1715 viewer.log("ToxicWaste: " + qsTr("Shader effect stopped")); 1716 } 1717 } 1718 } 1719 focus: true 1720 Keys.onPressed: { 1721 switch ( event.key ) { 1722 case Qt.Key_Escape: 1723 if ( searchTextInput.focus ) 1724 searchTextInput.focus = false; 1725 else { 1726 if ( toxicWasteMain.confirmQuit ) { 1727 if ( confirmQuitDialog.state == "hidden" ) 1728 confirmQuitDialog.state = "shown"; 1729 else 1730 confirmQuitDialog.state = "hidden"; 1731 } else 1732 Qt.quit(); 1733 } 1734 event.accepted = true; 1735 break; 1736 case Qt.Key_F11: 1737 fullScreen = !fullScreen; 1738 event.accepted = true; 1739 break; 1740 case Qt.Key_Enter: 1741 case Qt.Key_Return: 1742 if ( event.modifiers & Qt.AltModifier ) { 1743 fullScreen = !fullScreen; 1744 event.accepted = true; 1745 } 1746 break; 1747 default: 1748 if ( event.modifiers & Qt.ControlModifier) { 1749 switch ( event.key ) { 1750 case Qt.Key_P: 1751 if ( !toxicWasteMain.ignoreLaunch ) { 1752 launchButton.opacity = 1.0; 1753 viewer.launchEmulator(machineListModel[machineListView.currentIndex].id); 1754 launchButtonFlashTimer.start(); 1755 } 1756 event.accepted = true; 1757 break; 1758 case Qt.Key_O: 1759 preferencesDialog.state = preferencesDialog.state == "shown" ? "hidden" : "shown"; 1760 event.accepted = true; 1761 break; 1762 case Qt.Key_M: 1763 toxicWasteMain.menuHidden = !toxicWasteMain.menuHidden; 1764 event.accepted = true; 1765 break; 1766 case Qt.Key_X: 1767 if ( toxicWasteMain.confirmQuit ) { 1768 if ( confirmQuitDialog.state == "hidden" ) 1769 confirmQuitDialog.state = "shown"; 1770 else 1771 confirmQuitDialog.state = "hidden"; 1772 } else 1773 Qt.quit(); 1774 break; 1775 case Qt.Key_F: 1776 if ( !toxicWasteMain.menuHidden ) { 1777 searchTextInput.text = ""; 1778 searchTextInput.focus = true; 1779 event.accepted = true; 1780 } 1781 break; 1782 case Qt.Key_Backspace: 1783 toxicWasteMain.cabinetFlipped = !toxicWasteMain.cabinetFlipped; 1784 event.accepted = true; 1785 break; 1786 } 1787 } else if ( !toxicWasteMain.menuHidden ) { 1788 if ( ToxicWaste.validateKey(event.text) ) { 1789 searchTextInput.text += event.text; 1790 searchTextInput.focus = true; 1791 } else if ( ToxicWaste.validateSpecialKey(event.text) ) { 1792 searchTextInput.focus = true; 1793 switch ( event.text ) { 1794 case "\b": 1795 if ( searchTextInput.text.length > 0) 1796 searchTextInput.text = searchTextInput.text.substring(0, searchTextInput.text.length - 1); 1797 break; 1798 } 1799 } 1800 } 1801 } 1802 } 1803 Keys.forwardTo: [machineListView] 1804 onFullScreenChanged: { 1805 if ( !ToxicWaste.initializing ) { 1806 if ( fullScreen ) { 1807 viewer.switchToFullScreen(); 1808 fullScreenToggleButton.state = "fullscreen"; 1809 } else { 1810 viewer.switchToWindowed(); 1811 fullScreenToggleButton.state = "windowed"; 1812 } 1813 } 1814 } 1815} 1816