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