Add full Settings window to prelaunch

This commit is contained in:
Luke Street
2026-05-05 14:12:53 -06:00
parent 08321699cd
commit ccd2bdbaac
10 changed files with 360 additions and 483 deletions
-2
View File
@@ -1488,8 +1488,6 @@ set(DUSK_FILES
src/dusk/ui/popup.hpp
src/dusk/ui/prelaunch.cpp
src/dusk/ui/prelaunch.hpp
src/dusk/ui/prelaunch_options.cpp
src/dusk/ui/prelaunch_options.hpp
src/dusk/ui/preset.cpp
src/dusk/ui/preset.hpp
src/dusk/ui/select_button.cpp
-4
View File
@@ -52,10 +52,6 @@ window[open] {
transform: scale(1);
}
window[open].blurred {
filter: blur(2dp);
}
@media (max-height: 640dp) {
body {
padding: 16dp;
-1
View File
@@ -18,7 +18,6 @@ public:
Rml::String bodyRml;
std::vector<ModalAction> actions;
std::function<void(Modal&)> onDismiss;
bool doBlur = false;
};
explicit Modal(Props props);
+55 -16
View File
@@ -1,17 +1,17 @@
#include "prelaunch.hpp"
#include "popup.hpp"
#include "dusk/config.hpp"
#include "dusk/file_select.hpp"
#include "dusk/iso_validate.hpp"
#include "dusk/main.h"
#include "dusk/settings.h"
#include "dusk/ui/prelaunch_options.hpp"
#include "dusk/ui/preset.hpp"
#include "modal.hpp"
#include "popup.hpp"
#include "preset.hpp"
#include "settings.hpp"
#include "version.h"
#include <SDL3/SDL_dialog.h>
#include <SDL3/SDL_filesystem.h>
#include <aurora/lib/window.hpp>
namespace dusk::ui {
@@ -171,8 +171,8 @@ Prelaunch::Prelaunch() : Document(kDocumentSource), mRoot(mDocument->GetElementB
if (auto* menuList = mDocument->GetElementById("menu-list")) {
auto& state = prelaunch_state();
mMenuButtons.push_back(
std::make_unique<Button>(menuList, state.selectedDiscIsValid ? "Play" : "Select Disc Image"));
mMenuButtons.push_back(std::make_unique<Button>(
menuList, state.selectedDiscIsValid ? "Play" : "Select Disc Image"));
mMenuButtons.back()->on_pressed([this] {
if (!prelaunch_state().selectedDiscIsValid) {
open_iso_picker();
@@ -196,7 +196,10 @@ Prelaunch::Prelaunch() : Document(kDocumentSource), mRoot(mDocument->GetElementB
apply_intro_animation(mMenuButtons.back()->root(), "delay-1");
mMenuButtons.push_back(std::make_unique<Button>(menuList, "Options"));
mMenuButtons.back()->on_pressed([this] { push(std::make_unique<PrelaunchOptions>()); });
mMenuButtons.back()->on_pressed([this] {
mRestartSuppressed = false;
push(std::make_unique<SettingsWindow>(true));
});
apply_intro_animation(mMenuButtons.back()->root(), "delay-2");
mMenuButtons.push_back(std::make_unique<Button>(menuList, "Quit To Desktop"));
@@ -227,6 +230,40 @@ void Prelaunch::show() {
Document::show();
mDocument->SetAttribute("open", "");
mRoot->SetAttribute("open", "");
if (is_restart_pending() && !mRestartSuppressed) {
const auto dismiss = [this](Modal& modal) {
mRestartSuppressed = true;
modal.pop();
};
std::vector<ModalAction> actions;
if constexpr (dusk::SupportsProcessRestart) {
actions.push_back(ModalAction{
.label = "Restart later",
.onPressed = dismiss,
});
actions.push_back(ModalAction{
.label = "Restart now",
.onPressed = [](Modal&) { dusk::RequestRestart(); },
});
} else {
actions.push_back(ModalAction{
.label = "OK",
.onPressed = dismiss,
});
}
push(std::make_unique<Modal>(Modal::Props{
.title = "Apply Options",
.bodyRml =
dusk::SupportsProcessRestart ?
"A restart is required to apply selected options.<br/><br/>Restart now to "
"apply them immediately?" :
"A restart is required to apply selected options.<br/><br/>Close and reopen "
"Dusk to apply them.",
.actions = std::move(actions),
.onDismiss = dismiss,
}));
}
}
void Prelaunch::hide(bool close) {
@@ -249,20 +286,21 @@ void Prelaunch::update() {
auto& state = prelaunch_state();
if (!state.errorString.empty() && top_document() == this) {
auto dismissInvalidDisc = [](Modal& modal) {
auto dismiss = [](Modal& modal) {
prelaunch_state().errorString.clear();
modal.pop();
};
push_document(std::make_unique<Modal>(Modal::Props{
push(std::make_unique<Modal>(Modal::Props{
.title = "Invalid disc image",
.bodyRml = state.errorString,
.actions = {
ModalAction{
.label = "OK",
.onPressed = dismissInvalidDisc,
.actions =
{
ModalAction{
.label = "OK",
.onPressed = dismiss,
},
},
},
.onDismiss = dismissInvalidDisc,
.onDismiss = dismiss,
}));
}
@@ -295,7 +333,8 @@ void Prelaunch::update() {
if (mDiscDetail != nullptr) {
if (hasValidPath) {
mDiscDetail->SetProperty(Rml::PropertyId::Display, Rml::Style::Display::Block);
mDiscDetail->SetInnerRML(prelaunch_state().initialDiscIsPal ? "GameCube • EUR" : "GameCube • USA");
mDiscDetail->SetInnerRML(
prelaunch_state().initialDiscIsPal ? "GameCube • EUR" : "GameCube • USA");
} else {
mDiscDetail->SetProperty(Rml::PropertyId::Display, Rml::Style::Display::None);
}
+1
View File
@@ -24,6 +24,7 @@ protected:
private:
bool mEntranceAnimationStarted = false;
bool mRestartSuppressed = false;
std::vector<std::unique_ptr<Button>> mMenuButtons;
Rml::Element* mRoot = nullptr;
Rml::Element* mDiscStatus = nullptr;
-420
View File
@@ -1,420 +0,0 @@
#include "prelaunch_options.hpp"
#include "dusk/config.hpp"
#include "dusk/main.h"
#include "dusk/settings.h"
#include "pane.hpp"
#include "prelaunch.hpp"
namespace dusk::ui {
namespace {
static constexpr std::array<const char*, 5> kLanguageNames = {
"English",
"German",
"French",
"Spanish",
"Italian",
};
// TODO: Copied from ImGui prelaunch. Needs a refactor?
bool try_parse_backend(std::string_view backend, AuroraBackend& outBackend) {
if (backend == "auto") {
outBackend = BACKEND_AUTO;
return true;
}
if (backend == "d3d11") {
outBackend = BACKEND_D3D11;
return true;
}
if (backend == "d3d12") {
outBackend = BACKEND_D3D12;
return true;
}
if (backend == "metal") {
outBackend = BACKEND_METAL;
return true;
}
if (backend == "vulkan") {
outBackend = BACKEND_VULKAN;
return true;
}
if (backend == "opengl") {
outBackend = BACKEND_OPENGL;
return true;
}
if (backend == "opengles") {
outBackend = BACKEND_OPENGLES;
return true;
}
if (backend == "webgpu") {
outBackend = BACKEND_WEBGPU;
return true;
}
if (backend == "null") {
outBackend = BACKEND_NULL;
return true;
}
return false;
}
std::string_view backend_name(AuroraBackend backend) {
switch (backend) {
default:
return "Auto";
case BACKEND_D3D12:
return "D3D12";
case BACKEND_D3D11:
return "D3D11";
case BACKEND_METAL:
return "Metal";
case BACKEND_VULKAN:
return "Vulkan";
case BACKEND_OPENGL:
return "OpenGL";
case BACKEND_OPENGLES:
return "OpenGL ES";
case BACKEND_WEBGPU:
return "WebGPU";
case BACKEND_NULL:
return "Null";
}
}
std::string_view backend_id(AuroraBackend backend) {
switch (backend) {
default:
return "auto";
case BACKEND_D3D12:
return "d3d12";
case BACKEND_D3D11:
return "d3d11";
case BACKEND_METAL:
return "metal";
case BACKEND_VULKAN:
return "vulkan";
case BACKEND_OPENGL:
return "opengl";
case BACKEND_OPENGLES:
return "opengles";
case BACKEND_WEBGPU:
return "webgpu";
case BACKEND_NULL:
return "null";
}
}
std::vector<AuroraBackend> available_backends() {
std::vector<AuroraBackend> backends;
backends.emplace_back(BACKEND_AUTO);
size_t backendCount = 0;
const AuroraBackend* raw = aurora_get_available_backends(&backendCount);
for (size_t i = 0; i < backendCount; ++i) {
// Do not expose NULL or D3D11
if (raw[i] != BACKEND_NULL && raw[i] != BACKEND_D3D11) {
backends.emplace_back(raw[i]);
}
}
return backends;
}
class DiscSelect final : public SelectButton {
public:
explicit DiscSelect(Rml::Element* parent)
: SelectButton(parent, Props{.key = "Change Disc Image"}) {}
void update() override {
ensure_initialized();
const auto& path = prelaunch_state().selectedDiscPath;
std::string display;
if (path.empty()) {
display = "(none)";
} else {
display = std::filesystem::path(path).filename().string();
if (display.empty()) {
display = path;
}
}
const auto& initial = prelaunch_state().initialDiscPath;
if (!initial.empty() && path != initial) {
display += " (restart required)";
}
set_value_label(Rml::String(display));
SelectButton::update();
}
protected:
bool handle_nav_command(NavCommand cmd) override {
if (cmd != NavCommand::Confirm) {
return false;
}
open_iso_picker();
return true;
}
};
class LanguageSelect final : public SelectButton {
public:
explicit LanguageSelect(Rml::Element* parent)
: SelectButton(parent, Props{.key = "Language"}) {}
void update() override {
ensure_initialized();
// LanguageInit already forces English for USA discs, so we can just change the button's
// value instead of actually updating the config. This allows the old language setting to
// be remembered when switching back to a PAL disc.
const auto& state = prelaunch_state();
std::string value;
if (state.selectedDiscIsValid && !state.selectedDiscIsPal) {
value = kLanguageNames[0];
set_disabled(true);
} else {
const u8 idx = static_cast<u8>(getSettings().game.language.getValue());
value = kLanguageNames[idx];
set_disabled(false);
}
if (getSettings().game.language.getValue() != state.initialLanguage) {
value += " (restart required)";
}
set_value_label(Rml::String(value));
SelectButton::update();
}
protected:
bool handle_nav_command(NavCommand cmd) override {
if (disabled()) {
return false;
}
if (cmd != NavCommand::Confirm && cmd != NavCommand::Left && cmd != NavCommand::Right) {
return false;
}
constexpr int n = static_cast<int>(kLanguageNames.size());
int idx = static_cast<int>(getSettings().game.language.getValue());
const int dir = (cmd == NavCommand::Left) ? -1 : 1;
idx = ((idx + dir) % n + n) % n;
getSettings().game.language.setValue(static_cast<GameLanguage>(idx));
config::Save();
return true;
}
};
class BackendSelect final : public SelectButton {
public:
explicit BackendSelect(Rml::Element* parent)
: SelectButton(parent, Props{.key = "Graphics Backend"}) {}
void update() override {
AuroraBackend configuredBackend = BACKEND_AUTO;
const auto configuredId = getSettings().backend.graphicsBackend.getValue();
if (!try_parse_backend(configuredId, configuredBackend)) {
configuredBackend = BACKEND_AUTO;
}
// Do not expose NULL or D3D11
if (configuredBackend == BACKEND_NULL || configuredBackend == BACKEND_D3D11) {
getSettings().backend.graphicsBackend.setValue("auto");
config::Save();
configuredBackend = BACKEND_AUTO;
}
const auto backend = getSettings().backend.graphicsBackend.getValue();
Rml::String value = backend_name(configuredBackend).data();
if (backend != prelaunch_state().initialGraphicsBackend) {
value += " (restart required)";
}
set_value_label(value);
SelectButton::update();
}
protected:
bool handle_nav_command(NavCommand cmd) override {
if (cmd != NavCommand::Confirm && cmd != NavCommand::Left && cmd != NavCommand::Right) {
return false;
}
const auto backends = available_backends();
const int n = static_cast<int>(backends.size());
if (n <= 0) {
return false;
}
AuroraBackend configuredBackend = BACKEND_AUTO;
const auto configuredId = getSettings().backend.graphicsBackend.getValue();
if (!try_parse_backend(configuredId, configuredBackend)) {
configuredBackend = BACKEND_AUTO;
}
int idx = 0;
for (int i = 0; i < n; ++i) {
if (backends[static_cast<size_t>(i)] == configuredBackend) {
idx = i;
break;
}
}
const int dir = (cmd == NavCommand::Left) ? -1 : 1;
idx = ((idx + dir) % n + n) % n;
getSettings().backend.graphicsBackend.setValue(
std::string(backend_id(backends[static_cast<size_t>(idx)])));
config::Save();
return true;
}
};
class SaveTypeSelect final : public SelectButton {
public:
explicit SaveTypeSelect(Rml::Element* parent)
: SelectButton(parent, Props{.key = "Save File Type"}) {}
void update() override {
ensure_initialized();
const CARDFileType cft =
static_cast<CARDFileType>(getSettings().backend.cardFileType.getValue());
std::string label = cft == CARD_GCIFOLDER ? "GCI Folder" : "Card Image";
if (getSettings().backend.cardFileType.getValue() != prelaunch_state().initialCardFileType)
{
label += " (restart required)";
}
set_value_label(Rml::String(label));
SelectButton::update();
}
protected:
bool handle_nav_command(NavCommand cmd) override {
if (cmd != NavCommand::Confirm && cmd != NavCommand::Left && cmd != NavCommand::Right) {
return false;
}
CARDFileType cft = static_cast<CARDFileType>(getSettings().backend.cardFileType.getValue());
const CARDFileType newValue = cft == CARD_GCIFOLDER ? CARD_RAWIMAGE : CARD_GCIFOLDER;
getSettings().backend.cardFileType.setValue(newValue);
config::Save();
return true;
}
};
} // namespace
PrelaunchOptions::PrelaunchOptions() {
mSuppressNavFallback = true;
add_tab("Options", [this](Rml::Element* content) {
auto& leftPane = add_child<Pane>(content, Pane::Type::Controlled);
leftPane.add_child<DiscSelect>();
leftPane.add_child<LanguageSelect>();
leftPane.add_child<BackendSelect>();
leftPane.add_child<SaveTypeSelect>();
});
}
void PrelaunchOptions::push_modal(Modal::Props props) {
for (auto& action : props.actions) {
auto originalOnPressed = std::move(action.onPressed);
action.onPressed = [this, props, callback = std::move(originalOnPressed)](Modal& modal) {
if (props.doBlur) {
mRoot->SetClass("blurred", false);
}
if (callback) {
callback(modal);
}
};
}
auto originalOnDismiss = std::move(props.onDismiss);
props.onDismiss = [this, props, callback = std::move(originalOnDismiss)](Modal& modal) {
if (props.doBlur) {
mRoot->SetClass("blurred", false);
}
if (callback) {
callback(modal);
}
};
if (props.doBlur) {
mRoot->SetClass("blurred", true);
}
push_document(std::make_unique<Modal>(std::move(props)));
}
void PrelaunchOptions::hide(bool close) {
mRoot->SetClass("blurred", false);
Window::hide(close);
}
void PrelaunchOptions::update() {
Window::update();
auto& state = prelaunch_state();
if (state.errorString.empty() || top_document() != this) {
return;
}
auto dismissInvalidDisc = [](Modal& modal) {
prelaunch_state().errorString.clear();
modal.pop();
};
push_modal(Modal::Props{
.title = "Invalid disc image",
.bodyRml = state.errorString,
.actions =
{
ModalAction{
.label = "OK",
.onPressed = dismissInvalidDisc,
},
},
.onDismiss = dismissInvalidDisc,
.doBlur = true,
});
}
bool PrelaunchOptions::consume_close_request() {
if (!is_restart_pending()) {
return false;
}
if (top_document() != this) {
return true;
}
std::vector<ModalAction> actions;
if constexpr (dusk::SupportsProcessRestart) {
actions.push_back(ModalAction{
.label = "Restart later",
.onPressed =
[this](Modal& modal) {
modal.pop();
pop();
},
});
actions.push_back(ModalAction{
.label = "Restart now",
.onPressed = [](Modal&) { dusk::RequestRestart(); },
});
} else {
actions.push_back(ModalAction{
.label = "OK",
.onPressed =
[this](Modal& modal) {
modal.pop();
pop();
},
});
}
push_modal(Modal::Props{
.title = "Apply Options",
.bodyRml = dusk::SupportsProcessRestart ?
"A restart is required to apply selected options.<br/><br/>Restart now to "
"apply them immediately?" :
"A restart is required to apply selected options.<br/><br/>Close and reopen "
"Dusk to apply them.",
.actions = std::move(actions),
.onDismiss = [](Modal& modal) { modal.pop(); },
.doBlur = true,
});
return true;
}
} // namespace dusk::ui
-21
View File
@@ -1,21 +0,0 @@
#pragma once
#include "modal.hpp"
#include "window.hpp"
namespace dusk::ui {
class PrelaunchOptions : public Window {
public:
PrelaunchOptions();
void update() override;
void hide(bool close) override;
protected:
bool consume_close_request() override;
private:
void push_modal(Modal::Props props);
};
} // namespace dusk::ui
+1
View File
@@ -59,6 +59,7 @@ SelectButton& SelectButton::on_pressed(SelectButtonCallback callback) {
listen(Rml::EventId::Submit, [this, callback = std::move(callback)](Rml::Event& event) {
if (!disabled() && event.GetTargetElement() == mRoot) {
callback();
event.StopPropagation();
}
});
return *this;
+297 -18
View File
@@ -12,13 +12,139 @@
#include "number_button.hpp"
#include "overlay.hpp"
#include "pane.hpp"
#include "prelaunch.hpp"
#include "ui.hpp"
#include <algorithm>
#include "modal.hpp"
namespace dusk::ui {
namespace {
constexpr std::array kLanguageNames = {
"English",
"German",
"French",
"Spanish",
"Italian",
};
constexpr std::array kCardFileTypes = {
"Card Image",
"GCI Folder",
};
bool try_parse_backend(std::string_view backend, AuroraBackend& outBackend) {
if (backend == "auto") {
outBackend = BACKEND_AUTO;
return true;
}
if (backend == "d3d11") {
outBackend = BACKEND_D3D11;
return true;
}
if (backend == "d3d12") {
outBackend = BACKEND_D3D12;
return true;
}
if (backend == "metal") {
outBackend = BACKEND_METAL;
return true;
}
if (backend == "vulkan") {
outBackend = BACKEND_VULKAN;
return true;
}
if (backend == "opengl") {
outBackend = BACKEND_OPENGL;
return true;
}
if (backend == "opengles") {
outBackend = BACKEND_OPENGLES;
return true;
}
if (backend == "webgpu") {
outBackend = BACKEND_WEBGPU;
return true;
}
if (backend == "null") {
outBackend = BACKEND_NULL;
return true;
}
return false;
}
std::string_view backend_name(AuroraBackend backend) {
switch (backend) {
default:
return "Auto";
case BACKEND_D3D12:
return "D3D12";
case BACKEND_D3D11:
return "D3D11";
case BACKEND_METAL:
return "Metal";
case BACKEND_VULKAN:
return "Vulkan";
case BACKEND_OPENGL:
return "OpenGL";
case BACKEND_OPENGLES:
return "OpenGL ES";
case BACKEND_WEBGPU:
return "WebGPU";
case BACKEND_NULL:
return "Null";
}
}
std::string_view backend_id(AuroraBackend backend) {
switch (backend) {
default:
return "auto";
case BACKEND_D3D12:
return "d3d12";
case BACKEND_D3D11:
return "d3d11";
case BACKEND_METAL:
return "metal";
case BACKEND_VULKAN:
return "vulkan";
case BACKEND_OPENGL:
return "opengl";
case BACKEND_OPENGLES:
return "opengles";
case BACKEND_WEBGPU:
return "webgpu";
case BACKEND_NULL:
return "null";
}
}
std::vector<AuroraBackend> available_backends() {
std::vector<AuroraBackend> backends;
backends.emplace_back(BACKEND_AUTO);
size_t backendCount = 0;
const AuroraBackend* raw = aurora_get_available_backends(&backendCount);
for (size_t i = 0; i < backendCount; ++i) {
// Do not expose NULL or D3D11
if (raw[i] != BACKEND_NULL && raw[i] != BACKEND_D3D11) {
backends.emplace_back(raw[i]);
}
}
return backends;
}
AuroraBackend configured_backend() {
AuroraBackend configuredBackend = BACKEND_AUTO;
const auto configuredId = getSettings().backend.graphicsBackend.getValue();
if (!try_parse_backend(configuredId, configuredBackend)) {
configuredBackend = BACKEND_AUTO;
}
return configuredBackend;
}
void reset_for_speedrun_mode() {
mDoMain::developmentMode = -1;
@@ -165,30 +291,156 @@ void overlay_control(
} // namespace
SettingsWindow::SettingsWindow() {
SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) {
if (prelaunch) {
mSuppressNavFallback = true;
add_tab("Prelaunch", [this](Rml::Element* content) {
auto& leftPane = add_child<Pane>(content, Pane::Type::Controlled);
auto& rightPane = add_child<Pane>(content, Pane::Type::Uncontrolled);
leftPane.register_control(
leftPane
.add_select_button({
.key = "Disc Image",
.getValue =
[] {
const auto& path = prelaunch_state().selectedDiscPath;
std::string display;
if (path.empty()) {
display = "(none)";
} else {
display = std::filesystem::path(path).filename().string();
if (display.empty()) {
display = path;
}
}
return display;
},
.isModified =
[] {
const auto& state = prelaunch_state();
const auto& initial = state.initialDiscPath;
return !initial.empty() && state.selectedDiscPath != initial;
},
})
.on_pressed([] { open_iso_picker(); }),
rightPane, [](Pane& pane) {
pane.add_rml("Set the disc image that Dusk uses to launch the game.<br/><br/>"
"Changes require a restart.");
});
leftPane.register_control(
leftPane.add_select_button({
.key = "Language",
.getValue =
[] {
const auto& state = prelaunch_state();
if (!state.selectedDiscIsValid || !state.selectedDiscIsPal) {
return kLanguageNames[0];
}
const u8 idx = static_cast<u8>(getSettings().game.language.getValue());
return kLanguageNames[idx];
},
.isDisabled =
[] {
const auto& state = prelaunch_state();
return !state.selectedDiscIsValid || !state.selectedDiscIsPal;
},
.isModified =
[] {
return getSettings().game.language.getValue() !=
prelaunch_state().initialLanguage;
},
}),
rightPane, [](Pane& pane) {
for (int i = 0; i < kLanguageNames.size(); i++) {
pane.add_button({
.text = kLanguageNames[i],
.isSelected =
[i] {
return getSettings().game.language.getValue() ==
static_cast<GameLanguage>(i);
},
})
.on_pressed([i] {
getSettings().game.language.setValue(static_cast<GameLanguage>(i));
});
}
pane.add_rml("<br/>Changes require a restart.");
});
leftPane.register_control(
leftPane.add_select_button({
.key = "Graphics Backend",
.getValue = [] { return Rml::String{backend_name(configured_backend())}; },
.isModified =
[] {
return getSettings().backend.graphicsBackend.getValue() !=
prelaunch_state().initialGraphicsBackend;
},
}),
rightPane, [](Pane& pane) {
const auto availableBackends = available_backends();
for (const auto backend : availableBackends) {
pane
.add_button({
.text = Rml::String{backend_name(backend)},
.isSelected = [backend] { return configured_backend() == backend; },
})
.on_pressed([backend] {
getSettings().backend.graphicsBackend.setValue(
std::string{backend_id(backend)});
});
}
pane.add_rml("<br/>Changes require a restart.");
});
leftPane.register_control(
leftPane.add_select_button({
.key = "Save File Type",
.getValue =
[] {
return kCardFileTypes[getSettings().backend.cardFileType.getValue()];
},
.isModified =
[] {
return getSettings().backend.cardFileType.getValue() !=
prelaunch_state().initialCardFileType;
},
}),
rightPane, [](Pane& pane) {
for (int i = 0; i < kCardFileTypes.size(); i++) {
pane
.add_button({
.text = kCardFileTypes[i],
.isSelected =
[i] {
return getSettings().backend.cardFileType.getValue() == i;
},
})
.on_pressed([i] { getSettings().backend.cardFileType.setValue(i); });
}
pane.add_rml("<br/>Changes require a restart.");
});
});
}
add_tab("Graphics", [this](Rml::Element* content) {
auto& leftPane = add_child<Pane>(content, Pane::Type::Controlled);
auto& rightPane = add_child<Pane>(content, Pane::Type::Uncontrolled);
leftPane.add_section("Display");
leftPane.register_control(
leftPane.add_button("Toggle Fullscreen").on_pressed([] {
getSettings().video.enableFullscreen.setValue(!getSettings().video.enableFullscreen);
VISetWindowFullscreen(getSettings().video.enableFullscreen);
config::Save();
}),
rightPane, [](Pane& pane) { pane.clear(); }
);
leftPane.register_control(
leftPane.add_button("Restore Default Window Size").on_pressed([] {
getSettings().video.enableFullscreen.setValue(false);
VISetWindowFullscreen(false);
VISetWindowSize(FB_WIDTH * 2, FB_HEIGHT * 2);
VICenterWindow();
}),
rightPane, [](Pane& pane) { pane.clear(); }
);
leftPane.register_control(leftPane.add_button("Toggle Fullscreen").on_pressed([] {
getSettings().video.enableFullscreen.setValue(!getSettings().video.enableFullscreen);
VISetWindowFullscreen(getSettings().video.enableFullscreen);
config::Save();
}),
rightPane, [](Pane& pane) { pane.clear(); });
leftPane.register_control(leftPane.add_button("Restore Default Window Size").on_pressed([] {
getSettings().video.enableFullscreen.setValue(false);
VISetWindowFullscreen(false);
VISetWindowSize(FB_WIDTH * 2, FB_HEIGHT * 2);
VICenterWindow();
}),
rightPane, [](Pane& pane) { pane.clear(); });
config_bool_select(leftPane, rightPane, getSettings().video.enableVsync,
{
.key = "Enable VSync",
@@ -610,4 +862,31 @@ SettingsWindow::SettingsWindow() {
});
}
void SettingsWindow::update() {
// Show disc validation error message if present
if (mPrelaunch && top_document() == this) {
auto& state = prelaunch_state();
if (!state.errorString.empty()) {
auto dismissInvalidDisc = [](Modal& modal) {
prelaunch_state().errorString.clear();
modal.pop();
};
push_document(std::make_unique<Modal>(Modal::Props{
.title = "Invalid disc image",
.bodyRml = state.errorString,
.actions =
{
ModalAction{
.label = "OK",
.onPressed = dismissInvalidDisc,
},
},
.onDismiss = dismissInvalidDisc,
}));
}
}
Window::update();
}
} // namespace dusk::ui
+6 -1
View File
@@ -5,7 +5,12 @@ namespace dusk::ui {
class SettingsWindow : public Window {
public:
SettingsWindow();
SettingsWindow(bool prelaunch = false);
void update() override;
protected:
bool mPrelaunch;
};
} // namespace dusk::ui