diff --git a/lib/ocelot-brain b/lib/ocelot-brain index 6fc005d..ece5372 160000 --- a/lib/ocelot-brain +++ b/lib/ocelot-brain @@ -1 +1 @@ -Subproject commit 6fc005d04d785d76fd3330ac304cc717eb99e752 +Subproject commit ece53729967381bc1278479242a147539f33c60a diff --git a/sprites/Loading.png b/sprites/Loading.png new file mode 100644 index 0000000..52dcdb2 Binary files /dev/null and b/sprites/Loading.png differ diff --git a/sprites/Logo.png b/sprites/Logo.png new file mode 100644 index 0000000..c98fd46 Binary files /dev/null and b/sprites/Logo.png differ diff --git a/sprites/icons/Book.png b/sprites/icons/Book.png new file mode 100644 index 0000000..a783ea0 Binary files /dev/null and b/sprites/icons/Book.png differ diff --git a/sprites/icons/Help.png b/sprites/icons/Help.png new file mode 100644 index 0000000..afecd6d Binary files /dev/null and b/sprites/icons/Help.png differ diff --git a/sprites/icons/Ocelot.png b/sprites/icons/Ocelot.png new file mode 100644 index 0000000..c63d983 Binary files /dev/null and b/sprites/icons/Ocelot.png differ diff --git a/sprites/loading.png b/sprites/loading.png new file mode 100644 index 0000000..52dcdb2 Binary files /dev/null and b/sprites/loading.png differ diff --git a/sprites/logo.png b/sprites/logo.png new file mode 100644 index 0000000..c98fd46 Binary files /dev/null and b/sprites/logo.png differ diff --git a/src/main/resources/ocelot/desktop/colorscheme.txt b/src/main/resources/ocelot/desktop/colorscheme.txt index cfa653d..8f99869 100644 --- a/src/main/resources/ocelot/desktop/colorscheme.txt +++ b/src/main/resources/ocelot/desktop/colorscheme.txt @@ -1,3 +1,12 @@ +Accent = #bd353e + +AboutLogo = #ffffffee +AboutForeground = #eeeeee +AboutForegroundSubtle = #eeeeeeee +AboutButtonForeground = #222222 +AboutButtonBorder = #444444 +AboutButtonBackground = #777777 + PortDown = #8382d8 PortUp = #75bdc1 PortNorth = #c8ca5f @@ -23,7 +32,7 @@ ConnectionDel = #7f3333 Connection = #909090 NodeSelectorRing = #4c7f66 -NodeSelectorBackground = #000000BB +NodeSelectorBackground = #000000bb ContextMenuBackground = #222222ee ContextMenuBorder = #444444 diff --git a/src/main/resources/ocelot/desktop/images/spritesheet/spritesheet.png b/src/main/resources/ocelot/desktop/images/spritesheet/spritesheet.png index f2435d1..eb76c76 100644 Binary files a/src/main/resources/ocelot/desktop/images/spritesheet/spritesheet.png and b/src/main/resources/ocelot/desktop/images/spritesheet/spritesheet.png differ diff --git a/src/main/resources/ocelot/desktop/images/spritesheet/spritesheet.txt b/src/main/resources/ocelot/desktop/images/spritesheet/spritesheet.txt index f18e78c..242d019 100644 --- a/src/main/resources/ocelot/desktop/images/spritesheet/spritesheet.txt +++ b/src/main/resources/ocelot/desktop/images/spritesheet/spritesheet.txt @@ -1,295 +1,300 @@ BackgroundPattern 0 0 304 304 -BarSegment 29 499 16 4 -Empty 278 351 16 16 -EmptySlot 256 376 18 18 -Knob 692 0 50 50 -KnobCenter 743 0 50 50 -KnobLimits 794 0 50 50 -ShadowBorder 0 538 1 24 -ShadowCorner 153 351 24 24 -TabArrow 0 484 8 14 -blocks/Generic 295 351 16 16 -blocks/HologramEffect 8 547 4 4 -blocks/HologramProjector1Top 312 351 16 16 -blocks/HologramProjector2Top 329 351 16 16 -blocks/HologramProjectorSide 346 351 16 16 -buttons/BottomDrawerClose 275 376 18 18 -buttons/BottomDrawerOpen 294 376 18 18 -buttons/OpenFMRadioCloseOff 48 510 7 8 -buttons/OpenFMRadioCloseOn 56 510 7 8 -buttons/OpenFMRadioRedstoneOff 3 510 8 8 -buttons/OpenFMRadioRedstoneOn 12 510 8 8 -buttons/OpenFMRadioStartOff 178 351 24 24 -buttons/OpenFMRadioStartOn 203 351 24 24 -buttons/OpenFMRadioStopOff 228 351 24 24 -buttons/OpenFMRadioStopOn 253 351 24 24 -buttons/OpenFMRadioVolumeDownOff 9 484 10 10 -buttons/OpenFMRadioVolumeDownOn 20 484 10 10 -buttons/OpenFMRadioVolumeUpOff 31 484 10 10 -buttons/OpenFMRadioVolumeUpOn 42 484 10 10 -buttons/PowerOff 313 376 18 18 -buttons/PowerOn 332 376 18 18 -buttons/RackRelayOff 153 332 65 18 -buttons/RackRelayOn 219 332 65 18 -icons/Antenna 363 351 16 16 -icons/ArrowRight 380 351 16 16 -icons/AspectRatio 397 351 16 16 -icons/ButtonCheck 153 409 17 17 -icons/ButtonClipboard 171 409 17 17 -icons/ButtonRandomize 189 409 17 17 -icons/CPU 414 351 16 16 -icons/Card 431 351 16 16 -icons/Close 0 451 15 14 -icons/Code 448 351 16 16 -icons/ComponentBus 465 351 16 16 -icons/Copy 482 351 16 16 -icons/Cross 499 351 16 16 -icons/Delete 516 351 16 16 -icons/DragLMB 351 376 21 14 -icons/DragRMB 373 376 21 14 -icons/EEPROM 533 351 16 16 -icons/Edit 550 351 16 16 -icons/Eject 567 351 16 16 -icons/File 584 351 16 16 -icons/Floppy 601 351 16 16 -icons/Folder 618 351 16 16 -icons/FolderSlash 635 351 16 16 -icons/Grid 187 376 22 22 -icons/GridOff 210 376 22 22 -icons/HDD 652 351 16 16 -icons/Home 233 376 22 22 -icons/LMB 84 466 11 14 -icons/Label 669 351 16 16 -icons/LinesHorizontal 686 351 16 16 -icons/Link 703 351 16 16 -icons/LinkSlash 720 351 16 16 -icons/Memory 737 351 16 16 -icons/Microchip 754 351 16 16 -icons/NA 771 351 16 16 -icons/NotificationError 108 466 11 11 -icons/NotificationInfo 120 466 11 11 -icons/NotificationWarning 132 466 11 11 -icons/Pin 39 466 14 14 -icons/Plus 788 351 16 16 -icons/Power 805 351 16 16 -icons/RMB 96 466 11 14 -icons/Restart 822 351 16 16 -icons/Save 839 351 16 16 -icons/SaveAs 856 351 16 16 -icons/Server 873 351 16 16 -icons/SettingsSound 0 466 12 17 -icons/SettingsSystem 13 466 12 17 -icons/SettingsUI 26 466 12 17 -icons/Tier0 890 351 16 16 -icons/Tier1 907 351 16 16 -icons/Tier2 924 351 16 16 -icons/Tiers 941 351 16 16 -icons/Unpin 54 466 14 14 -icons/WaveLFSR 580 434 24 10 -icons/WaveNoise 605 434 24 10 -icons/WaveSawtooth 630 434 24 10 -icons/WaveSine 655 434 24 10 -icons/WaveSquare 680 434 24 10 -icons/WaveTriangle 705 434 24 10 -icons/Window 958 351 16 16 -icons/WireArrowLeft 2 538 4 8 -icons/WireArrowRight 7 538 4 8 -items/APU0 85 332 16 96 -items/APU1 102 332 16 96 -items/APU2 119 332 16 96 -items/CPU0 975 351 16 16 -items/CPU1 992 351 16 16 -items/CPU2 285 332 16 16 -items/CardBase 302 332 16 16 -items/CircuitBoard 319 332 16 16 -items/ComponentBus0 336 332 16 16 -items/ComponentBus1 353 332 16 16 -items/ComponentBus2 370 332 16 16 -items/ComponentBus3 387 332 16 16 -items/DataCard0 0 305 16 128 -items/DataCard1 17 305 16 128 -items/DataCard2 34 305 16 128 -items/DebugCard 404 332 16 16 -items/DiskDriveMountable 421 332 16 16 -items/EEPROM 438 332 16 16 -items/FloppyDisk_dyeBlack 455 332 16 16 -items/FloppyDisk_dyeBlue 472 332 16 16 -items/FloppyDisk_dyeBrown 489 332 16 16 -items/FloppyDisk_dyeCyan 506 332 16 16 -items/FloppyDisk_dyeGray 523 332 16 16 -items/FloppyDisk_dyeGreen 540 332 16 16 -items/FloppyDisk_dyeLightBlue 557 332 16 16 -items/FloppyDisk_dyeLightGray 574 332 16 16 -items/FloppyDisk_dyeLime 591 332 16 16 -items/FloppyDisk_dyeMagenta 608 332 16 16 -items/FloppyDisk_dyeOrange 625 332 16 16 -items/FloppyDisk_dyePink 642 332 16 16 -items/FloppyDisk_dyePurple 659 332 16 16 -items/FloppyDisk_dyeRed 676 332 16 16 -items/FloppyDisk_dyeWhite 693 332 16 16 -items/FloppyDisk_dyeYellow 710 332 16 16 -items/GraphicsCard0 727 332 16 16 -items/GraphicsCard1 744 332 16 16 -items/GraphicsCard2 761 332 16 16 -items/HardDiskDrive0 778 332 16 16 -items/HardDiskDrive1 795 332 16 16 -items/HardDiskDrive2 812 332 16 16 -items/InternetCard 153 376 16 32 -items/LinkedCard 136 332 16 96 -items/Memory0 829 332 16 16 -items/Memory1 846 332 16 16 -items/Memory2 863 332 16 16 -items/Memory3 880 332 16 16 -items/Memory4 897 332 16 16 -items/Memory5 914 332 16 16 -items/Memory6 931 332 16 16 -items/NetworkCard 948 332 16 16 -items/OcelotCard 51 305 16 128 -items/RedstoneCard0 965 332 16 16 -items/RedstoneCard1 982 332 16 16 -items/SelfDestructingCard 170 376 16 32 -items/Server0 999 332 16 16 -items/Server1 152 305 16 16 -items/Server2 169 305 16 16 -items/Server3 186 305 16 16 -items/SoundCard 68 305 16 128 -items/WirelessNetworkCard0 203 305 16 16 -items/WirelessNetworkCard1 220 305 16 16 -light-panel/BookmarkLeft 561 434 18 14 -light-panel/BookmarkRight 207 409 20 14 -light-panel/BorderB 13 547 4 4 -light-panel/BorderL 103 547 4 2 -light-panel/BorderR 18 547 4 4 -light-panel/BorderT 23 547 4 4 -light-panel/CornerBL 28 547 4 4 -light-panel/CornerBR 33 547 4 4 -light-panel/CornerTL 38 547 4 4 -light-panel/CornerTR 43 547 4 4 -light-panel/Fill 6 558 2 2 -light-panel/Vent 0 499 2 38 -nodes/Cable 21 510 8 8 -nodes/Camera 237 305 16 16 -nodes/Chest 69 466 14 14 -nodes/HologramProjector0 254 305 16 16 -nodes/HologramProjector1 271 305 16 16 -nodes/IronNoteBlock 288 305 16 16 -nodes/Lamp 305 305 16 16 -nodes/LampFrame 322 305 16 16 -nodes/LampGlow 305 106 128 128 -nodes/NewNode 339 305 16 16 -nodes/NoteBlock 356 305 16 16 -nodes/OpenFMRadio 373 305 16 16 -nodes/Relay 390 305 16 16 -nodes/computer/Default 407 305 16 16 -nodes/computer/DiskActivity 424 305 16 16 -nodes/computer/Error 441 305 16 16 -nodes/computer/On 458 305 16 16 -nodes/disk-drive/Default 475 305 16 16 -nodes/disk-drive/DiskActivity 492 305 16 16 -nodes/disk-drive/Floppy 509 305 16 16 -nodes/microcontroller/Default 526 305 16 16 -nodes/microcontroller/Error 543 305 16 16 -nodes/microcontroller/On 560 305 16 16 -nodes/rack/Default 577 305 16 16 -nodes/rack/Empty 594 305 16 16 -nodes/rack/drive/0/Default 611 305 16 16 -nodes/rack/drive/0/DiskActivity 628 305 16 16 -nodes/rack/drive/0/Floppy 645 305 16 16 -nodes/rack/drive/1/Default 662 305 16 16 -nodes/rack/drive/1/DiskActivity 679 305 16 16 -nodes/rack/drive/1/Floppy 696 305 16 16 -nodes/rack/drive/2/Default 713 305 16 16 -nodes/rack/drive/2/DiskActivity 730 305 16 16 -nodes/rack/drive/2/Floppy 747 305 16 16 -nodes/rack/drive/3/Default 764 305 16 16 -nodes/rack/drive/3/DiskActivity 781 305 16 16 -nodes/rack/drive/3/Floppy 798 305 16 16 -nodes/rack/drive/Floppy 815 305 16 16 -nodes/rack/server/0/Default 832 305 16 16 -nodes/rack/server/0/DiskActivity 849 305 16 16 -nodes/rack/server/0/Error 866 305 16 16 -nodes/rack/server/0/NetworkActivity 883 305 16 16 -nodes/rack/server/0/On 900 305 16 16 -nodes/rack/server/1/Default 917 305 16 16 -nodes/rack/server/1/DiskActivity 934 305 16 16 -nodes/rack/server/1/Error 951 305 16 16 -nodes/rack/server/1/NetworkActivity 968 305 16 16 -nodes/rack/server/1/On 985 305 16 16 -nodes/rack/server/2/Default 1002 305 16 16 -nodes/rack/server/2/DiskActivity 0 434 16 16 -nodes/rack/server/2/Error 17 434 16 16 -nodes/rack/server/2/NetworkActivity 34 434 16 16 -nodes/rack/server/2/On 51 434 16 16 -nodes/rack/server/3/Default 68 434 16 16 -nodes/rack/server/3/DiskActivity 85 434 16 16 -nodes/rack/server/3/Error 102 434 16 16 -nodes/rack/server/3/NetworkActivity 119 434 16 16 -nodes/rack/server/3/On 136 434 16 16 -nodes/raid/0/DiskActivity 153 434 16 16 -nodes/raid/0/Error 170 434 16 16 -nodes/raid/1/DiskActivity 187 434 16 16 -nodes/raid/1/Error 204 434 16 16 -nodes/raid/2/DiskActivity 221 434 16 16 -nodes/raid/2/Error 238 434 16 16 -nodes/raid/Default 255 434 16 16 -nodes/screen/BottomLeft 272 434 16 16 -nodes/screen/BottomMiddle 289 434 16 16 -nodes/screen/BottomRight 306 434 16 16 -nodes/screen/ColumnBottom 323 434 16 16 -nodes/screen/ColumnMiddle 340 434 16 16 -nodes/screen/ColumnTop 357 434 16 16 -nodes/screen/Middle 374 434 16 16 -nodes/screen/MiddleLeft 391 434 16 16 -nodes/screen/MiddleRight 408 434 16 16 -nodes/screen/PowerOnOverlay 425 434 16 16 -nodes/screen/RowLeft 442 434 16 16 -nodes/screen/RowMiddle 459 434 16 16 -nodes/screen/RowRight 476 434 16 16 -nodes/screen/Standalone 493 434 16 16 -nodes/screen/TopLeft 510 434 16 16 -nodes/screen/TopMiddle 527 434 16 16 -nodes/screen/TopRight 544 434 16 16 -panel/BorderB 48 547 4 4 -panel/BorderL 108 547 4 2 -panel/BorderR 53 547 4 4 -panel/BorderT 58 547 4 4 -panel/CornerBL 63 547 4 4 -panel/CornerBR 68 547 4 4 -panel/CornerTL 73 547 4 4 -panel/CornerTR 78 547 4 4 -panel/Fill 9 558 2 2 -particles/Note 21 499 7 10 -screen/BorderB 5 547 2 8 -screen/BorderT 2 547 2 10 -screen/CornerBL 30 510 8 8 -screen/CornerBR 39 510 8 8 -screen/CornerTL 3 499 8 10 -screen/CornerTR 12 499 8 10 -window/BorderDark 2 558 1 4 -window/BorderLight 4 558 1 4 -window/CornerBL 83 547 4 4 -window/CornerBR 88 547 4 4 -window/CornerTL 93 547 4 4 -window/CornerTR 98 547 4 4 -window/OpenFMRadio 305 0 232 105 -window/case/Motherboard 612 0 79 70 -window/rack/Lines 538 0 73 91 -window/rack/Motherboard 434 106 100 78 -window/rack/NetworkBack 20 552 1 2 -window/rack/NetworkBottom 22 552 1 2 -window/rack/NetworkConnector 24 552 1 2 -window/rack/NetworkLeft 26 552 1 2 -window/rack/NetworkRight 28 552 1 2 -window/rack/NetworkTop 30 552 1 2 -window/rack/NodeBack 113 547 5 1 -window/rack/NodeBottom 119 547 5 1 -window/rack/NodeLeft 125 547 5 1 -window/rack/NodeRight 131 547 5 1 -window/rack/NodeTop 137 547 5 1 -window/rack/SideBack 8 552 1 3 -window/rack/SideBottom 10 552 1 3 -window/rack/SideConnector 12 552 1 3 -window/rack/SideLeft 14 552 1 3 -window/rack/SideRight 16 552 1 3 -window/rack/SideTop 18 552 1 3 -window/raid/Slots 85 305 66 26 +BarSegment 385 434 16 4 +Empty 327 572 16 16 +EmptySlot 305 597 18 18 +Knob 203 434 50 50 +KnobCenter 254 434 50 50 +KnobLimits 305 434 50 50 +Loading 0 305 48 448 +Logo 305 0 168 200 +ShadowBorder 49 720 1 24 +ShadowCorner 202 572 24 24 +TabArrow 49 705 8 14 +blocks/Generic 344 572 16 16 +blocks/HologramEffect 57 729 4 4 +blocks/HologramProjector1Top 361 572 16 16 +blocks/HologramProjector2Top 378 572 16 16 +blocks/HologramProjectorSide 395 572 16 16 +buttons/BottomDrawerClose 324 597 18 18 +buttons/BottomDrawerOpen 343 597 18 18 +buttons/OpenFMRadioCloseOff 404 445 7 8 +buttons/OpenFMRadioCloseOn 412 445 7 8 +buttons/OpenFMRadioRedstoneOff 359 445 8 8 +buttons/OpenFMRadioRedstoneOn 368 445 8 8 +buttons/OpenFMRadioStartOff 227 572 24 24 +buttons/OpenFMRadioStartOn 252 572 24 24 +buttons/OpenFMRadioStopOff 277 572 24 24 +buttons/OpenFMRadioStopOn 302 572 24 24 +buttons/OpenFMRadioVolumeDownOff 58 705 10 10 +buttons/OpenFMRadioVolumeDownOn 69 705 10 10 +buttons/OpenFMRadioVolumeUpOff 80 705 10 10 +buttons/OpenFMRadioVolumeUpOn 91 705 10 10 +buttons/PowerOff 362 597 18 18 +buttons/PowerOn 381 597 18 18 +buttons/RackRelayOff 202 553 65 18 +buttons/RackRelayOn 268 553 65 18 +icons/Antenna 412 572 16 16 +icons/ArrowRight 429 572 16 16 +icons/AspectRatio 446 572 16 16 +icons/Book 463 572 16 16 +icons/ButtonCheck 202 630 17 17 +icons/ButtonClipboard 220 630 17 17 +icons/ButtonRandomize 238 630 17 17 +icons/CPU 480 572 16 16 +icons/Card 497 572 16 16 +icons/Close 49 672 15 14 +icons/Code 514 572 16 16 +icons/ComponentBus 531 572 16 16 +icons/Copy 548 572 16 16 +icons/Cross 565 572 16 16 +icons/Delete 582 572 16 16 +icons/DragLMB 400 597 21 14 +icons/DragRMB 422 597 21 14 +icons/EEPROM 599 572 16 16 +icons/Edit 616 572 16 16 +icons/Eject 633 572 16 16 +icons/File 650 572 16 16 +icons/Floppy 667 572 16 16 +icons/Folder 684 572 16 16 +icons/FolderSlash 701 572 16 16 +icons/Grid 236 597 22 22 +icons/GridOff 259 597 22 22 +icons/HDD 718 572 16 16 +icons/Help 735 572 16 16 +icons/Home 282 597 22 22 +icons/LMB 133 687 11 14 +icons/Label 752 572 16 16 +icons/LinesHorizontal 769 572 16 16 +icons/Link 786 572 16 16 +icons/LinkSlash 803 572 16 16 +icons/Memory 820 572 16 16 +icons/Microchip 837 572 16 16 +icons/NA 854 572 16 16 +icons/NotificationError 157 687 11 11 +icons/NotificationInfo 169 687 11 11 +icons/NotificationWarning 181 687 11 11 +icons/Ocelot 871 572 16 16 +icons/Pin 88 687 14 14 +icons/Plus 888 572 16 16 +icons/Power 905 572 16 16 +icons/RMB 145 687 11 14 +icons/Restart 922 572 16 16 +icons/Save 939 572 16 16 +icons/SaveAs 956 572 16 16 +icons/Server 973 572 16 16 +icons/SettingsSound 49 687 12 17 +icons/SettingsSystem 62 687 12 17 +icons/SettingsUI 75 687 12 17 +icons/Tier0 990 572 16 16 +icons/Tier1 1007 572 16 16 +icons/Tier2 334 553 16 16 +icons/Tiers 351 553 16 16 +icons/Unpin 103 687 14 14 +icons/WaveLFSR 816 655 24 10 +icons/WaveNoise 841 655 24 10 +icons/WaveSawtooth 866 655 24 10 +icons/WaveSine 891 655 24 10 +icons/WaveSquare 916 655 24 10 +icons/WaveTriangle 941 655 24 10 +icons/Window 368 553 16 16 +icons/WireArrowLeft 51 720 4 8 +icons/WireArrowRight 56 720 4 8 +items/APU0 134 553 16 96 +items/APU1 151 553 16 96 +items/APU2 168 553 16 96 +items/CPU0 385 553 16 16 +items/CPU1 402 553 16 16 +items/CPU2 419 553 16 16 +items/CardBase 436 553 16 16 +items/CircuitBoard 453 553 16 16 +items/ComponentBus0 470 553 16 16 +items/ComponentBus1 487 553 16 16 +items/ComponentBus2 504 553 16 16 +items/ComponentBus3 521 553 16 16 +items/DataCard0 49 526 16 128 +items/DataCard1 66 526 16 128 +items/DataCard2 83 526 16 128 +items/DebugCard 538 553 16 16 +items/DiskDriveMountable 555 553 16 16 +items/EEPROM 572 553 16 16 +items/FloppyDisk_dyeBlack 589 553 16 16 +items/FloppyDisk_dyeBlue 606 553 16 16 +items/FloppyDisk_dyeBrown 623 553 16 16 +items/FloppyDisk_dyeCyan 640 553 16 16 +items/FloppyDisk_dyeGray 657 553 16 16 +items/FloppyDisk_dyeGreen 674 553 16 16 +items/FloppyDisk_dyeLightBlue 691 553 16 16 +items/FloppyDisk_dyeLightGray 708 553 16 16 +items/FloppyDisk_dyeLime 725 553 16 16 +items/FloppyDisk_dyeMagenta 742 553 16 16 +items/FloppyDisk_dyeOrange 759 553 16 16 +items/FloppyDisk_dyePink 776 553 16 16 +items/FloppyDisk_dyePurple 793 553 16 16 +items/FloppyDisk_dyeRed 810 553 16 16 +items/FloppyDisk_dyeWhite 827 553 16 16 +items/FloppyDisk_dyeYellow 844 553 16 16 +items/GraphicsCard0 861 553 16 16 +items/GraphicsCard1 878 553 16 16 +items/GraphicsCard2 895 553 16 16 +items/HardDiskDrive0 912 553 16 16 +items/HardDiskDrive1 929 553 16 16 +items/HardDiskDrive2 946 553 16 16 +items/InternetCard 202 597 16 32 +items/LinkedCard 185 553 16 96 +items/Memory0 963 553 16 16 +items/Memory1 980 553 16 16 +items/Memory2 997 553 16 16 +items/Memory3 201 526 16 16 +items/Memory4 218 526 16 16 +items/Memory5 235 526 16 16 +items/Memory6 252 526 16 16 +items/NetworkCard 269 526 16 16 +items/OcelotCard 100 526 16 128 +items/RedstoneCard0 286 526 16 16 +items/RedstoneCard1 303 526 16 16 +items/SelfDestructingCard 219 597 16 32 +items/Server0 320 526 16 16 +items/Server1 337 526 16 16 +items/Server2 354 526 16 16 +items/Server3 371 526 16 16 +items/SoundCard 117 526 16 128 +items/WirelessNetworkCard0 388 526 16 16 +items/WirelessNetworkCard1 405 526 16 16 +light-panel/BookmarkLeft 797 655 18 14 +light-panel/BookmarkRight 256 630 20 14 +light-panel/BorderB 62 729 4 4 +light-panel/BorderL 152 729 4 2 +light-panel/BorderR 67 729 4 4 +light-panel/BorderT 72 729 4 4 +light-panel/CornerBL 77 729 4 4 +light-panel/CornerBR 82 729 4 4 +light-panel/CornerTL 87 729 4 4 +light-panel/CornerTR 92 729 4 4 +light-panel/Fill 55 740 2 2 +light-panel/Vent 356 434 2 38 +nodes/Cable 377 445 8 8 +nodes/Camera 422 526 16 16 +nodes/Chest 118 687 14 14 +nodes/HologramProjector0 439 526 16 16 +nodes/HologramProjector1 456 526 16 16 +nodes/IronNoteBlock 473 526 16 16 +nodes/Lamp 490 526 16 16 +nodes/LampFrame 507 526 16 16 +nodes/LampGlow 49 305 128 128 +nodes/NewNode 524 526 16 16 +nodes/NoteBlock 541 526 16 16 +nodes/OpenFMRadio 558 526 16 16 +nodes/Relay 575 526 16 16 +nodes/computer/Default 592 526 16 16 +nodes/computer/DiskActivity 609 526 16 16 +nodes/computer/Error 626 526 16 16 +nodes/computer/On 643 526 16 16 +nodes/disk-drive/Default 660 526 16 16 +nodes/disk-drive/DiskActivity 677 526 16 16 +nodes/disk-drive/Floppy 694 526 16 16 +nodes/microcontroller/Default 711 526 16 16 +nodes/microcontroller/Error 728 526 16 16 +nodes/microcontroller/On 745 526 16 16 +nodes/rack/Default 762 526 16 16 +nodes/rack/Empty 779 526 16 16 +nodes/rack/drive/0/Default 796 526 16 16 +nodes/rack/drive/0/DiskActivity 813 526 16 16 +nodes/rack/drive/0/Floppy 830 526 16 16 +nodes/rack/drive/1/Default 847 526 16 16 +nodes/rack/drive/1/DiskActivity 864 526 16 16 +nodes/rack/drive/1/Floppy 881 526 16 16 +nodes/rack/drive/2/Default 898 526 16 16 +nodes/rack/drive/2/DiskActivity 915 526 16 16 +nodes/rack/drive/2/Floppy 932 526 16 16 +nodes/rack/drive/3/Default 949 526 16 16 +nodes/rack/drive/3/DiskActivity 966 526 16 16 +nodes/rack/drive/3/Floppy 983 526 16 16 +nodes/rack/drive/Floppy 1000 526 16 16 +nodes/rack/server/0/Default 49 655 16 16 +nodes/rack/server/0/DiskActivity 66 655 16 16 +nodes/rack/server/0/Error 83 655 16 16 +nodes/rack/server/0/NetworkActivity 100 655 16 16 +nodes/rack/server/0/On 117 655 16 16 +nodes/rack/server/1/Default 134 655 16 16 +nodes/rack/server/1/DiskActivity 151 655 16 16 +nodes/rack/server/1/Error 168 655 16 16 +nodes/rack/server/1/NetworkActivity 185 655 16 16 +nodes/rack/server/1/On 202 655 16 16 +nodes/rack/server/2/Default 219 655 16 16 +nodes/rack/server/2/DiskActivity 236 655 16 16 +nodes/rack/server/2/Error 253 655 16 16 +nodes/rack/server/2/NetworkActivity 270 655 16 16 +nodes/rack/server/2/On 287 655 16 16 +nodes/rack/server/3/Default 304 655 16 16 +nodes/rack/server/3/DiskActivity 321 655 16 16 +nodes/rack/server/3/Error 338 655 16 16 +nodes/rack/server/3/NetworkActivity 355 655 16 16 +nodes/rack/server/3/On 372 655 16 16 +nodes/raid/0/DiskActivity 389 655 16 16 +nodes/raid/0/Error 406 655 16 16 +nodes/raid/1/DiskActivity 423 655 16 16 +nodes/raid/1/Error 440 655 16 16 +nodes/raid/2/DiskActivity 457 655 16 16 +nodes/raid/2/Error 474 655 16 16 +nodes/raid/Default 491 655 16 16 +nodes/screen/BottomLeft 508 655 16 16 +nodes/screen/BottomMiddle 525 655 16 16 +nodes/screen/BottomRight 542 655 16 16 +nodes/screen/ColumnBottom 559 655 16 16 +nodes/screen/ColumnMiddle 576 655 16 16 +nodes/screen/ColumnTop 593 655 16 16 +nodes/screen/Middle 610 655 16 16 +nodes/screen/MiddleLeft 627 655 16 16 +nodes/screen/MiddleRight 644 655 16 16 +nodes/screen/PowerOnOverlay 661 655 16 16 +nodes/screen/RowLeft 678 655 16 16 +nodes/screen/RowMiddle 695 655 16 16 +nodes/screen/RowRight 712 655 16 16 +nodes/screen/Standalone 729 655 16 16 +nodes/screen/TopLeft 746 655 16 16 +nodes/screen/TopMiddle 763 655 16 16 +nodes/screen/TopRight 780 655 16 16 +panel/BorderB 97 729 4 4 +panel/BorderL 157 729 4 2 +panel/BorderR 102 729 4 4 +panel/BorderT 107 729 4 4 +panel/CornerBL 112 729 4 4 +panel/CornerBR 117 729 4 4 +panel/CornerTL 122 729 4 4 +panel/CornerTR 127 729 4 4 +panel/Fill 58 740 2 2 +particles/Note 377 434 7 10 +screen/BorderB 54 729 2 8 +screen/BorderT 51 729 2 10 +screen/CornerBL 386 445 8 8 +screen/CornerBR 395 445 8 8 +screen/CornerTL 359 434 8 10 +screen/CornerTR 368 434 8 10 +window/BorderDark 51 740 1 4 +window/BorderLight 53 740 1 4 +window/CornerBL 132 729 4 4 +window/CornerBR 137 729 4 4 +window/CornerTL 142 729 4 4 +window/CornerTR 147 729 4 4 +window/OpenFMRadio 474 0 232 105 +window/case/Motherboard 123 434 79 70 +window/rack/Lines 49 434 73 91 +window/rack/Motherboard 178 305 100 78 +window/rack/NetworkBack 69 734 1 2 +window/rack/NetworkBottom 71 734 1 2 +window/rack/NetworkConnector 73 734 1 2 +window/rack/NetworkLeft 75 734 1 2 +window/rack/NetworkRight 77 734 1 2 +window/rack/NetworkTop 79 734 1 2 +window/rack/NodeBack 162 729 5 1 +window/rack/NodeBottom 168 729 5 1 +window/rack/NodeLeft 174 729 5 1 +window/rack/NodeRight 180 729 5 1 +window/rack/NodeTop 186 729 5 1 +window/rack/SideBack 57 734 1 3 +window/rack/SideBottom 59 734 1 3 +window/rack/SideConnector 61 734 1 3 +window/rack/SideLeft 63 734 1 3 +window/rack/SideRight 65 734 1 3 +window/rack/SideTop 67 734 1 3 +window/raid/Slots 134 526 66 26 diff --git a/src/main/scala/ocelot/desktop/ColorScheme.scala b/src/main/scala/ocelot/desktop/ColorScheme.scala index 35c52c8..75cd406 100644 --- a/src/main/scala/ocelot/desktop/ColorScheme.scala +++ b/src/main/scala/ocelot/desktop/ColorScheme.scala @@ -6,11 +6,13 @@ import ocelot.desktop.util.Logging import scala.collection.mutable import scala.io.Source -object ColorScheme extends Logging { +class ColorScheme extends Logging { private val entries = new mutable.HashMap[String, RGBAColorNorm]() def apply(key: String): RGBAColorNorm = entries(key) + def add(key: String, color: RGBAColorNorm): Unit = entries.addOne((key, color)) + def load(source: Source): Unit = { logger.info(s"Loading color scheme") @@ -36,3 +38,11 @@ object ColorScheme extends Logging { logger.info(s"Loaded ${entries.size - oldSize} colors") } } + +object ColorScheme { + val General = new ColorScheme + + def load(source: Source): Unit = General.load(source) + + def apply(key: String): RGBAColorNorm = General(key) +} diff --git a/src/main/scala/ocelot/desktop/OcelotDesktop.scala b/src/main/scala/ocelot/desktop/OcelotDesktop.scala index 653a0e7..1a3638c 100644 --- a/src/main/scala/ocelot/desktop/OcelotDesktop.scala +++ b/src/main/scala/ocelot/desktop/OcelotDesktop.scala @@ -6,7 +6,6 @@ import ocelot.desktop.ui.UiHandler import ocelot.desktop.ui.swing.SplashScreen import ocelot.desktop.ui.widget._ import ocelot.desktop.ui.widget.modal.notification.{NotificationDialog, NotificationType} -import ocelot.desktop.ui.widget.settings.SettingsDialog import ocelot.desktop.util.CommandLine.Argument import ocelot.desktop.util.FileUtils.getOcelotConfigDirectory import ocelot.desktop.util._ @@ -372,8 +371,6 @@ object OcelotDesktop extends Logging { } } - def showSettings(): Unit = new SettingsDialog().show() - def exit(): Unit = showCloseConfirmationDialog() { UiHandler.exit() } diff --git a/src/main/scala/ocelot/desktop/audio/AL10W.scala b/src/main/scala/ocelot/desktop/audio/AL10W.scala index 26c7d85..3b0c7ee 100644 --- a/src/main/scala/ocelot/desktop/audio/AL10W.scala +++ b/src/main/scala/ocelot/desktop/audio/AL10W.scala @@ -1,7 +1,7 @@ package ocelot.desktop.audio import ocelot.desktop.Settings -import ocelot.desktop.util.{Logging, OpenAlException} +import ocelot.desktop.util.Logging import org.lwjgl.openal.AL10 import java.nio.{ByteBuffer, IntBuffer} diff --git a/src/main/scala/ocelot/desktop/audio/Audio.scala b/src/main/scala/ocelot/desktop/audio/Audio.scala index bb01d83..1002152 100644 --- a/src/main/scala/ocelot/desktop/audio/Audio.scala +++ b/src/main/scala/ocelot/desktop/audio/Audio.scala @@ -1,7 +1,7 @@ package ocelot.desktop.audio import ocelot.desktop.Settings -import ocelot.desktop.util.{Logging, OpenAlException, Transaction} +import ocelot.desktop.util.{Logging, Transaction} import org.lwjgl.LWJGLException import org.lwjgl.openal.{AL, AL10, ALC10} diff --git a/src/main/scala/ocelot/desktop/util/OpenAlException.scala b/src/main/scala/ocelot/desktop/audio/OpenAlException.scala similarity index 92% rename from src/main/scala/ocelot/desktop/util/OpenAlException.scala rename to src/main/scala/ocelot/desktop/audio/OpenAlException.scala index 5ff4a83..d301d1d 100644 --- a/src/main/scala/ocelot/desktop/util/OpenAlException.scala +++ b/src/main/scala/ocelot/desktop/audio/OpenAlException.scala @@ -1,4 +1,4 @@ -package ocelot.desktop.util +package ocelot.desktop.audio import scala.util.control import scala.util.control.Exception.Catch diff --git a/src/main/scala/ocelot/desktop/audio/SoundBuffer.scala b/src/main/scala/ocelot/desktop/audio/SoundBuffer.scala index bacd9f4..d11dd7f 100644 --- a/src/main/scala/ocelot/desktop/audio/SoundBuffer.scala +++ b/src/main/scala/ocelot/desktop/audio/SoundBuffer.scala @@ -1,6 +1,6 @@ package ocelot.desktop.audio -import ocelot.desktop.util.{FileUtils, Logging, OpenAlException, Resource} +import ocelot.desktop.util.{FileUtils, Logging, Resource} import org.lwjgl.openal.AL10 class SoundBuffer(val file: String) extends Resource with Logging { diff --git a/src/main/scala/ocelot/desktop/audio/SoundSamples.scala b/src/main/scala/ocelot/desktop/audio/SoundSamples.scala index 6fddac2..db8d215 100644 --- a/src/main/scala/ocelot/desktop/audio/SoundSamples.scala +++ b/src/main/scala/ocelot/desktop/audio/SoundSamples.scala @@ -1,6 +1,6 @@ package ocelot.desktop.audio -import ocelot.desktop.util.{Logging, OpenAlException} +import ocelot.desktop.util.Logging import org.lwjgl.openal.AL10 import java.nio.ByteBuffer diff --git a/src/main/scala/ocelot/desktop/graphics/Graphics.scala b/src/main/scala/ocelot/desktop/graphics/Graphics.scala index 4362f8b..b3a2743 100644 --- a/src/main/scala/ocelot/desktop/graphics/Graphics.scala +++ b/src/main/scala/ocelot/desktop/graphics/Graphics.scala @@ -2,6 +2,7 @@ package ocelot.desktop.graphics import ocelot.desktop.color.{Color, RGBAColorNorm} import ocelot.desktop.geometry.{Rect2D, Size2D, Transform2D, Vector2D} +import ocelot.desktop.graphics.IconSource.Animation import ocelot.desktop.graphics.mesh.{Mesh2D, MeshInstance2D, MeshVertex2D} import ocelot.desktop.graphics.render.InstanceRenderer import ocelot.desktop.ui.UiHandler @@ -279,9 +280,14 @@ class Graphics(private var scalingFactor: Float) extends Logging with Resource { sprite(name, pos.x, pos.y, size.width, size.height) } + def sprite(name: String, x: Float, y: Float): Unit = { + val size = Spritesheet.spriteSize(name) + sprite(name, x, y, size.width, size.height) + } + def sprite(name: String, x: Float, y: Float, width: Float, height: Float, color: Color = RGBAColorNorm(1f, 1f, 1f), - animation: Option[IconSource.Animation] = None): Unit = + animation: Option[Animation] = None): Unit = { sprite = name foreground = color @@ -308,23 +314,27 @@ class Graphics(private var scalingFactor: Float) extends Logging with Resource { private def _rect(x: Float, y: Float, width: Float, height: Float, fixUV: Boolean = true, - animation: Option[Array[(Int, Float)]] = None): Unit = { + animation: Option[Animation] = None): Unit = { val spriteRect = animation match { case None => this.spriteRect - case Some(frames) => - val duration = frames.map(_._2).sum + case Some(animation) => + val duration = animation.frames.map(_._2).sum var timeOffset = 0f var curFrame = 0 breakable { - for ((idx, dur) <- frames) { + for ((idx, dur) <- animation.frames) { timeOffset += dur curFrame = idx if (timeOffset >= time % duration) break } } - this.spriteRect.copy(y = this.spriteRect.y + curFrame * this.spriteRect.w, h = this.spriteRect.w) + val size = animation.frameSize match { + case Some(size) => Size2D(this.spriteRect.w, this.spriteRect.w * size.height / size.width) + case None => Size2D(this.spriteRect.w, this.spriteRect.w) + } + this.spriteRect.copy(y = this.spriteRect.y + curFrame * size.height, h = size.height) } val uvTransform = Transform2D.translate(spriteRect.x, spriteRect.y) >> diff --git a/src/main/scala/ocelot/desktop/graphics/IconSource.scala b/src/main/scala/ocelot/desktop/graphics/IconSource.scala index 2ad9937..33d3913 100644 --- a/src/main/scala/ocelot/desktop/graphics/IconSource.scala +++ b/src/main/scala/ocelot/desktop/graphics/IconSource.scala @@ -1,5 +1,6 @@ package ocelot.desktop.graphics +import ocelot.desktop.geometry.Size2D import ocelot.desktop.ui.widget.modal.notification.NotificationType.NotificationType import totoro.ocelot.brain.util.DyeColor import totoro.ocelot.brain.util.ExtendedTier.ExtendedTier @@ -11,8 +12,6 @@ case class IconSource( ) object IconSource { - type Animation = Array[(Int, Float)] - val CardIcon: IconSource = IconSource("icons/Card") val CpuIcon: IconSource = IconSource("icons/CPU") val HddIcon: IconSource = IconSource("icons/HDD") @@ -88,24 +87,36 @@ object IconSource { //noinspection ScalaWeakerAccess object Animations { - val Apu: Animation = Array( + val Apu: Animation = Animation( (0, 3f), (1, 3f), (2, 3f), (3, 3f), (4, 3f), (5, 3f), (4, 3f), (3, 3f), (2, 3f), (1, 3f), (0, 3f)) val LinkedCard: Animation = - Array((0, 3f), (1, 3f), (2, 3f), (3, 3f), (4, 3f), (5, 3f)) + Animation((0, 3f), (1, 3f), (2, 3f), (3, 3f), (4, 3f), (5, 3f)) - val InternetCard: Animation = Array( + val InternetCard: Animation = Animation( (0, 2f), (1, 7f), (0, 5f), (1, 4f), (0, 7f), (1, 2f), (0, 8f), (1, 9f), (0, 6f), (1, 4f)) - val DataCard: Animation = Array( + val DataCard: Animation = Animation( (0, 4f), (1, 4f), (2, 4f), (3, 4f), (4, 4f), (5, 4f), (6, 4f), (7, 4f)) - val SelfDestructingCard: Animation = Array((0, 4f), (1, 4f)) + val SelfDestructingCard: Animation = Animation((0, 4f), (1, 4f)) - val OcelotCard: Animation = Array( + val OcelotCard: Animation = Animation( (0, 12f), (1, 4f), (2, 4f), (3, 4f), (4, 5f), (5, 6f), (0, 9f), (6, 4f), (7, 3f)) + + val Loading: Animation = Animation(Size2D(48, 32), + (0, 0.7f), (1, 0.7f), (2, 0.7f), (3, 0.7f), (4, 0.7f), (5, 0.7f), (6, 0.7f), + (7, 0.7f), (8, 0.7f), (9, 0.7f), (10, 0.7f), (11, 0.7f), (12, 0.7f), (13, 0.7f) + ) + } + + case class Animation(frames: Array[(Int, Float)], frameSize: Option[Size2D]) + object Animation { + def apply(frames: (Int, Float)*) = new Animation(frames.toArray, None) + def apply(size: Size2D, frames: (Int, Float)*) = new Animation(frames.toArray, Some(size)) + def apply(size: Option[Size2D] = None, frames: Array[(Int, Float)]) = new Animation(frames, size) } // ----------------------- Ocelot interface icons ----------------------- @@ -115,6 +126,8 @@ object IconSource { IconSource(s"icons/Notification$notificationType") } + val Loading: IconSource = IconSource("Loading", animation = Some(Animations.Loading)) + val SettingsSystem: IconSource = IconSource("icons/SettingsSystem") val SettingsSound: IconSource = IconSource("icons/SettingsSound") val SettingsUI: IconSource = IconSource("icons/SettingsUI") @@ -142,4 +155,7 @@ object IconSource { val Tiers: IconSource = IconSource("icons/Tiers") val LinesHorizontal: IconSource = IconSource("icons/LinesHorizontal") val ArrowRight: IconSource = IconSource("icons/ArrowRight") + val Book: IconSource = IconSource("icons/Book") + val Help: IconSource = IconSource("icons/Help") + val Ocelot: IconSource = IconSource("icons/Ocelot") } diff --git a/src/main/scala/ocelot/desktop/ui/widget/Button.scala b/src/main/scala/ocelot/desktop/ui/widget/Button.scala index 12ec8a5..63ce2fd 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/Button.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/Button.scala @@ -5,11 +5,14 @@ import ocelot.desktop.audio.SoundSource import ocelot.desktop.color.Color import ocelot.desktop.geometry.Size2D import ocelot.desktop.graphics.Graphics -import ocelot.desktop.ui.event.handlers.ClickHandler -import ocelot.desktop.ui.event.{ClickEvent, MouseEvent} +import ocelot.desktop.ui.event.handlers.{ClickHandler, HoverHandler} +import ocelot.desktop.ui.event.{ClickEvent, HoverEvent, MouseEvent} +import ocelot.desktop.ui.widget.tooltip.Tooltip import ocelot.desktop.util.DrawUtils -class Button extends Widget with ClickHandler with ClickSoundSource { +class Button(tooltip: Tooltip = null) extends Widget with ClickHandler with HoverHandler with ClickSoundSource { + protected def colorScheme: ColorScheme = ColorScheme.General + def text: String = "" def onClick(): Unit = {} @@ -19,23 +22,37 @@ class Button extends Widget with ClickHandler with ClickSoundSource { eventHandlers += { case ClickEvent(MouseEvent.Button.Left, _) if enabled => onClick() - clickSoundSource.play() } + if (tooltip != null) { + eventHandlers += { + case HoverEvent(HoverEvent.State.Enter) => onHoverEnter() + case HoverEvent(HoverEvent.State.Leave) => onHoverLeave() + } + } + + private def onHoverEnter(): Unit = { + root.get.tooltipPool.addTooltip(tooltip) + } + + private def onHoverLeave(): Unit = { + root.get.tooltipPool.closeTooltip(tooltip) + } + override def minimumSize: Size2D = Size2D(24 + text.length * 8, 24) override def maximumSize: Size2D = minimumSize override def draw(g: Graphics): Unit = { val (background, border, foreground) = if (enabled) ( - ColorScheme("ButtonBackground"), - ColorScheme("ButtonBorder"), - ColorScheme("ButtonForeground") + colorScheme("ButtonBackground"), + colorScheme("ButtonBorder"), + colorScheme("ButtonForeground") ) else ( - ColorScheme("ButtonBackgroundDisabled"), - ColorScheme("ButtonBorderDisabled"), - ColorScheme("ButtonForegroundDisabled") + colorScheme("ButtonBackgroundDisabled"), + colorScheme("ButtonBorderDisabled"), + colorScheme("ButtonForegroundDisabled") ) g.rect(bounds, background) diff --git a/src/main/scala/ocelot/desktop/ui/widget/ChangeSimulationSpeedDialog.scala b/src/main/scala/ocelot/desktop/ui/widget/ChangeSimulationSpeedDialog.scala index 991ff38..6adb386 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/ChangeSimulationSpeedDialog.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/ChangeSimulationSpeedDialog.scala @@ -24,9 +24,7 @@ class ChangeSimulationSpeedDialog() extends ModalDialog { children :+= new PaddingBox(new Widget { override val layout = new LinearLayout(this, orientation = Orientation.Vertical) - children :+= new PaddingBox(new Label { - override def text = "Change simulation speed" - }, Padding2D(bottom = 16)) + children :+= new PaddingBox(new Label("Change simulation speed"), Padding2D(bottom = 16)) private var inputTPS: TextInput = _ private var inputMSPT: TextInput = _ @@ -73,13 +71,9 @@ class ChangeSimulationSpeedDialog() extends ModalDialog { override def onConfirm(): Unit = confirm() } - children :+= new Label { - override def text = "Milliseconds per tick:" - } + children :+= new Label("Milliseconds per tick:") children :+= new PaddingBox(inputMSPT, Padding2D(bottom = 8)) - children :+= new Label { - override def text = "Ticks per second:" - } + children :+= new Label("Ticks per second:") children :+= new PaddingBox(inputTPS, Padding2D(bottom = 8)) children :+= new Widget { diff --git a/src/main/scala/ocelot/desktop/ui/widget/DiskEditWindow.scala b/src/main/scala/ocelot/desktop/ui/widget/DiskEditWindow.scala index 6e705d4..bdbe79b 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/DiskEditWindow.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/DiskEditWindow.scala @@ -65,9 +65,7 @@ class DiskEditWindow(item: DiskItem) extends PanelWindow { override val layout = new LinearLayout(this, orientation = Orientation.Horizontal, alignItems = AlignItems.Center) - children :+= new Label { - override def text: String = "Label" - } + children :+= new Label("Label") children :+= new TextInput(item.label.getOrElse("")) { override def onConfirm(): Unit = { diff --git a/src/main/scala/ocelot/desktop/ui/widget/Icon.scala b/src/main/scala/ocelot/desktop/ui/widget/Icon.scala index 88260fe..cf7f3eb 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/Icon.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/Icon.scala @@ -5,7 +5,7 @@ import ocelot.desktop.geometry.Size2D import ocelot.desktop.graphics.{Graphics, IconSource} import ocelot.desktop.util.Spritesheet -class Icon(icon: IconSource) extends Widget { +class Icon(icon: IconSource, size: Size2D = null, private val color: Color = Color.White) extends Widget { def this(name: String) { this(IconSource(name)) } @@ -14,18 +14,14 @@ class Icon(icon: IconSource) extends Widget { this(IconSource(name, animation = Some(animation))) } - def iconColor: Color = Color.White - def iconSize: Size2D = Spritesheet.spriteSize(icon.path) - - override def minimumSize: Size2D = { - val size = iconSize - - icon.animation match { - case None => size - case Some(_) => size.copy(height = size.width) - } - } + def iconColor: Color = color + def iconSize: Size2D = + if (size != null) size + else if (icon.animation.isDefined && icon.animation.get.frameSize.isDefined) + icon.animation.get.frameSize.get + else Spritesheet.spriteSize(icon.path) + override def minimumSize: Size2D = iconSize override def maximumSize: Size2D = minimumSize override def draw(g: Graphics): Unit = { diff --git a/src/main/scala/ocelot/desktop/ui/widget/Label.scala b/src/main/scala/ocelot/desktop/ui/widget/Label.scala index a96788d..e3ba28a 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/Label.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/Label.scala @@ -5,11 +5,9 @@ import ocelot.desktop.color.Color import ocelot.desktop.geometry.Size2D import ocelot.desktop.graphics.Graphics -class Label extends Widget { - def text: String = "" - - def isSmall: Boolean = false - +class Label(private val value: String = "", private val small: Boolean = false) extends Widget { + def text: String = value + def isSmall: Boolean = small def color: Color = ColorScheme("Label") private var textWidth = text.length * 8 diff --git a/src/main/scala/ocelot/desktop/ui/widget/MenuBar.scala b/src/main/scala/ocelot/desktop/ui/widget/MenuBar.scala index e3f8b9d..793369a 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/MenuBar.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/MenuBar.scala @@ -5,6 +5,8 @@ import ocelot.desktop.geometry.{Padding2D, Size2D} import ocelot.desktop.graphics.{Graphics, IconSource} import ocelot.desktop.ui.event.KeyEvent import ocelot.desktop.ui.widget.contextmenu.{ContextMenuEntry, ContextMenuSubmenu} +import ocelot.desktop.ui.widget.help.{AboutDialog, UpdateCheckerDialog} +import ocelot.desktop.ui.widget.settings.SettingsDialog import ocelot.desktop.{ColorScheme, OcelotDesktop} import org.lwjgl.input.Keyboard @@ -43,7 +45,16 @@ class MenuBar extends Widget { }) })) - addEntry(new MenuBarButton("Settings", () => OcelotDesktop.showSettings())) + addEntry(new MenuBarButton("Settings", () => new SettingsDialog().show())) + + addEntry(new MenuBarSubmenu("Help", menu => { + menu.addEntry(ContextMenuEntry("Check for Updates", IconSource.Help) { + new UpdateCheckerDialog().show() + }) + menu.addSeparator() + menu.addEntry(ContextMenuEntry("Manual", IconSource.Book) {}.setEnabled(false)) + menu.addEntry(ContextMenuEntry("About", IconSource.Ocelot) { new AboutDialog().show() }) + })) addEntry(new Widget { override def maximumSize: Size2D = Size2D(Float.PositiveInfinity, 1) diff --git a/src/main/scala/ocelot/desktop/ui/widget/ScreenAspectRatioDialog.scala b/src/main/scala/ocelot/desktop/ui/widget/ScreenAspectRatioDialog.scala index c8d048b..136214e 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/ScreenAspectRatioDialog.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/ScreenAspectRatioDialog.scala @@ -12,12 +12,7 @@ class ScreenAspectRatioDialog(screenNode: ScreenNode) extends ModalDialog { new Widget { override val layout = new LinearLayout(this, orientation = Orientation.Vertical) - children :+= new PaddingBox( - new Label { - override def text = "Set aspect ratio" - }, - Padding2D(bottom = 16) - ) + children :+= new PaddingBox(new Label("Set aspect ratio"), Padding2D(bottom = 16)) private var typedWidth: Option[Int] = Some(screenNode.screen.aspectRatio._1.toInt) private var typedHeight: Option[Int] = Some(screenNode.screen.aspectRatio._2.toInt) @@ -61,9 +56,7 @@ class ScreenAspectRatioDialog(screenNode: ScreenNode) extends ModalDialog { ) } - children :+= new Label { - override def text = "Width:" - } + children :+= new Label("Width:") addInput( 8, @@ -71,9 +64,7 @@ class ScreenAspectRatioDialog(screenNode: ScreenNode) extends ModalDialog { value => typedWidth = value ) - children :+= new Label { - override def text = "Height:" - } + children :+= new Label("Height:") addInput( 6, diff --git a/src/main/scala/ocelot/desktop/ui/widget/TunnelDialog.scala b/src/main/scala/ocelot/desktop/ui/widget/TunnelDialog.scala index cb65bf0..d853350 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/TunnelDialog.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/TunnelDialog.scala @@ -32,9 +32,7 @@ class TunnelDialog( new Widget { override val layout = new LinearLayout(this, orientation = Orientation.Vertical, gap = 8) - children :+= new PaddingBox(new Label { - override def text: String = "Set channel name/code" - }, Padding2D(bottom = 8)) + children :+= new PaddingBox(new Label("Set channel name/code"), Padding2D(bottom = 8)) children :+= new Widget { children :+= input diff --git a/src/main/scala/ocelot/desktop/ui/widget/help/AboutDialog.scala b/src/main/scala/ocelot/desktop/ui/widget/help/AboutDialog.scala new file mode 100644 index 0000000..b2c42e1 --- /dev/null +++ b/src/main/scala/ocelot/desktop/ui/widget/help/AboutDialog.scala @@ -0,0 +1,118 @@ +package ocelot.desktop.ui.widget.help + +import buildinfo.BuildInfo +import ocelot.desktop.ColorScheme +import ocelot.desktop.color.Color +import ocelot.desktop.geometry.{Padding2D, Size2D} +import ocelot.desktop.graphics.Graphics +import ocelot.desktop.ui.layout.LinearLayout +import ocelot.desktop.ui.widget.{Button, Filler, Label, PaddingBox, Widget} +import ocelot.desktop.ui.widget.modal.ModalDialog +import ocelot.desktop.ui.widget.tooltip.{LabelTooltip, Tooltip} +import ocelot.desktop.util.{DrawUtils, Orientation, Spritesheet} + +import java.awt.Desktop +import java.net.URI +import java.time.Year + +class AboutDialog extends ModalDialog { + val WebsiteURL: String = "https://ocelot.fomalhaut.me/desktop" + val IrcURL: String = "https://webchat.esper.net/?join=cc.ru" + val DiscordURL: String = "https://discord.gg/ygxUkxg9pt" + + children :+= new Widget { + private val logo: Widget = new Widget { + override def minimumSize: Size2D = Spritesheet.spriteSize("Logo") + 40 + + override def draw(g: Graphics): Unit = { + g.sprite("Logo", bounds.x + 20, bounds.y + 80, ColorScheme("AboutLogo")) + drawChildren(g) + } + } + children :+= logo + + children :+= new PaddingBox(new Widget { + override val layout = new LinearLayout(this, orientation = Orientation.Vertical, gap = 8) + + children :++= Seq( + new PaddingBox(AboutLabel("OCELOT DESKTOP"), Padding2D(top = 32)), + new PaddingBox(AboutLabel(" OpenComputers Emulator", isSmall = true), Padding2D(bottom = 16)), + + AboutLabel(s"Version: ${BuildInfo.version}"), + new PaddingBox(AboutLabel(s" (${BuildInfo.commit.take(7)})", isSmall = true), Padding2D(bottom = 16)), + + AboutLabel(s"MIT License (c) ${Year.now.getValue} Ocelot Dev Team"), + AboutLabel(" OpenComputers (C) 2013-2015, MIT, Florian \"Sangar\" Nücke", isSmall = true), + AboutLabel(" MineOS (C) 2022, Dungeon MIT License, ECS", isSmall = true), + AboutLabel(" Advanced Loader (C) 2018, MIT, Luca_S", isSmall = true), + AboutLabel(" LuaJ (C) 2007, MIT, Luaj.org", isSmall = true), + AboutLabel(" Unifont (C) 2007, SIL OFL 1.1, Roman Czyborra", isSmall = true), + AboutLabel(" Unscii (Public Domain) by viznut", isSmall = true), + AboutLabel(" wcwidth (C) 2005-2020, MIT, Rich Felker, et al.", isSmall = true), + AboutLabel(" JNLua (C) 2008-2012, MIT, Andre Naef", isSmall = true), + AboutLabel(" LuaJ (C) 2007, MIT, Luaj.org", isSmall = true), + AboutLabel(" Typesafe Config (C) 2011-2012, Apache License 2.0, Typesafe Inc.", isSmall = true), + ) + + children :+= new Filler + + children :+= new Widget { + children :+= new Filler { + override def maximumSize: Size2D = Size2D(Float.PositiveInfinity, 1) + } + + if (Desktop.isDesktopSupported) { + children :+= new PaddingBox(new AboutButton("Website", WebsiteURL), Padding2D(right = 8)) + children :+= new PaddingBox(new AboutButton("IRC", IrcURL), Padding2D(right = 8)) + children :+= new PaddingBox(new AboutButton("Discord", DiscordURL), Padding2D(right = 20)) + } + + children :+= new Button { + override def text: String = "Ok" + override def onClick(): Unit = close() + override protected def colorScheme: ColorScheme = customButtonColorScheme + } + } + }, Padding2D.equal(18)) + + override def draw(g: Graphics): Unit = { + g.rect(bounds.x, bounds.y, logo.bounds.w, bounds.h, ColorScheme("Accent")) + super.draw(g) + } + } + + protected override def drawInner(g: Graphics): Unit = { + DrawUtils.shadow(g, bounds.x - 8, bounds.y - 8, bounds.w + 16, bounds.h + 20, 0.5f) + g.rect(bounds, ColorScheme("ContextMenuBackground")) + DrawUtils.ring(g, bounds.x, bounds.y, bounds.w, bounds.h, 1, ColorScheme("ContextMenuBorder")) + + drawChildren(g) + } + + private def customButtonColorScheme = { + val scheme = new ColorScheme() + scheme.add("ButtonBackground", ColorScheme("AboutButtonBackground")) + scheme.add("ButtonBorder", ColorScheme("AboutButtonBorder")) + scheme.add("ButtonForeground", ColorScheme("AboutButtonForeground")) + scheme.add("ButtonBackgroundDisabled", ColorScheme("ButtonBackgroundDisabled")) + scheme.add("ButtonBorderDisabled", ColorScheme("ButtonBorderDisabled")) + scheme.add("ButtonForegroundDisabled", ColorScheme("ButtonForegroundDisabled")) + scheme + } + + private class AboutButton(val label: String, val url: String) extends Button(new LabelTooltip(url)) { + override def text: String = label + override def onClick(): Unit = Desktop.getDesktop.browse(new URI(url)) + override protected def colorScheme: ColorScheme = customButtonColorScheme + } + + private class AboutLabel(private val _text: String, private val _isSmall: Boolean = false) extends Label { + override def text: String = _text + override def isSmall: Boolean = _isSmall + override def color: Color = if (isSmall) ColorScheme("AboutForegroundSubtle") else ColorScheme("AboutForeground") + } + + private object AboutLabel { + def apply(text: String, isSmall: Boolean = false): AboutLabel = new AboutLabel(text, isSmall) + } +} diff --git a/src/main/scala/ocelot/desktop/ui/widget/help/UpdateCheckerDialog.scala b/src/main/scala/ocelot/desktop/ui/widget/help/UpdateCheckerDialog.scala new file mode 100644 index 0000000..99f9169 --- /dev/null +++ b/src/main/scala/ocelot/desktop/ui/widget/help/UpdateCheckerDialog.scala @@ -0,0 +1,113 @@ +package ocelot.desktop.ui.widget.help + +import buildinfo.BuildInfo +import ocelot.desktop.ColorScheme +import ocelot.desktop.geometry.{Padding2D, Size2D} +import ocelot.desktop.graphics.IconSource +import ocelot.desktop.ui.layout.{Layout, LinearLayout} +import ocelot.desktop.ui.widget.{Button, Filler, Icon, Label, PaddingBox, Widget} +import ocelot.desktop.ui.widget.modal.ModalDialog +import ocelot.desktop.util.Orientation.Vertical +import ocelot.desktop.util.{Logging, OcelotOnlineAPI} + +import java.awt.Desktop +import java.net.URI +import java.time.ZoneId +import java.time.format.DateTimeFormatter +import scala.collection.immutable.ArraySeq +import scala.util.{Failure, Success} + +class UpdateCheckerDialog extends ModalDialog with Logging { + private val dateFormat = DateTimeFormatter.ofPattern("dd MMM YYYY (HH:mm:ss Z)") + + private val container = new Widget { + override protected val layout: Layout = new LinearLayout(this, orientation = Vertical) + } + + children :+= new PaddingBox(new Widget { + override protected val layout: Layout = new LinearLayout(this, orientation = Vertical, gap = 16) + + children :+= container + + children :+= new Widget { + children :+= new Filler { + override def maximumSize: Size2D = Size2D(Float.PositiveInfinity, 1) + } + children :+= new Button { + override def text: String = "Ok" + override def onClick(): Unit = close() + } + } + }, Padding2D.equal(16)) + + // loading screen + container.children :+= new Label("Checking development news...") + container.children :+= new PaddingBox(new Icon(IconSource.Loading, color = ColorScheme("Label")), Padding2D(16, 0, 0, 90)) + + // check the API + OcelotOnlineAPI.checkRemoteVersion { + case Success(version) => + setContainerChildren(ArraySeq.empty) + if (version.releaseVersion.isDefined) { + val newReleaseVersion = version.releaseVersion.get.drop(1) + container.children :+= new Label(s"Release: ${BuildInfo.version} ▸ ${newReleaseVersion}") + if (newReleaseVersion == BuildInfo.version) { + container.children :+= new PaddingBox(new Label("Up to date!", small = true), Padding2D(6)) + } else { + container.children :+= new PaddingBox(new Label("New release version is available:", small = true), Padding2D(6)) + if (Desktop.isDesktopSupported) { + container.children :+= new PaddingBox(new Button { + override def text: String = "Download" + + override def onClick(): Unit = Desktop.getDesktop.browse(new URI("https://ocelot.fomalhaut.me/desktop")) + }, Padding2D(6)) + } else { + container.children :+= new PaddingBox(new Label("https://ocelot.fomalhaut.me/desktop", small = true), Padding2D(6)) + } + } + } else { + container.children :+= new Label("Release: N\\A") + } + + container.children :+= new Filler { + override def minimumSize: Size2D = Size2D(8, 16) + } + + if (version.devId.isDefined && version.devDate.isDefined) { + val newDevVersion = version.devId.get.take(7) + container.children :+= new Label(s"Dev build: ${BuildInfo.commit.take(7)} ▸ ${newDevVersion}") + if (BuildInfo.commit.take(7) == newDevVersion) { + container.children :+= new PaddingBox(new Label("Up to date!", small = true), Padding2D(6)) + } else { + container.children :+= new PaddingBox( + new Label(s"New dev build from ${version.devDate.get.withZoneSameInstant(ZoneId.systemDefault()).format(dateFormat)}:", small = true) + , Padding2D(4)) + if (Desktop.isDesktopSupported) { + container.children :+= new PaddingBox(new Button { + override def text: String = "Download" + + override def onClick(): Unit = Desktop.getDesktop.browse(new URI("https://ocelot.fomalhaut.me/desktop")) + }, Padding2D(6)) + } else { + container.children :+= new PaddingBox(new Label("https://ocelot.fomalhaut.me/desktop", small = true), Padding2D(6)) + } + } + } else { + container.children :+= new Label("Dev build: N\\A") + } + + case Failure(exception) => + logger.error("Cannot download information about new Ocelot version!", exception) + val message = "Ocelot website is unavailable...\n" + + s"($exception)\n" + + "Check the log file for a full stacktrace." + setContainerChildren(ArraySeq.unsafeWrapArray(message.split('\n').map(line => new Label { + override def text: String = line + }))) + } + + private def setContainerChildren(children: ArraySeq[Widget]): Unit = { + container.children.foreach(it => { it.parent = None; it.root = None }) + container.children = children + } +} diff --git a/src/main/scala/ocelot/desktop/ui/widget/modal/ModalDialog.scala b/src/main/scala/ocelot/desktop/ui/widget/modal/ModalDialog.scala index 4853e34..1866c92 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/modal/ModalDialog.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/modal/ModalDialog.scala @@ -9,7 +9,6 @@ import ocelot.desktop.util.animation.UnitAnimation import org.lwjgl.input.Keyboard abstract class ModalDialog(val autoClose: Boolean = true) extends Widget { - protected def dialogPool: ModalDialogPool = parent.get.asInstanceOf[ModalDialogPool] protected val openCloseAnimation: UnitAnimation = UnitAnimation.easeInOutQuad(0.13f) if (autoClose) { diff --git a/src/main/scala/ocelot/desktop/ui/widget/modal/notification/NotificationDialog.scala b/src/main/scala/ocelot/desktop/ui/widget/modal/notification/NotificationDialog.scala index 2cf4039..e08d60e 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/modal/notification/NotificationDialog.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/modal/notification/NotificationDialog.scala @@ -2,7 +2,7 @@ package ocelot.desktop.ui.widget.modal.notification import ocelot.desktop.ColorScheme import ocelot.desktop.color.Color -import ocelot.desktop.geometry.Padding2D +import ocelot.desktop.geometry.{Padding2D, Size2D} import ocelot.desktop.graphics.{Graphics, IconSource} import ocelot.desktop.ui.layout.LinearLayout import ocelot.desktop.ui.widget._ @@ -11,7 +11,7 @@ import ocelot.desktop.ui.widget.modal.notification.NotificationType.Notification import ocelot.desktop.util.{DrawUtils, Orientation} class NotificationDialog(message: String, notificationType: NotificationType = NotificationType.Warning) extends ModalDialog { - protected val buttonsLayout: Widget = new Widget { + private val buttonsLayout: Widget = new Widget { override val layout: LinearLayout = new LinearLayout(this, gap = 15) children :+= new Filler @@ -30,7 +30,7 @@ class NotificationDialog(message: String, notificationType: NotificationType = N // Icon children :+= new PaddingBox( - new Icon(IconSource.Notification(notificationType)), + new Icon(IconSource.Notification(notificationType), Size2D(22, 22)), Padding2D.equal(10) ) diff --git a/src/main/scala/ocelot/desktop/ui/widget/settings/SystemSettingsTab.scala b/src/main/scala/ocelot/desktop/ui/widget/settings/SystemSettingsTab.scala index 8ad662c..5d44ec7 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/settings/SystemSettingsTab.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/settings/SystemSettingsTab.scala @@ -15,9 +15,7 @@ class SystemSettingsTab extends SettingsTab { override val icon: IconSource = IconSource.SettingsSystem override val label: String = "System" - children :+= new PaddingBox(new Label { - override def text = "OpenComputers configuration file path" - }, Padding2D(bottom = 8)) + children :+= new PaddingBox(new Label("OpenComputers configuration file path"), Padding2D(bottom = 8)) children :+= new PaddingBox(new Widget { override val layout = new LinearLayout(this, orientation = Orientation.Horizontal) diff --git a/src/main/scala/ocelot/desktop/ui/widget/verticalmenu/VerticalMenuButton.scala b/src/main/scala/ocelot/desktop/ui/widget/verticalmenu/VerticalMenuButton.scala index d3dcb2c..e35d77f 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/verticalmenu/VerticalMenuButton.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/verticalmenu/VerticalMenuButton.scala @@ -22,9 +22,7 @@ class VerticalMenuButton(icon: IconSource, label: String, handler: VerticalMenuB override val layout = new LinearLayout(this, orientation = Orientation.Horizontal) children :+= new PaddingBox( - new Icon(icon) { - override def iconColor: Color = ColorScheme("VerticalMenuEntryIcon") - }, + new Icon(icon, color = ColorScheme("VerticalMenuEntryIcon")), Padding2D(right = 8) ) diff --git a/src/main/scala/ocelot/desktop/util/OcelotOnlineAPI.scala b/src/main/scala/ocelot/desktop/util/OcelotOnlineAPI.scala new file mode 100644 index 0000000..5e43645 --- /dev/null +++ b/src/main/scala/ocelot/desktop/util/OcelotOnlineAPI.scala @@ -0,0 +1,49 @@ +package ocelot.desktop.util + +import java.net.{HttpURLConnection, URL} +import java.time.ZonedDateTime +import scala.util.{Success, Try, Using} + +object OcelotOnlineAPI { + private val UrlInfo: String = "https://ocelot.fomalhaut.me/desktop/info" + + private val RegexDevId = "\"dev\":\\s?\\{.*?\"id\":\\s?\"(.*?)\"".r + private val RegexDevDate = "\"dev\":\\s?\\{.*?\"date\":\\s?\"(.*?)\"".r + private val RegexReleaseVersion = "\"release\":\\s?\\{.*?\"version\":\\s?\"(.*?)\"".r + private val RegexReleaseDate = "\"release\":\\s?\\{.*?\"date\":\\s?\"(.*?)\"".r + + def checkRemoteVersion(callback: Try[Version] => Unit): Unit = + new Thread(() => callback(getVersion)).start() + + private def getVersion: Try[Version] = Try { + val source = scala.io.Source.fromURL(UrlInfo) + val response = source.mkString + + val devId = RegexDevId.findFirstMatchIn(response).map(_.group(1)) + val devDate = RegexDevDate.findFirstMatchIn(response).map(_.group(1)) + val releaseVersion = RegexReleaseVersion.findFirstMatchIn(response).map(_.group(1)) + val releaseDate = RegexReleaseDate.findFirstMatchIn(response).map(_.group(1)) + val version = Version( + devId, devDate.map(it => ZonedDateTime.parse(it)), + releaseVersion, releaseDate.map(it => ZonedDateTime.parse(it)) + ) + + source.close() + version + } + + @throws(classOf[java.io.IOException]) + @throws(classOf[java.net.SocketTimeoutException]) + def get(url: String, connectTimeout: Int = 5000, readTimeout: Int = 5000, requestMethod: String = "GET"): String = { + val connection = new URL(url).openConnection.asInstanceOf[HttpURLConnection] + connection.setConnectTimeout(connectTimeout) + connection.setReadTimeout(readTimeout) + connection.setRequestMethod(requestMethod) + Using.resource(connection.getInputStream) { inputStream => + val content = io.Source.fromInputStream(inputStream).mkString + content + } + } + + case class Version(devId: Option[String], devDate: Option[ZonedDateTime], releaseVersion: Option[String], releaseDate: Option[ZonedDateTime]) +}