mirror of
https://github.com/TwilitRealm/dusklight
synced 2026-08-25 06:41:30 -04:00
Merge branch 'main' of ssh://github.com/TwilitRealm/dusklight into rando-mod
This commit is contained in:
@@ -808,7 +808,8 @@ ModResult ui_window_close(LoadedMod& mod, uint64_t handle) {
|
||||
return MOD_OK;
|
||||
}
|
||||
|
||||
ModResult ui_dialog_push(LoadedMod& mod, const UiDialogDesc& desc, uint64_t& outHandle) {
|
||||
ModResult ui_dialog_push(
|
||||
LoadedMod& mod, const UiDialogDesc& desc, UiPaneBuildFn build, uint64_t& outHandle) {
|
||||
outHandle = 0;
|
||||
if (!aurora::rmlui::is_initialized()) {
|
||||
return MOD_UNAVAILABLE;
|
||||
@@ -851,6 +852,16 @@ ModResult ui_dialog_push(LoadedMod& mod, const UiDialogDesc& desc, uint64_t& out
|
||||
|
||||
auto dialog = std::make_unique<ModDialog>(
|
||||
std::move(props), [handle] { on_mod_dialog_destroyed(handle); });
|
||||
if (build != nullptr) {
|
||||
auto& pane = dialog->content_pane();
|
||||
const uint64_t paneHandle = wrap_pane(mod, pane, nullptr);
|
||||
invoke_mod_ui_callback(mod, "mod UI dialog build", [&](ModError* error) {
|
||||
return build(mod.context.get(), paneHandle, desc.user_data, error);
|
||||
});
|
||||
if (!mod.active) {
|
||||
return MOD_ERROR;
|
||||
}
|
||||
}
|
||||
if (auto* slot = slot_from_handle(handle)) {
|
||||
slot->document = dialog.get();
|
||||
}
|
||||
@@ -1338,7 +1349,8 @@ ModResult ui_dialog_push(ModContext* context, const UiDialogDesc* desc, UiDialog
|
||||
*outDialog = 0;
|
||||
}
|
||||
auto* mod = mod_from_context(context);
|
||||
if (mod == nullptr || desc == nullptr || desc->struct_size < sizeof(UiDialogDesc) ||
|
||||
constexpr size_t kLegacyDescSize = offsetof(UiDialogDesc, build);
|
||||
if (mod == nullptr || desc == nullptr || desc->struct_size < kLegacyDescSize ||
|
||||
desc->title == nullptr || desc->body_rml == nullptr || desc->actions == nullptr ||
|
||||
desc->action_count == 0 || desc->variant > UI_DIALOG_DANGER)
|
||||
{
|
||||
@@ -1349,8 +1361,9 @@ ModResult ui_dialog_push(ModContext* context, const UiDialogDesc* desc, UiDialog
|
||||
return MOD_INVALID_ARGUMENT;
|
||||
}
|
||||
}
|
||||
const UiPaneBuildFn build = desc->struct_size >= sizeof(UiDialogDesc) ? desc->build : nullptr;
|
||||
uint64_t handle = 0;
|
||||
const auto result = ui_impl::ui_dialog_push(*mod, *desc, handle);
|
||||
const auto result = ui_impl::ui_dialog_push(*mod, *desc, build, handle);
|
||||
if (result == MOD_OK && outDialog != nullptr) {
|
||||
*outDialog = handle;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "dusk/settings.h"
|
||||
#include <aurora/aurora.h>
|
||||
#include "dusk/config.hpp"
|
||||
#include "dusk/ui/ui.hpp"
|
||||
#include "dusk/game_mode.hpp"
|
||||
|
||||
namespace dusk {
|
||||
@@ -16,6 +17,7 @@ UserSettings g_userSettings = {
|
||||
.rememberWindowSize {"video.rememberWindowSize", false},
|
||||
.lastWindowWidth {"video.lastWindowWidth", 0},
|
||||
.lastWindowHeight {"video.lastWindowHeight", 0},
|
||||
.uiScale {"video.uiScale", 100},
|
||||
},
|
||||
|
||||
.audio = {
|
||||
@@ -228,6 +230,8 @@ void registerSettings() {
|
||||
Register(g_userSettings.video.rememberWindowSize);
|
||||
Register(g_userSettings.video.lastWindowWidth);
|
||||
Register(g_userSettings.video.lastWindowHeight);
|
||||
Register(g_userSettings.video.uiScale,
|
||||
[](const int&, const int&) { dusk::ui::apply_scale(); });
|
||||
|
||||
// Audio
|
||||
Register(g_userSettings.audio.masterVolume);
|
||||
|
||||
@@ -145,6 +145,7 @@ struct UserSettings {
|
||||
ConfigVar<bool> rememberWindowSize;
|
||||
ConfigVar<int> lastWindowWidth;
|
||||
ConfigVar<int> lastWindowHeight;
|
||||
ConfigVar<int> uiScale;
|
||||
} video;
|
||||
|
||||
struct {
|
||||
|
||||
+49
-2
@@ -1,5 +1,7 @@
|
||||
#include "modal.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace dusk::ui {
|
||||
|
||||
Modal::Modal(Props props) : WindowSmall("modal", "modal-dialog"), mProps(std::move(props)) {
|
||||
@@ -23,6 +25,9 @@ Modal::Modal(Props props) : WindowSmall("modal", "modal-dialog"), mProps(std::mo
|
||||
body->SetClass("modal-body", true);
|
||||
body->SetInnerRML(mProps.bodyRml);
|
||||
|
||||
mContentRoot = append(mDialog, "div");
|
||||
mContentRoot->SetClass("modal-content", true);
|
||||
|
||||
auto* actions = append(mDialog, "div");
|
||||
actions->SetClass("modal-actions", true);
|
||||
if (props.isVertical) {
|
||||
@@ -36,12 +41,36 @@ Modal::Modal(Props props) : WindowSmall("modal", "modal-dialog"), mProps(std::mo
|
||||
mDoAud_seStartMenu(kSoundWindowOpen);
|
||||
}
|
||||
|
||||
void Modal::update() {
|
||||
if (mContentPane != nullptr) {
|
||||
mContentPane->update();
|
||||
}
|
||||
if (mPendingAction) {
|
||||
auto action = std::move(mPendingAction);
|
||||
action(*this);
|
||||
}
|
||||
WindowSmall::update();
|
||||
}
|
||||
|
||||
Pane& Modal::content_pane() {
|
||||
if (mContentPane == nullptr) {
|
||||
mContentRoot->SetClass("active", true);
|
||||
mContentPane = std::make_unique<Pane>(mContentRoot, Pane::Type::Uncontrolled);
|
||||
}
|
||||
return *mContentPane;
|
||||
}
|
||||
|
||||
void Modal::add_action(ModalAction action) {
|
||||
auto* actions = mDialog->QuerySelector(".modal-actions");
|
||||
auto btn = std::make_unique<Button>(actions, action.label);
|
||||
btn->root()->SetClass("modal-btn", true);
|
||||
btn->on_pressed([this, callback = std::move(action.onPressed)] {
|
||||
if (callback) {
|
||||
if (!callback) {
|
||||
return;
|
||||
}
|
||||
if (mContentPane != nullptr) {
|
||||
mPendingAction = callback;
|
||||
} else {
|
||||
callback(*this);
|
||||
}
|
||||
});
|
||||
@@ -68,6 +97,9 @@ void Modal::set_icon(const Rml::String& icon) {
|
||||
}
|
||||
|
||||
bool Modal::focus() {
|
||||
if (mContentPane != nullptr && mContentPane->focus()) {
|
||||
return true;
|
||||
}
|
||||
if (!mButtons.empty()) {
|
||||
return mButtons.front()->focus();
|
||||
}
|
||||
@@ -89,6 +121,22 @@ bool Modal::handle_nav_command(Rml::Event& event, NavCommand cmd) {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto* target = event.GetTargetElement();
|
||||
if (mContentPane != nullptr && mContentPane->contains(target) && cmd == NavCommand::Down &&
|
||||
!mButtons.empty() && mButtons.front()->focus())
|
||||
{
|
||||
mDoAud_seStartMenu(kSoundItemFocus);
|
||||
return true;
|
||||
}
|
||||
if (mContentPane != nullptr && cmd == NavCommand::Up &&
|
||||
std::ranges::any_of(
|
||||
mButtons, [target](const auto& button) { return button->contains(target); }) &&
|
||||
mContentPane->focus_last())
|
||||
{
|
||||
mDoAud_seStartMenu(kSoundItemFocus);
|
||||
return true;
|
||||
}
|
||||
|
||||
int direction = 0;
|
||||
NavCommand prevCommand = mProps.isVertical ? NavCommand::Up : NavCommand::Left;
|
||||
NavCommand nextCommand = mProps.isVertical ? NavCommand::Down : NavCommand::Right;
|
||||
@@ -100,7 +148,6 @@ bool Modal::handle_nav_command(Rml::Event& event, NavCommand cmd) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto* target = event.GetTargetElement();
|
||||
for (int i = 0; i < static_cast<int>(mButtons.size()); ++i) {
|
||||
if (mButtons[i]->contains(target)) {
|
||||
const int next = i + direction;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "button.hpp"
|
||||
#include "pane.hpp"
|
||||
#include "window.hpp"
|
||||
|
||||
namespace dusk::ui {
|
||||
@@ -20,13 +21,15 @@ public:
|
||||
std::function<void(Modal&)> onDismiss;
|
||||
Rml::String variant;
|
||||
Rml::String icon = "";
|
||||
bool isVertical;
|
||||
bool isVertical = false;
|
||||
};
|
||||
|
||||
explicit Modal(Props props);
|
||||
|
||||
void update() override;
|
||||
bool focus() override;
|
||||
|
||||
Pane& content_pane();
|
||||
void add_action(ModalAction action);
|
||||
void set_body(const Rml::String& bodyRml);
|
||||
void set_icon(const Rml::String& icon);
|
||||
@@ -38,7 +41,10 @@ private:
|
||||
void dismiss();
|
||||
|
||||
Props mProps;
|
||||
std::vector<std::unique_ptr<Button> > mButtons;
|
||||
Rml::Element* mContentRoot = nullptr;
|
||||
std::unique_ptr<Pane> mContentPane;
|
||||
std::function<void(Modal&)> mPendingAction;
|
||||
std::vector<std::unique_ptr<Button>> mButtons;
|
||||
};
|
||||
|
||||
} // namespace dusk::ui
|
||||
|
||||
@@ -163,6 +163,15 @@ bool Pane::focus() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Pane::focus_last() {
|
||||
for (auto child = mChildren.rbegin(); child != mChildren.rend(); ++child) {
|
||||
if ((*child)->focus()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Rml::Element* Pane::add_section(const Rml::String& text) {
|
||||
auto* elem = append(mRoot, "div");
|
||||
elem->SetClass("section-heading", true);
|
||||
|
||||
@@ -17,6 +17,7 @@ public:
|
||||
explicit Pane(Rml::Element* parent, Type type);
|
||||
|
||||
bool focus() override;
|
||||
bool focus_last();
|
||||
void update() override;
|
||||
|
||||
void set_selected_item(int index);
|
||||
|
||||
@@ -52,6 +52,10 @@ Popover::Popover(Rml::Element* anchor, Side side, const Rml::String& windowClass
|
||||
true);
|
||||
}
|
||||
|
||||
Popover::~Popover() {
|
||||
notify_close(false);
|
||||
}
|
||||
|
||||
void Popover::show() {
|
||||
Document::show();
|
||||
reposition();
|
||||
@@ -60,7 +64,7 @@ void Popover::show() {
|
||||
}
|
||||
|
||||
void Popover::hide(bool close) {
|
||||
notify_close();
|
||||
notify_close(true);
|
||||
mBody->RemoveAttribute("open");
|
||||
mPendingClose = close;
|
||||
}
|
||||
@@ -136,13 +140,13 @@ void Popover::reposition() {
|
||||
mBody->SetProperty(Rml::PropertyId::Top, Rml::Property{pos.y, Rml::Unit::PX});
|
||||
}
|
||||
|
||||
void Popover::notify_close() {
|
||||
void Popover::notify_close(bool restoreFocus) {
|
||||
if (mOnClose) {
|
||||
auto callback = std::move(mOnClose);
|
||||
mOnClose = nullptr;
|
||||
callback();
|
||||
}
|
||||
if (mAnchor != nullptr && mAnchor->IsVisible(true)) {
|
||||
if (restoreFocus && mAnchor != nullptr && mAnchor->IsVisible(true)) {
|
||||
mAnchor->Focus(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ public:
|
||||
};
|
||||
|
||||
Popover(Rml::Element* anchor, Side side, const Rml::String& windowClass = "");
|
||||
~Popover() override;
|
||||
|
||||
void show() override;
|
||||
void hide(bool close) override;
|
||||
@@ -32,7 +33,7 @@ protected:
|
||||
|
||||
private:
|
||||
void reposition();
|
||||
void notify_close();
|
||||
void notify_close(bool restoreFocus);
|
||||
|
||||
Rml::Element* mAnchor = nullptr;
|
||||
Side mSide;
|
||||
|
||||
@@ -770,6 +770,12 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) {
|
||||
},
|
||||
.isDisabled = [] { return IsMobile; },
|
||||
});
|
||||
|
||||
config_int_select(leftPane, rightPane, getSettings().video.uiScale,
|
||||
"UI Scale",
|
||||
"Scales the Dusklight interface relative to the display's DPI scale. Has no effect on the game's UI and HUD.",
|
||||
50, 200, 25, {}, {}, "%");
|
||||
|
||||
leftPane.add_section("Resolution");
|
||||
graphics_tuner_control(*this, leftPane, rightPane,
|
||||
getSettings().game.internalResolutionScale,
|
||||
|
||||
@@ -214,6 +214,8 @@ void handle_event(const SDL_Event& event) noexcept {
|
||||
});
|
||||
}
|
||||
sConnectedGamepads.erase(event.gdevice.which);
|
||||
} else if (event.type == SDL_EVENT_WINDOW_DISPLAY_SCALE_CHANGED) {
|
||||
apply_scale();
|
||||
}
|
||||
input::handle_event(event);
|
||||
}
|
||||
@@ -491,4 +493,14 @@ bool consume_menu_notification_request() noexcept {
|
||||
return requested;
|
||||
}
|
||||
|
||||
void apply_scale() noexcept {
|
||||
const auto userScale = getSettings().video.uiScale.getValue();
|
||||
auto scale = 0.0f;
|
||||
if (userScale != 0) {
|
||||
const auto displayScale = aurora::window::get_window_size().scale;
|
||||
scale = static_cast<float>(userScale) / 100.0f * (displayScale > 0.0f ? displayScale : 1.0f);
|
||||
}
|
||||
aurora::rmlui::set_ui_scale(scale);
|
||||
}
|
||||
|
||||
} // namespace dusk::ui
|
||||
|
||||
@@ -114,4 +114,6 @@ bool consume_menu_notification_request() noexcept;
|
||||
const char* battery_icon(SDL_PowerState state, int level) noexcept;
|
||||
const char* connection_state_icon(SDL_JoystickConnectionState state) noexcept;
|
||||
|
||||
void apply_scale() noexcept;
|
||||
|
||||
} // namespace dusk::ui
|
||||
|
||||
@@ -776,6 +776,7 @@ int game_main(int argc, char* argv[]) {
|
||||
|
||||
dusk::texture_replacements::reload();
|
||||
dusk::ui::initialize();
|
||||
dusk::ui::apply_scale();
|
||||
dusk::ui::push_document(std::make_unique<dusk::ui::Overlay>(), true, true);
|
||||
dusk::ui::push_document(std::make_unique<dusk::ui::TouchControls>(), false, true);
|
||||
dusk::ui::push_document(std::make_unique<dusk::ui::MenuBar>(), false);
|
||||
|
||||
Reference in New Issue
Block a user