684 行
23 KiB
C++
684 行
23 KiB
C++
#include "../desktop-ui.hpp"
|
|
namespace Instances { Instance<Presentation> presentation; }
|
|
Presentation& presentation = Instances::presentation();
|
|
|
|
#define ELLIPSIS "\u2026"
|
|
|
|
Presentation::Presentation() {
|
|
loadMenu.setText("Load");
|
|
|
|
systemMenu.setVisible(false);
|
|
|
|
settingsMenu.setText("Settings");
|
|
videoSizeMenu.setText("Size").setIcon(Icon::Emblem::Image);
|
|
|
|
//generate size menu
|
|
u32 multipliers = 5;
|
|
for(u32 multiplier : range(1, multipliers + 1)) {
|
|
MenuRadioItem item{&videoSizeMenu};
|
|
item.setText({multiplier, "x"});
|
|
item.onActivate([=] {
|
|
settings.video.multiplier = multiplier;
|
|
resizeWindow();
|
|
});
|
|
|
|
videoSizeGroup.append(item);
|
|
}
|
|
|
|
for(auto& item : videoSizeGroup.objects<MenuRadioItem>()) {
|
|
if(item.text() == string{settings.video.multiplier, "x"}) item.setChecked();
|
|
}
|
|
|
|
videoSizeMenu.append(MenuSeparator());
|
|
MenuItem centerWindow{&videoSizeMenu};
|
|
centerWindow.setText("Center Window").setIcon(Icon::Place::Settings).onActivate([&] {
|
|
setAlignment(Alignment::Center);
|
|
});
|
|
videoOutputMenu.setText("Output").setIcon(Icon::Emblem::Image);
|
|
videoOutputPixelPerfect.setText("Pixel Perfect").onActivate([&] {
|
|
settings.video.output = "Perfect";
|
|
});
|
|
videoOutputFixedScale.setText("Scale (Fixed)").onActivate([&] {
|
|
settings.video.output = "Fixed";
|
|
});
|
|
videoOutputIntegerScale.setText("Scale (Integer)").onActivate([&] {
|
|
settings.video.output = "Integer";
|
|
});
|
|
videoOutputScale.setText("Scale (Best Fit)").onActivate([&] {
|
|
settings.video.output = "Scale";
|
|
});
|
|
videoOutputStretch.setText("Stretch").onActivate([&] {
|
|
settings.video.output = "Stretch";
|
|
});
|
|
|
|
if(settings.video.output == "Perfect" ) videoOutputPixelPerfect.setChecked();
|
|
if(settings.video.output == "Fixed" ) videoOutputFixedScale.setChecked();
|
|
if(settings.video.output == "Integer" ) videoOutputIntegerScale.setChecked();
|
|
if(settings.video.output == "Scale" ) videoOutputScale.setChecked();
|
|
if(settings.video.output == "Stretch" ) videoOutputStretch.setChecked();
|
|
|
|
videoAspectCorrection.setText("Aspect Correction").setChecked(settings.video.aspectCorrection).onToggle([&] {
|
|
settings.video.aspectCorrection = videoAspectCorrection.checked();
|
|
if(settings.video.adaptiveSizing) resizeWindow();
|
|
});
|
|
videoAdaptiveSizing.setText("Adaptive Sizing").setChecked(settings.video.adaptiveSizing).onToggle([&] {
|
|
if(settings.video.adaptiveSizing = videoAdaptiveSizing.checked()) resizeWindow();
|
|
});
|
|
videoAutoCentering.setText("Auto Centering").setChecked(settings.video.autoCentering).onToggle([&] {
|
|
if(settings.video.autoCentering = videoAutoCentering.checked()) resizeWindow();
|
|
});
|
|
videoShaderMenu.setText("Shader").setIcon(Icon::Emblem::Image);
|
|
loadShaders();
|
|
bootOptionsMenu.setText("Boot Options").setIcon(Icon::Place::Settings);
|
|
fastBoot.setText("Fast Boot").setChecked(settings.boot.fast).onToggle([&] {
|
|
settings.boot.fast = fastBoot.checked();
|
|
});
|
|
launchDebugger.setText("Launch Debugger").setChecked(settings.boot.debugger).onToggle([&] {
|
|
settings.boot.debugger = launchDebugger.checked();
|
|
});
|
|
preferNTSCU.setText("Prefer US").onActivate([&] {
|
|
settings.boot.prefer = "NTSC-U";
|
|
});
|
|
preferNTSCJ.setText("Prefer Japan").onActivate([&] {
|
|
settings.boot.prefer = "NTSC-J";
|
|
});
|
|
preferPAL.setText("Prefer Europe").onActivate([&] {
|
|
settings.boot.prefer = "PAL";
|
|
});
|
|
if(settings.boot.prefer == "NTSC-U") preferNTSCU.setChecked();
|
|
if(settings.boot.prefer == "NTSC-J") preferNTSCJ.setChecked();
|
|
if(settings.boot.prefer == "PAL") preferPAL.setChecked();
|
|
muteAudioSetting.setText("Mute Audio").setChecked(settings.audio.mute).onToggle([&] {
|
|
settings.audio.mute = muteAudioSetting.checked();
|
|
});
|
|
showStatusBarSetting.setText("Show Status Bar").setChecked(settings.general.showStatusBar).onToggle([&] {
|
|
settings.general.showStatusBar = showStatusBarSetting.checked();
|
|
if(!showStatusBarSetting.checked()) {
|
|
layout.remove(statusLayout);
|
|
} else {
|
|
layout.append(statusLayout, Size{~0, StatusHeight});
|
|
}
|
|
if(visible()) resizeWindow();
|
|
}).doToggle();
|
|
videoSettingsAction.setText("Video" ELLIPSIS).setIcon(Icon::Device::Display).onActivate([&] {
|
|
settingsWindow.show("Video");
|
|
});
|
|
audioSettingsAction.setText("Audio" ELLIPSIS).setIcon(Icon::Device::Speaker).onActivate([&] {
|
|
settingsWindow.show("Audio");
|
|
});
|
|
inputSettingsAction.setText("Input" ELLIPSIS).setIcon(Icon::Device::Joypad).onActivate([&] {
|
|
settingsWindow.show("Input");
|
|
});
|
|
hotkeySettingsAction.setText("Hotkeys" ELLIPSIS).setIcon(Icon::Device::Keyboard).onActivate([&] {
|
|
settingsWindow.show("Hotkeys");
|
|
});
|
|
emulatorSettingsAction.setText("Emulators" ELLIPSIS).setIcon(Icon::Place::Server).onActivate([&] {
|
|
settingsWindow.show("Emulators");
|
|
});
|
|
optionSettingsAction.setText("Options" ELLIPSIS).setIcon(Icon::Action::Settings).onActivate([&] {
|
|
settingsWindow.show("Options");
|
|
});
|
|
firmwareSettingsAction.setText("Firmware" ELLIPSIS).setIcon(Icon::Emblem::Binary).onActivate([&] {
|
|
settingsWindow.show("Firmware");
|
|
});
|
|
pathSettingsAction.setText("Paths" ELLIPSIS).setIcon(Icon::Emblem::Folder).onActivate([&] {
|
|
settingsWindow.show("Paths");
|
|
});
|
|
driverSettingsAction.setText("Drivers" ELLIPSIS).setIcon(Icon::Place::Settings).onActivate([&] {
|
|
settingsWindow.show("Drivers");
|
|
});
|
|
debugSettingsAction.setText("Debug" ELLIPSIS).setIcon(Icon::Device::Network).onActivate([&] {
|
|
settingsWindow.show("Debug");
|
|
});
|
|
|
|
toolsMenu.setVisible(false).setText("Tools");
|
|
saveStateMenu.setText("Save State").setIcon(Icon::Media::Record);
|
|
for(u32 slot : range(9)) {
|
|
MenuItem item{&saveStateMenu};
|
|
item.setText({"Slot ", 1 + slot}).onActivate([=] {
|
|
if(program.stateSave(1 + slot)) {
|
|
undoSaveStateMenu.setEnabled(true);
|
|
}
|
|
});
|
|
}
|
|
loadStateMenu.setText("Load State").setIcon(Icon::Media::Rewind);
|
|
for(u32 slot : range(9)) {
|
|
MenuItem item{&loadStateMenu};
|
|
item.setText({"Slot ", 1 + slot}).onActivate([=] {
|
|
if(program.stateLoad(1 + slot)) {
|
|
undoLoadStateMenu.setEnabled(true);
|
|
}
|
|
});
|
|
}
|
|
undoSaveStateMenu.setText("Undo Last Save State").setIcon(Icon::Edit::Undo).setEnabled(false);
|
|
undoSaveStateMenu.onActivate([&] {
|
|
program.undoStateSave();
|
|
undoSaveStateMenu.setEnabled(false);
|
|
});
|
|
undoLoadStateMenu.setText("Undo Last Load State").setIcon(Icon::Edit::Undo).setEnabled(false);
|
|
undoLoadStateMenu.onActivate([&] {
|
|
program.undoStateLoad();
|
|
undoLoadStateMenu.setEnabled(false);
|
|
});
|
|
captureScreenshot.setText("Capture Screenshot").setIcon(Icon::Emblem::Image).onActivate([&] {
|
|
program.requestScreenshot = true;
|
|
});
|
|
pauseEmulation.setText("Pause Emulation").onToggle([&] {
|
|
program.pause(!program.paused);
|
|
});
|
|
reloadGame.setText("Reload Game").setIcon(Icon::Action::Refresh).onActivate([&] {
|
|
program.load(emulator, emulator->game->location);
|
|
});
|
|
frameAdvance.setText("Frame Advance").setIcon(Icon::Media::Play).onActivate([&] {
|
|
if (!program.paused) program.pause(true);
|
|
program.requestFrameAdvance = true;
|
|
});
|
|
manifestViewerAction.setText("Manifest").setIcon(Icon::Emblem::Binary).onActivate([&] {
|
|
toolsWindow.show("Manifest");
|
|
});
|
|
cheatEditorAction.setText("Cheats").setIcon(Icon::Emblem::File).onActivate([&] {
|
|
toolsWindow.show("Cheats");
|
|
});
|
|
#if !defined(PLATFORM_MACOS)
|
|
// Cocoa hiro is missing the hex editor widget
|
|
memoryEditorAction.setText("Memory").setIcon(Icon::Device::Storage).onActivate([&] {
|
|
toolsWindow.show("Memory");
|
|
});
|
|
#endif
|
|
graphicsViewerAction.setText("Graphics").setIcon(Icon::Emblem::Image).onActivate([&] {
|
|
toolsWindow.show("Graphics");
|
|
});
|
|
streamManagerAction.setText("Streams").setIcon(Icon::Emblem::Audio).onActivate([&] {
|
|
toolsWindow.show("Streams");
|
|
});
|
|
propertiesViewerAction.setText("Properties").setIcon(Icon::Emblem::Text).onActivate([&] {
|
|
toolsWindow.show("Properties");
|
|
});
|
|
traceLoggerAction.setText("Tracer").setIcon(Icon::Emblem::Script).onActivate([&] {
|
|
toolsWindow.show("Tracer");
|
|
});
|
|
|
|
helpMenu.setText("Help");
|
|
aboutAction.setText("About" ELLIPSIS).setIcon(Icon::Prompt::Question).onActivate([&] {
|
|
multiFactorImage logo(Resource::Ares::Logo1x, Resource::Ares::Logo2x);
|
|
AboutDialog()
|
|
.setName(ares::Name)
|
|
.setLogo(logo)
|
|
.setDescription({ares::Name, " — a simplified multi-system emulator"})
|
|
.setVersion(ares::Version)
|
|
.setCopyright(ares::Copyright)
|
|
.setLicense(ares::License, ares::LicenseURI)
|
|
.setWebsite(ares::Website, ares::WebsiteURI)
|
|
.setAlignment(presentation)
|
|
.show();
|
|
});
|
|
|
|
viewport.setDroppable().onDrop([&](auto filenames) {
|
|
if(filenames.size() != 1) return;
|
|
if(auto emulator = program.identify(filenames.first())) {
|
|
program.load(emulator, filenames.first());
|
|
}
|
|
});
|
|
|
|
Application::onOpenFile([&](auto filename) {
|
|
if(auto emulator = program.identify(filename)) {
|
|
program.load(emulator, filename);
|
|
}
|
|
});
|
|
|
|
iconLayout.setCollapsible();
|
|
|
|
iconSpacer.setCollapsible().setColor({0, 0, 0}).setDroppable().onDrop([&](auto filenames) {
|
|
viewport.doDrop(filenames);
|
|
});
|
|
|
|
multiFactorImage icon(Resource::Ares::Icon1x, Resource::Ares::Icon2x);
|
|
icon.alphaBlend(0x000000);
|
|
iconCanvas.setCollapsible().setIcon(icon).setDroppable().onDrop([&](auto filenames) {
|
|
viewport.doDrop(filenames);
|
|
});
|
|
|
|
iconPadding.setCollapsible().setColor({0, 0, 0}).setDroppable().onDrop([&](auto filenames) {
|
|
viewport.doDrop(filenames);
|
|
});
|
|
|
|
iconBottom.setCollapsible().setColor({0, 0, 0}).setDroppable().onDrop([&](auto filenames) {
|
|
viewport.doDrop(filenames);
|
|
});
|
|
|
|
spacerLeft .setBackgroundColor({32, 32, 32});
|
|
statusLeft .setBackgroundColor({32, 32, 32}).setForegroundColor({255, 255, 255});
|
|
statusDebug.setBackgroundColor({32, 32, 32}).setForegroundColor({255, 255, 255});
|
|
statusRight.setBackgroundColor({32, 32, 32}).setForegroundColor({255, 255, 255});
|
|
spacerRight.setBackgroundColor({32, 32, 32});
|
|
|
|
statusLeft .setAlignment(0.0).setFont(Font().setBold());
|
|
statusDebug.setAlignment(1.0).setFont(Font().setBold());
|
|
statusRight.setAlignment(1.0).setFont(Font().setBold());
|
|
|
|
onClose([&] {
|
|
program.quit();
|
|
});
|
|
|
|
loadEmulators();
|
|
|
|
resizeWindow();
|
|
setTitle({ares::Name, " v", ares::Version});
|
|
setAssociatedFile();
|
|
setBackgroundColor({0, 0, 0});
|
|
setAlignment(Alignment::Center);
|
|
setVisible();
|
|
|
|
#if defined(PLATFORM_MACOS)
|
|
Application::Cocoa::onAbout([&] { aboutAction.doActivate(); });
|
|
Application::Cocoa::onActivate([&] { setFocused(); });
|
|
Application::Cocoa::onPreferences([&] { settingsWindow.show("Video"); });
|
|
Application::Cocoa::onQuit([&] { doClose(); });
|
|
#endif
|
|
}
|
|
|
|
auto Presentation::resizeWindow() -> void {
|
|
if(fullScreen()) setFullScreen(false);
|
|
if(maximized()) return;
|
|
if(settings.video.output == "Fixed") return;
|
|
|
|
u32 multiplier = settings.video.multiplier;
|
|
u32 viewportWidth = 320 * multiplier;
|
|
u32 viewportHeight = 240 * multiplier;
|
|
|
|
if(emulator && program.screens) {
|
|
auto& node = program.screens.first();
|
|
u32 videoWidth = node->width() * node->scaleX();
|
|
u32 videoHeight = node->height() * node->scaleY();
|
|
if(settings.video.aspectCorrection) videoWidth = videoWidth * node->aspectX() / node->aspectY();
|
|
if(node->rotation() == 90 || node->rotation() == 270) swap(videoWidth, videoHeight);
|
|
|
|
viewportWidth = videoWidth * multiplier;
|
|
viewportHeight = videoHeight * multiplier;
|
|
}
|
|
|
|
u32 statusHeight = showStatusBarSetting.checked() ? StatusHeight : 0;
|
|
|
|
// Prevent the window frame from going out of bounds
|
|
u32 monitorHeight = 1;
|
|
u32 monitorWidth = 1;
|
|
for(u32 monitor : range(Monitor::count())) {
|
|
monitorHeight = max(monitorHeight, Monitor::workspace(monitor).height());
|
|
}
|
|
for(u32 monitor : range(Monitor::count())) {
|
|
monitorWidth = max(monitorWidth, Monitor::workspace(monitor).width());
|
|
}
|
|
|
|
if(viewportWidth > monitorWidth || viewportHeight > monitorHeight) {
|
|
// setMaximized causes odd window glitches and is not supported on macOS, avoid!
|
|
// 100px buffer to account for possible taskbars
|
|
setGeometry(Alignment::Center, {monitorWidth, monitorHeight - statusHeight - 100});
|
|
return;
|
|
}
|
|
|
|
if(settings.video.autoCentering) {
|
|
setGeometry(Alignment::Center, {viewportWidth, viewportHeight + statusHeight});
|
|
} else {
|
|
setSize({viewportWidth, viewportHeight + statusHeight});
|
|
}
|
|
|
|
setMinimumSize({160, 144 + statusHeight});
|
|
}
|
|
|
|
auto Presentation::loadEmulators() -> void {
|
|
loadMenu.reset();
|
|
|
|
//clean up the recent games history first
|
|
vector<string> recentGames;
|
|
for(u32 index : range(9)) {
|
|
auto entry = settings.recent.game[index];
|
|
auto system = entry.split(";", 1L)(0);
|
|
auto location = entry.split(";", 1L)(1);
|
|
if(location.length()) { //remove missing games
|
|
if(!recentGames.find(entry)) { //remove duplicate entries
|
|
recentGames.append(entry);
|
|
}
|
|
}
|
|
settings.recent.game[index] = {};
|
|
}
|
|
|
|
//build recent games list
|
|
u32 count = 0;
|
|
for(auto& game : recentGames) {
|
|
settings.recent.game[count++] = game;
|
|
}
|
|
{ Menu recentGames{&loadMenu};
|
|
recentGames.setIcon(Icon::Action::Open);
|
|
recentGames.setText("Recent Games");
|
|
for(u32 index : range(count)) {
|
|
MenuItem item{&recentGames};
|
|
auto entry = settings.recent.game[index];
|
|
auto system = entry.split(";", 1L)(0);
|
|
auto location = entry.split(";", 1L)(1);
|
|
item.setIconForFile(location);
|
|
item.setText({Location::base(location).trimRight("/"), " (", system, ")"});
|
|
item.onActivate([=] {
|
|
if(!inode::exists(location)) {
|
|
MessageDialog()
|
|
.setTitle("Error")
|
|
.setText({location, " does not exist"})
|
|
.setAlignment(presentation)
|
|
.error();
|
|
|
|
//remove the entry from the recent games list
|
|
settings.recent.game[index] = {};
|
|
loadEmulators();
|
|
return;
|
|
}
|
|
for(auto& emulator : emulators) {
|
|
if(emulator->name == system) {
|
|
return (void)program.load(emulator, location);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
if(count > 0) {
|
|
recentGames.append(MenuSeparator());
|
|
MenuItem clearHistory{&recentGames};
|
|
clearHistory.setIcon(Icon::Edit::Clear);
|
|
#if !defined(PLATFORM_MACOS)
|
|
clearHistory.setText("Clear History");
|
|
#else
|
|
clearHistory.setText("Clear Menu");
|
|
#endif
|
|
clearHistory.onActivate([&] {
|
|
for(u32 index : range(9)) settings.recent.game[index] = {};
|
|
loadEmulators();
|
|
});
|
|
} else {
|
|
recentGames.setEnabled(false);
|
|
}
|
|
}
|
|
loadMenu.append(MenuSeparator());
|
|
|
|
//build emulator load list
|
|
u32 enabled = 0;
|
|
|
|
//first pass; make sure "Arcade" is start of list
|
|
for(auto& emulator : emulators) {
|
|
if(!emulator->configuration.visible) continue;
|
|
if(emulator->group() == "Arcade") {
|
|
Menu menu;
|
|
menu.setIcon(Icon::Emblem::Folder);
|
|
menu.setText(emulator->group());
|
|
loadMenu.append(menu);
|
|
break;
|
|
}
|
|
}
|
|
|
|
for(auto& emulator : emulators) {
|
|
if (!emulator->configuration.visible) continue;
|
|
enabled++;
|
|
MenuItem item;
|
|
item.setIcon(Icon::Place::Server);
|
|
item.setText({emulator->name, ELLIPSIS});
|
|
item.setVisible(emulator->configuration.visible);
|
|
item.onActivate([=] {
|
|
program.load(emulator);
|
|
});
|
|
|
|
Menu menu;
|
|
for(auto& action : loadMenu.actions()) {
|
|
if(auto group = action.cast<Menu>()) {
|
|
if(group.text() == emulator->group()) {
|
|
menu = group;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if(!menu) {
|
|
menu.setIcon(Icon::Emblem::Folder);
|
|
menu.setText(emulator->group());
|
|
loadMenu.append(menu);
|
|
}
|
|
menu.append(item);
|
|
}
|
|
if(enabled == 0) {
|
|
//if the user disables every system, give an indication for how to re-add systems to the load menu
|
|
MenuItem item{&loadMenu};
|
|
item.setIcon(Icon::Action::Add);
|
|
item.setText("Add Systems" ELLIPSIS);
|
|
item.onActivate([&] {
|
|
settingsWindow.show("Emulators");
|
|
});
|
|
}
|
|
|
|
#if !defined(PLATFORM_MACOS)
|
|
loadMenu.append(MenuSeparator());
|
|
|
|
{ MenuItem quit{&loadMenu};
|
|
quit.setIcon(Icon::Action::Quit);
|
|
quit.setText("Quit");
|
|
quit.onActivate([&] {
|
|
program.quit();
|
|
});
|
|
}
|
|
#endif
|
|
}
|
|
|
|
auto Presentation::loadEmulator() -> void {
|
|
setTitle(emulator->root->game());
|
|
setAssociatedFile(emulator->game->location);
|
|
systemMenu.setText(emulator->name);
|
|
systemMenu.setVisible();
|
|
|
|
refreshSystemMenu();
|
|
|
|
toolsMenu.setVisible(true);
|
|
pauseEmulation.setChecked(false);
|
|
|
|
setFocused();
|
|
viewport.setFocused();
|
|
}
|
|
|
|
auto Presentation::refreshSystemMenu() -> void {
|
|
systemMenu.reset();
|
|
|
|
//allow each emulator core to create any specialized menus necessary:
|
|
//for instance, floppy disk and CD-ROM swapping support.
|
|
emulator->load(systemMenu);
|
|
if(systemMenu.actionCount() > 0) systemMenu.append(MenuSeparator());
|
|
|
|
u32 portsFound = 0;
|
|
for(auto port : ares::Node::enumerate<ares::Node::Port>(emulator->root)) {
|
|
//do not add unsupported ports to the port menu
|
|
if(emulator->portBlacklist.find(port->name())) continue;
|
|
|
|
if(!port->hotSwappable()) continue;
|
|
if(port->type() != "Controller" && port->type() != "Expansion") continue;
|
|
|
|
portsFound++;
|
|
Menu portMenu{&systemMenu};
|
|
if(port->type() == "Controller") portMenu.setIcon(Icon::Device::Joypad);
|
|
if(port->type() == "Expansion" ) portMenu.setIcon(Icon::Device::Storage);
|
|
portMenu.setText(port->name());
|
|
|
|
Group peripheralGroup;
|
|
{ MenuRadioItem peripheralItem{&portMenu};
|
|
peripheralItem.setAttribute<ares::Node::Port>("port", port);
|
|
peripheralItem.setText("Nothing");
|
|
peripheralItem.onActivate([=] {
|
|
auto port = peripheralItem.attribute<ares::Node::Port>("port");
|
|
port->disconnect();
|
|
refreshSystemMenu();
|
|
});
|
|
peripheralGroup.append(peripheralItem);
|
|
}
|
|
for(auto peripheral : port->supported()) {
|
|
//do not add unsupported peripherals to the peripheral port menu
|
|
if(emulator->inputBlacklist.find(peripheral)) continue;
|
|
|
|
MenuRadioItem peripheralItem{&portMenu};
|
|
peripheralItem.setAttribute<ares::Node::Port>("port", port);
|
|
peripheralItem.setText(peripheral);
|
|
peripheralItem.onActivate([=] {
|
|
auto port = peripheralItem.attribute<ares::Node::Port>("port");
|
|
port->disconnect();
|
|
port->allocate(peripheralItem.text());
|
|
port->connect();
|
|
refreshSystemMenu();
|
|
});
|
|
peripheralGroup.append(peripheralItem);
|
|
}
|
|
|
|
//check the peripheral item menu option that is currently connected to said port
|
|
if(auto connected = port->connected()) {
|
|
for(auto peripheralItem : peripheralGroup.objects<MenuRadioItem>()) {
|
|
if(peripheralItem.text() == connected->name()) peripheralItem.setChecked();
|
|
}
|
|
}
|
|
}
|
|
|
|
if(portsFound > 0) systemMenu.append(MenuSeparator());
|
|
|
|
MenuItem reset{&systemMenu};
|
|
reset.setText("Reset").setIcon(Icon::Action::Refresh).onActivate([&] {
|
|
emulator->root->power(true);
|
|
program.showMessage("System reset");
|
|
});
|
|
systemMenu.append(MenuSeparator());
|
|
|
|
MenuItem unload{&systemMenu};
|
|
unload.setText("Unload").setIcon(Icon::Media::Eject).onActivate([&] {
|
|
program.unload();
|
|
if(settings.video.adaptiveSizing) presentation.resizeWindow();
|
|
presentation.showIcon(true);
|
|
});
|
|
}
|
|
|
|
auto Presentation::unloadEmulator(bool reloading) -> void {
|
|
setTitle({ares::Name, " v", ares::Version});
|
|
setAssociatedFile();
|
|
systemMenu.setVisible(false);
|
|
systemMenu.reset();
|
|
|
|
toolsMenu.setVisible(false);
|
|
}
|
|
|
|
auto Presentation::showIcon(bool visible) -> void {
|
|
iconLayout.setVisible(visible);
|
|
iconSpacer.setVisible(visible);
|
|
iconCanvas.setVisible(visible);
|
|
iconBottom.setVisible(visible);
|
|
layout.resize();
|
|
}
|
|
|
|
auto Presentation::loadShaders() -> void {
|
|
videoShaderMenu.reset();
|
|
videoShaderMenu.setEnabled(ruby::video.hasShader());
|
|
if(!ruby::video.hasShader()) return;
|
|
|
|
Group shaders;
|
|
|
|
MenuCheckItem none{&videoShaderMenu};
|
|
none.setText("None").onToggle([&] {
|
|
settings.video.shader = "None";
|
|
ruby::video.setShader(settings.video.shader);
|
|
loadShaders();
|
|
});
|
|
shaders.append(none);
|
|
|
|
string location = locate("Shaders/");
|
|
|
|
if(shaderDirectories.size() == 0) {
|
|
function<void(string)> findShaderDirectories = [&](string path) {
|
|
for(auto &entry: directory::folders(path)) findShaderDirectories({path, entry});
|
|
auto files = directory::files(path, "*.slangp");
|
|
if(files.size() > 0) shaderDirectories.append((string({path}).trimLeft(location, 1L)));
|
|
};
|
|
findShaderDirectories(location);
|
|
|
|
// Sort by name and depth such that child folders appear after their parents
|
|
shaderDirectories.sort([](const string &lhs, const string &rhs) {
|
|
auto lhsParts = lhs.split("/");
|
|
auto rhsParts = rhs.split("/");
|
|
for(u32 i : range(min(lhsParts.size(), rhsParts.size()))) {
|
|
if(lhsParts[i] != rhsParts[i]) return lhsParts[i] < rhsParts[i];
|
|
}
|
|
return lhsParts.size() < rhsParts.size();
|
|
});
|
|
}
|
|
|
|
if(ruby::video.hasShader()) {
|
|
for(auto &directory : shaderDirectories) {
|
|
auto parts = directory.split("/");
|
|
Menu parent = videoShaderMenu;
|
|
|
|
if(directory != "") {
|
|
for (auto &part: parts) {
|
|
if(part == "") continue;
|
|
Menu child;
|
|
bool found = false;
|
|
for(auto &action: parent.actions()) {
|
|
if(auto menu = action.cast<Menu>()) {
|
|
if(menu.text() == part) {
|
|
child = menu;
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(found) {
|
|
parent = child;
|
|
} else {
|
|
Menu newMenu{&parent};
|
|
newMenu.setText(part);
|
|
parent = newMenu;
|
|
}
|
|
}
|
|
}
|
|
|
|
auto files = directory::files({location, directory}, "*.slangp");
|
|
for(auto &file: files) {
|
|
MenuCheckItem item{&parent};
|
|
item.setAttribute("file", {directory, file});
|
|
item.setText(string{file}.trimRight(".slangp", 1L)).onToggle([=] {
|
|
settings.video.shader = {directory, file};
|
|
ruby::video.setShader({location, settings.video.shader});
|
|
loadShaders();
|
|
});
|
|
shaders.append(item);
|
|
}
|
|
}
|
|
}
|
|
|
|
if(program.startShader) {
|
|
string existingShader = settings.video.shader;
|
|
|
|
if(!program.startShader.imatch("None")) {
|
|
settings.video.shader = {location, program.startShader, ".slangp"};
|
|
} else {
|
|
settings.video.shader = program.startShader;
|
|
}
|
|
|
|
if(inode::exists(settings.video.shader)) {
|
|
ruby::video.setShader({location, settings.video.shader});
|
|
loadShaders();
|
|
} else if(settings.video.shader.imatch("None")) {
|
|
ruby::video.setShader("None");
|
|
loadShaders();
|
|
} else {
|
|
hiro::MessageDialog()
|
|
.setTitle("Warning")
|
|
.setAlignment(hiro::Alignment::Center)
|
|
.setText({ "Requested shader not found: ", settings.video.shader , "\nUsing existing defined shader: ", existingShader })
|
|
.warning();
|
|
settings.video.shader = existingShader;
|
|
}
|
|
}
|
|
|
|
if(settings.video.shader.imatch("None")) {none.setChecked(); settings.video.shader = "None";}
|
|
for(auto item : shaders.objects<MenuCheckItem>()) {
|
|
if(settings.video.shader.imatch(item.attribute("file"))) {
|
|
item.setChecked();
|
|
settings.video.shader = item.attribute("file");
|
|
ruby::video.setShader({location, settings.video.shader});
|
|
}
|
|
}
|
|
} |