mirror of
https://github.com/Zelda64Recomp/Zelda64Recomp
synced 2025-10-05 16:12:49 +02:00
1.2 Release Candidate (#572)
* Remove dummy description for mod config options
* Tag release candidate version
* Apply min width to element triggering rmlui assert (#573)
* Restore 0th day (#574)
* Handle controller up events even while binding inputs to avoid spamming the bind button
* Add MouseButton UI event and use it to fix focus issue on radio, also fix sliders not moving until mouse is released
* Bump version string to 1.2.0-rc2
* mod configure menu description padding set to 16
* Added the ability for focus to set the current mod config option description (#576)
* Added the ability for focus to set the current mod config option description
* add focus to text input
* only clear description if element matches
* Fix race condition crash when setting element text, bump version to 1.2.0-rc3
* Revert "Fix race condition crash when setting element text, bump version to 1.2.0-rc3"
This reverts commit 4934a04d8a
.
* Defer setting an element's text if it has children to fix race condition crash, bump version to 1.2.0-rc3
* Defer remaining set_text calls to prevent another race conditionresource
* Update runtime to fix some issues that could happen after mod conflicts
and bump version to 1.2.0-rc4
* Update runtime to fix regenerated functions using the wrong event index and bump version to 1.2.0-rc5
* Add support for suffixed .so. files. Also prevent dropping extracted dynamic libraries.
* Update RT64 commit to fix cstdint include for re-spirv.
* Bump version to rc6.
* Dummy commit to fix CI bot
* Use compile-time macro for Flatpak instead.
* Rename macro.
* Bump version to 1.2.0-rc7
* Fix define on flatpak, add cwd behavior.
* Temporarily disable current working dir code.
* Add the cmake option for flatpak.
* Bump version to 1.2.0-rc8
* Update MacPorts. (#578)
* Update MacPorts.
* Try GitHub runner.
* Deselect universal, return to blaze.
* pull universal libiconv first
* Fix controller nav issues in config menu, bump version to 1.2.0-rc9
---------
Co-authored-by: thecozies <79979276+thecozies@users.noreply.github.com>
Co-authored-by: LittleCube <littlecubehax@gmail.com>
Co-authored-by: Dario <dariosamo@gmail.com>
This commit is contained in:
8
.github/macos/macports.yaml
vendored
8
.github/macos/macports.yaml
vendored
@@ -1,4 +1,4 @@
|
||||
version: '2.9.3'
|
||||
version: '2.10.6'
|
||||
prefix: '/opt/local'
|
||||
variants:
|
||||
select:
|
||||
@@ -6,9 +6,11 @@ variants:
|
||||
- metal
|
||||
deselect: x11
|
||||
ports:
|
||||
- name: clang-18
|
||||
- name: llvm-18
|
||||
- name: libiconv
|
||||
select: universal
|
||||
- name: libsdl2
|
||||
select: universal
|
||||
- name: freetype
|
||||
select: universal
|
||||
- name: clang-18
|
||||
- name: llvm-18
|
||||
|
@@ -25,6 +25,10 @@ if (APPLE)
|
||||
enable_language(OBJC OBJCXX)
|
||||
endif()
|
||||
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
option(RECOMP_FLATPAK "Configure the build for Flatpak compatibility." OFF)
|
||||
endif()
|
||||
|
||||
# Avoid warning about DOWNLOAD_EXTRACT_TIMESTAMP in CMake 3.24:
|
||||
if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.24.0")
|
||||
cmake_policy(SET CMP0135 NEW)
|
||||
@@ -41,6 +45,10 @@ set(RT64_STATIC TRUE)
|
||||
set(RT64_SDL_WINDOW_VULKAN TRUE)
|
||||
add_compile_definitions(HLSL_CPU)
|
||||
|
||||
if (RECOMP_FLATPAK)
|
||||
add_compile_definitions(RECOMP_FLATPAK)
|
||||
endif()
|
||||
|
||||
add_subdirectory(${CMAKE_SOURCE_DIR}/lib/rt64 ${CMAKE_BINARY_DIR}/rt64)
|
||||
|
||||
# set(BUILD_SHARED_LIBS_SAVED "${BUILD_SHARED_LIBS}")
|
||||
|
@@ -1656,6 +1656,7 @@ scrollbarhorizontal sliderbar {
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
width: 268dp;
|
||||
min-width: 1dp;
|
||||
height: 128dp;
|
||||
margin-right: 10dp;
|
||||
}
|
||||
|
@@ -342,6 +342,8 @@ $stick-size: 200;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
width: space(268);
|
||||
// WORKAROUND FIX: prevents RMLui assert error
|
||||
min-width: 1dp;
|
||||
height: space(128);
|
||||
margin-right: space(10);
|
||||
}
|
||||
|
@@ -28,7 +28,7 @@
|
||||
"./N64Recomp us.rev1.toml",
|
||||
"./RSPRecomp aspMain.us.rev1.toml",
|
||||
"./RSPRecomp njpgdspMain.us.rev1.toml",
|
||||
"cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER=clang -DCMAKE_MAKE_PROGRAM=ninja -DPATCHES_C_COMPILER=clang -DPATCHES_LD=ld.lld -G Ninja -S . -B cmake-build",
|
||||
"cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER=clang -DCMAKE_MAKE_PROGRAM=ninja -DPATCHES_C_COMPILER=clang -DPATCHES_LD=ld.lld -DRECOMP_FLATPAK=ON -G Ninja -S . -B cmake-build",
|
||||
"cmake --build cmake-build --config Release --target Zelda64Recompiled --parallel",
|
||||
"rm -rf assets/scss",
|
||||
"mkdir -p /app/bin",
|
||||
|
@@ -69,6 +69,7 @@ namespace recompui {
|
||||
};
|
||||
|
||||
void set_config_tab(ConfigTab tab);
|
||||
int config_tab_to_index(ConfigTab tab);
|
||||
Rml::ElementTabSet* get_config_tabset();
|
||||
Rml::Element* get_mod_tab();
|
||||
void set_config_tabset_mod_nav();
|
||||
|
Submodule lib/N64ModernRuntime updated: 0aa75b98ba...c5e268aa0f
2
lib/rt64
2
lib/rt64
Submodule lib/rt64 updated: 793abe0bc3...ada6cc62c4
@@ -11,7 +11,7 @@ void Sleep_Msec(u32 ms);
|
||||
extern u16 D_801F6AF0;
|
||||
extern u8 D_801F6AF2;
|
||||
|
||||
// @recomp Patched to not wait a hardcoded amount of time for the save to complete.
|
||||
// @recomp Patched to wait a much shorter amount of time for the save to complete.
|
||||
RECOMP_PATCH void Sram_UpdateWriteToFlashDefault(SramContext* sramCtx) {
|
||||
if (sramCtx->status == 2) {
|
||||
if (SysFlashrom_IsBusy() != 0) { // if task running
|
||||
@@ -23,13 +23,13 @@ RECOMP_PATCH void Sram_UpdateWriteToFlashDefault(SramContext* sramCtx) {
|
||||
sramCtx->status = 4;
|
||||
}
|
||||
}
|
||||
} else if (sramCtx->status == 4) {
|
||||
// @recomp Patched to check status instead of using a hardcoded wait.
|
||||
} else if (OSTIME_TO_TIMER(osGetTime() - sramCtx->startWriteOsTime) >= SECONDS_TO_TIMER_PRECISE(0, 25)) {
|
||||
// @recomp Patched to wait a much shorter amount of time.
|
||||
sramCtx->status = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// @recomp Patched to not wait a hardcoded amount of time for the save to complete.
|
||||
// @recomp Patched to wait a much shorter amount of time for the save to complete.
|
||||
RECOMP_PATCH void Sram_UpdateWriteToFlashOwlSave(SramContext* sramCtx) {
|
||||
if (sramCtx->status == 7) {
|
||||
if (SysFlashrom_IsBusy() != 0) { // Is task running
|
||||
@@ -49,8 +49,8 @@ RECOMP_PATCH void Sram_UpdateWriteToFlashOwlSave(SramContext* sramCtx) {
|
||||
sramCtx->status = 4;
|
||||
}
|
||||
}
|
||||
} else if (sramCtx->status == 4) {
|
||||
// @recomp Patched to check status instead of using a hardcoded wait.
|
||||
} else if (OSTIME_TO_TIMER(osGetTime() - sramCtx->startWriteOsTime) >= SECONDS_TO_TIMER_PRECISE(0, 25)) {
|
||||
// @recomp Patched to wait a much shorter amount of time.
|
||||
sramCtx->status = 0;
|
||||
bzero(sramCtx->saveBuf, SAVE_BUFFER_SIZE);
|
||||
gSaveContext.save.isOwlSave = false;
|
||||
|
@@ -287,6 +287,10 @@ bool sdl_event_filter(void* userdata, SDL_Event* event) {
|
||||
case SDL_EventType::SDL_DROPCOMPLETE:
|
||||
recompui::drop_files(DropState.files_dropped);
|
||||
break;
|
||||
case SDL_EventType::SDL_CONTROLLERBUTTONUP:
|
||||
// Always queue button up events to avoid missing them during binding.
|
||||
recompui::queue_event(*event);
|
||||
break;
|
||||
default:
|
||||
queue_if_enabled(event);
|
||||
break;
|
||||
|
@@ -48,7 +48,7 @@
|
||||
|
||||
#include "../../lib/rt64/src/contrib/stb/stb_image.h"
|
||||
|
||||
const std::string version_string = "1.2.0-dev";
|
||||
const std::string version_string = "1.2.0-rc9";
|
||||
|
||||
template<typename... Ts>
|
||||
void exit_error(const char* str, Ts ...args) {
|
||||
@@ -601,7 +601,14 @@ int main(int argc, char** argv) {
|
||||
// Force wasapi on Windows, as there seems to be some issue with sample queueing with directsound currently.
|
||||
SDL_setenv("SDL_AUDIODRIVER", "wasapi", true);
|
||||
#endif
|
||||
//printf("Current dir: %ls\n", std::filesystem::current_path().c_str());
|
||||
|
||||
#if defined(__linux__) && defined(RECOMP_FLATPAK)
|
||||
// When using Flatpak, applications tend to launch from the home directory by default.
|
||||
// Mods might use the current working directory to store the data, so we switch it to a directory
|
||||
// with persistent data storage and write permissions under Flatpak to ensure it works.
|
||||
std::error_code ec;
|
||||
std::filesystem::current_path("/var/data", ec);
|
||||
#endif
|
||||
|
||||
// Initialize SDL audio and set the output frequency.
|
||||
SDL_InitSubSystem(SDL_INIT_AUDIO);
|
||||
|
@@ -48,13 +48,11 @@ namespace zelda64 {
|
||||
std::filesystem::path get_program_path() {
|
||||
#if defined(__APPLE__)
|
||||
return get_bundle_resource_directory();
|
||||
#elif defined(__linux__)
|
||||
std::error_code ec;
|
||||
if (std::filesystem::exists("/.flatpak-info", ec)) {
|
||||
#elif defined(__linux__) && defined(RECOMP_FLATPAK)
|
||||
return "/app/bin";
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
return "";
|
||||
#endif
|
||||
}
|
||||
|
||||
std::filesystem::path get_asset_path(const char* asset) {
|
||||
|
@@ -37,6 +37,7 @@ namespace recompui {
|
||||
Element* autofocus_element = nullptr;
|
||||
std::vector<Element*> loose_elements;
|
||||
std::unordered_set<ResourceId> to_update;
|
||||
std::vector<std::tuple<Element*, ResourceId, std::string>> to_set_text;
|
||||
bool captures_input = true;
|
||||
bool captures_mouse = true;
|
||||
Context(Rml::ElementDocument* document) : document(document), root_element(document) {}
|
||||
@@ -67,6 +68,8 @@ enum class ContextErrorType {
|
||||
AddResourceToWrongContext,
|
||||
UpdateElementWithoutContext,
|
||||
UpdateElementInWrongContext,
|
||||
SetTextElementWithoutContext,
|
||||
SetTextElementInWrongContext,
|
||||
GetResourceWithoutOpen,
|
||||
GetResourceFailed,
|
||||
DestroyResourceWithoutOpen,
|
||||
@@ -119,6 +122,12 @@ void context_error(recompui::ContextId id, ContextErrorType type) {
|
||||
case ContextErrorType::UpdateElementInWrongContext:
|
||||
error_message = "Attempted to update a UI element in a different UI context than the one that's open";
|
||||
break;
|
||||
case ContextErrorType::SetTextElementWithoutContext:
|
||||
error_message = "Attempted to set the text of a UI element with no open UI context";
|
||||
break;
|
||||
case ContextErrorType::SetTextElementInWrongContext:
|
||||
error_message = "Attempted to set the text of a UI element in a different UI context than the one that's open";
|
||||
break;
|
||||
case ContextErrorType::GetResourceWithoutOpen:
|
||||
error_message = "Attempted to get a UI resource with no open UI context";
|
||||
break;
|
||||
@@ -407,6 +416,40 @@ void recompui::ContextId::process_updates() {
|
||||
|
||||
static_cast<Element*>(cur_resource->get())->handle_event(update_event);
|
||||
}
|
||||
|
||||
std::vector<std::tuple<Element*, ResourceId, std::string>> to_set_text = std::move(opened_context->to_set_text);
|
||||
|
||||
// Delete the Rml elements that are pending deletion.
|
||||
for (auto cur_text_update : to_set_text) {
|
||||
Element* element_ptr = std::get<0>(cur_text_update);
|
||||
ResourceId resource = std::get<1>(cur_text_update);
|
||||
std::string& text = std::get<2>(cur_text_update);
|
||||
|
||||
// If the resource ID is valid, prefer that as we can quickly validate if the resource still exists.
|
||||
if (resource != ResourceId::null()) {
|
||||
resource_slotmap::key cur_key{ resource.slot_id };
|
||||
std::unique_ptr<Style>* cur_resource = opened_context->resources.get(cur_key);
|
||||
|
||||
// Make sure the resource exists before setting its text, as it may have been deleted.
|
||||
if (cur_resource == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Perform the text update.
|
||||
static_cast<Element*>(cur_resource->get())->base->SetInnerRML(text);
|
||||
}
|
||||
// Otherwise we use the element pointer, but we need to validate that it still exists before doing so.
|
||||
else {
|
||||
// Scan the current resources to find the target element.
|
||||
for (const std::unique_ptr<Style>& cur_e : opened_context->resources) {
|
||||
if (cur_e.get() == element_ptr) {
|
||||
element_ptr->base->SetInnerRML(text);
|
||||
// We can stop after finding the element.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool recompui::ContextId::captures_input() {
|
||||
@@ -514,6 +557,20 @@ void recompui::ContextId::queue_element_update(ResourceId element) {
|
||||
opened_context->to_update.emplace(element);
|
||||
}
|
||||
|
||||
void recompui::ContextId::queue_set_text(Element* element, std::string&& text) {
|
||||
// Ensure a context is currently opened by this thread.
|
||||
if (opened_context_id == ContextId::null()) {
|
||||
context_error(*this, ContextErrorType::SetTextElementWithoutContext);
|
||||
}
|
||||
|
||||
// Check that the context that was specified is the same one that's currently open.
|
||||
if (*this != opened_context_id) {
|
||||
context_error(*this, ContextErrorType::SetTextElementInWrongContext);
|
||||
}
|
||||
|
||||
opened_context->to_set_text.emplace_back(std::make_tuple(element, element->resource_id, std::move(text)));
|
||||
}
|
||||
|
||||
recompui::Style* recompui::ContextId::create_style() {
|
||||
return add_resource_impl(std::make_unique<Style>());
|
||||
}
|
||||
|
@@ -31,6 +31,7 @@ namespace recompui {
|
||||
|
||||
void add_loose_element(Element* element);
|
||||
void queue_element_update(ResourceId element);
|
||||
void queue_set_text(Element* element, std::string&& text);
|
||||
|
||||
Style* create_style();
|
||||
|
||||
|
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace recompui {
|
||||
|
||||
Clickable::Clickable(Element *parent, bool draggable) : Element(parent, Events(EventType::Click, EventType::Hover, EventType::Enable, draggable ? EventType::Drag : EventType::None)) {
|
||||
Clickable::Clickable(Element *parent, bool draggable) : Element(parent, Events(EventType::Click, EventType::MouseButton, EventType::Hover, EventType::Enable, draggable ? EventType::Drag : EventType::None)) {
|
||||
set_cursor(Cursor::Pointer);
|
||||
if (draggable) {
|
||||
set_drag(Drag::Drag);
|
||||
@@ -14,12 +14,23 @@ namespace recompui {
|
||||
case EventType::Click: {
|
||||
if (is_enabled()) {
|
||||
const EventClick &click = std::get<EventClick>(e.variant);
|
||||
for (const auto &function : pressed_callbacks) {
|
||||
for (const auto &function : clicked_callbacks) {
|
||||
function(click.x, click.y);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
case EventType::MouseButton: {
|
||||
if (is_enabled()) {
|
||||
const EventMouseButton &mousebutton = std::get<EventMouseButton>(e.variant);
|
||||
if (mousebutton.button == MouseButton::Left && mousebutton.pressed) {
|
||||
for (const auto &function : pressed_callbacks) {
|
||||
function(mousebutton.x, mousebutton.y);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
case EventType::Hover:
|
||||
set_style_enabled(hover_state, std::get<EventHover>(e.variant).active && is_enabled());
|
||||
break;
|
||||
@@ -51,6 +62,10 @@ namespace recompui {
|
||||
}
|
||||
}
|
||||
|
||||
void Clickable::add_clicked_callback(std::function<void(float, float)> callback) {
|
||||
clicked_callbacks.emplace_back(callback);
|
||||
}
|
||||
|
||||
void Clickable::add_pressed_callback(std::function<void(float, float)> callback) {
|
||||
pressed_callbacks.emplace_back(callback);
|
||||
}
|
||||
|
@@ -6,6 +6,7 @@ namespace recompui {
|
||||
|
||||
class Clickable : public Element {
|
||||
protected:
|
||||
std::vector<std::function<void(float, float)>> clicked_callbacks;
|
||||
std::vector<std::function<void(float, float)>> pressed_callbacks;
|
||||
std::vector<std::function<void(float, float, DragPhase)>> dragged_callbacks;
|
||||
|
||||
@@ -14,6 +15,7 @@ namespace recompui {
|
||||
std::string_view get_type_name() override { return "Clickable"; }
|
||||
public:
|
||||
Clickable(Element *parent, bool draggable = false);
|
||||
void add_clicked_callback(std::function<void(float, float)> callback);
|
||||
void add_pressed_callback(std::function<void(float, float)> callback);
|
||||
void add_dragged_callback(std::function<void(float, float, DragPhase)> callback);
|
||||
};
|
||||
|
@@ -75,6 +75,11 @@ void Element::register_event_listeners(uint32_t events_enabled) {
|
||||
base->AddEventListener(Rml::EventId::Click, this);
|
||||
}
|
||||
|
||||
if (events_enabled & Events(EventType::MouseButton)) {
|
||||
base->AddEventListener(Rml::EventId::Mousedown, this);
|
||||
base->AddEventListener(Rml::EventId::Mouseup, this);
|
||||
}
|
||||
|
||||
if (events_enabled & Events(EventType::Focus)) {
|
||||
base->AddEventListener(Rml::EventId::Focus, this);
|
||||
base->AddEventListener(Rml::EventId::Blur, this);
|
||||
@@ -152,6 +157,19 @@ void Element::set_id(const std::string& new_id) {
|
||||
base->SetId(new_id);
|
||||
}
|
||||
|
||||
recompui::MouseButton convert_rml_mouse_button(int button) {
|
||||
switch (button) {
|
||||
case 0:
|
||||
return recompui::MouseButton::Left;
|
||||
case 1:
|
||||
return recompui::MouseButton::Right;
|
||||
case 2:
|
||||
return recompui::MouseButton::Middle;
|
||||
default:
|
||||
return recompui::MouseButton::Count;
|
||||
}
|
||||
}
|
||||
|
||||
void Element::ProcessEvent(Rml::Event &event) {
|
||||
ContextId prev_context = recompui::try_close_current_context();
|
||||
ContextId context = ContextId::null();
|
||||
@@ -172,6 +190,22 @@ void Element::ProcessEvent(Rml::Event &event) {
|
||||
case Rml::EventId::Click:
|
||||
handle_event(Event::click_event(event.GetParameter("mouse_x", 0.0f), event.GetParameter("mouse_y", 0.0f)));
|
||||
break;
|
||||
case Rml::EventId::Mousedown:
|
||||
{
|
||||
MouseButton mouse_button = convert_rml_mouse_button(event.GetParameter("button", 3));
|
||||
if (mouse_button != MouseButton::Count) {
|
||||
handle_event(Event::mousebutton_event(event.GetParameter("mouse_x", 0.0f), event.GetParameter("mouse_y", 0.0f), mouse_button, true));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Rml::EventId::Mouseup:
|
||||
{
|
||||
MouseButton mouse_button = convert_rml_mouse_button(event.GetParameter("button", 3));
|
||||
if (mouse_button != MouseButton::Count) {
|
||||
handle_event(Event::mousebutton_event(event.GetParameter("mouse_x", 0.0f), event.GetParameter("mouse_y", 0.0f), mouse_button, false));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Rml::EventId::Keydown:
|
||||
switch ((Rml::Input::KeyIdentifier)event.GetParameter<int>("key_identifier", 0)) {
|
||||
case Rml::Input::KeyIdentifier::KI_LEFT:
|
||||
@@ -343,8 +377,12 @@ std::string escape_rml(std::string_view string)
|
||||
|
||||
void Element::set_text(std::string_view text) {
|
||||
if (can_set_text) {
|
||||
// Queue the text update. If it's applied immediately, it might happen
|
||||
// while the document is being updated or rendered. This can cause a crash
|
||||
// due to the child elements being deleted while the document is being updated.
|
||||
// Queueing them defers it to the update thread, which prevents that issue.
|
||||
// Escape the string into Rml to prevent element injection.
|
||||
base->SetInnerRML(escape_rml(text));
|
||||
get_current_context().queue_set_text(this, escape_rml(text));
|
||||
}
|
||||
else {
|
||||
assert(false && "Attempted to set text of an element that cannot have its text set.");
|
||||
|
@@ -6,7 +6,7 @@ namespace recompui {
|
||||
|
||||
// RadioOption
|
||||
|
||||
RadioOption::RadioOption(Element *parent, std::string_view name, uint32_t index) : Element(parent, Events(EventType::Click, EventType::Focus, EventType::Hover, EventType::Enable, EventType::Update), "label", true) {
|
||||
RadioOption::RadioOption(Element *parent, std::string_view name, uint32_t index) : Element(parent, Events(EventType::MouseButton, EventType::Click, EventType::Focus, EventType::Hover, EventType::Enable, EventType::Update), "label", true) {
|
||||
this->index = index;
|
||||
|
||||
enable_focus();
|
||||
@@ -37,12 +37,24 @@ namespace recompui {
|
||||
pressed_callback = callback;
|
||||
}
|
||||
|
||||
void RadioOption::set_focus_callback(std::function<void(bool)> callback) {
|
||||
focus_callback = callback;
|
||||
}
|
||||
|
||||
void RadioOption::set_selected_state(bool enable) {
|
||||
set_style_enabled(checked_state, enable);
|
||||
}
|
||||
|
||||
void RadioOption::process_event(const Event &e) {
|
||||
switch (e.type) {
|
||||
case EventType::MouseButton:
|
||||
{
|
||||
const EventMouseButton &mousebutton = std::get<EventMouseButton>(e.variant);
|
||||
if (mousebutton.button == MouseButton::Left && mousebutton.pressed) {
|
||||
pressed_callback(index);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EventType::Click:
|
||||
pressed_callback(index);
|
||||
break;
|
||||
@@ -59,6 +71,9 @@ namespace recompui {
|
||||
if (active) {
|
||||
queue_update();
|
||||
}
|
||||
if (focus_callback != nullptr) {
|
||||
focus_callback(active);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EventType::Update:
|
||||
@@ -67,10 +82,6 @@ namespace recompui {
|
||||
apply_styles();
|
||||
queue_update();
|
||||
}
|
||||
if (focus_queued) {
|
||||
focus_queued = false;
|
||||
focus();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -106,7 +117,7 @@ namespace recompui {
|
||||
}, val);
|
||||
}
|
||||
|
||||
Radio::Radio(Element *parent) : Container(parent, FlexDirection::Row, JustifyContent::FlexStart, Events(EventType::Focus)) {
|
||||
Radio::Radio(Element *parent) : Container(parent, FlexDirection::Row, JustifyContent::FlexStart, Events(EventType::Focus, EventType::Update)) {
|
||||
set_gap(24.0f);
|
||||
set_align_items(AlignItems::FlexStart);
|
||||
enable_focus();
|
||||
@@ -118,10 +129,18 @@ namespace recompui {
|
||||
if (!options.empty()) {
|
||||
if (std::get<EventFocus>(e.variant).active) {
|
||||
blur();
|
||||
options[index]->queue_focus();
|
||||
queue_child_focus();
|
||||
}
|
||||
if (focus_callback != nullptr) {
|
||||
focus_callback(std::get<EventFocus>(e.variant).active);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EventType::Update:
|
||||
if (child_focus_queued) {
|
||||
child_focus_queued = false;
|
||||
options[index]->focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,7 +150,12 @@ namespace recompui {
|
||||
|
||||
void Radio::add_option(std::string_view name) {
|
||||
RadioOption *option = get_current_context().create_element<RadioOption>(this, name, uint32_t(options.size()));
|
||||
option->set_pressed_callback([this](uint32_t index){ option_selected(index); });
|
||||
option->set_pressed_callback([this](uint32_t index){ options[index]->focus(); option_selected(index); });
|
||||
option->set_focus_callback([this](bool active) {
|
||||
if (focus_callback != nullptr) {
|
||||
focus_callback(active);
|
||||
}
|
||||
});
|
||||
options.emplace_back(option);
|
||||
|
||||
// The first option was added, select it.
|
||||
@@ -157,6 +181,10 @@ namespace recompui {
|
||||
index_changed_callbacks.emplace_back(callback);
|
||||
}
|
||||
|
||||
void Radio::set_focus_callback(std::function<void(bool)> callback) {
|
||||
focus_callback = callback;
|
||||
}
|
||||
|
||||
void Radio::set_nav_auto(NavDirection dir) {
|
||||
Element::set_nav_auto(dir);
|
||||
if (!options.empty()) {
|
||||
|
@@ -10,16 +10,16 @@ namespace recompui {
|
||||
Style checked_style;
|
||||
Style pulsing_style;
|
||||
std::function<void(uint32_t)> pressed_callback = nullptr;
|
||||
std::function<void(bool)> focus_callback = nullptr;
|
||||
uint32_t index = 0;
|
||||
bool focus_queued = false;
|
||||
protected:
|
||||
virtual void process_event(const Event &e) override;
|
||||
std::string_view get_type_name() override { return "LabelRadioOption"; }
|
||||
public:
|
||||
RadioOption(Element *parent, std::string_view name, uint32_t index);
|
||||
void set_pressed_callback(std::function<void(uint32_t)> callback);
|
||||
void set_focus_callback(std::function<void(bool)> callback);
|
||||
void set_selected_state(bool enable);
|
||||
void queue_focus() { focus_queued = true; queue_update(); }
|
||||
};
|
||||
|
||||
class Radio : public Container {
|
||||
@@ -27,6 +27,8 @@ namespace recompui {
|
||||
std::vector<RadioOption *> options;
|
||||
uint32_t index = 0;
|
||||
std::vector<std::function<void(uint32_t)>> index_changed_callbacks;
|
||||
std::function<void(bool)> focus_callback = nullptr;
|
||||
bool child_focus_queued = false;
|
||||
|
||||
void set_index_internal(uint32_t index, bool setup, bool trigger_callbacks);
|
||||
void option_selected(uint32_t index);
|
||||
@@ -35,6 +37,7 @@ namespace recompui {
|
||||
protected:
|
||||
virtual void process_event(const Event &e) override;
|
||||
std::string_view get_type_name() override { return "LabelRadio"; }
|
||||
void queue_child_focus() { child_focus_queued = true; queue_update(); }
|
||||
public:
|
||||
Radio(Element *parent);
|
||||
virtual ~Radio();
|
||||
@@ -42,6 +45,7 @@ namespace recompui {
|
||||
void set_index(uint32_t index);
|
||||
uint32_t get_index() const;
|
||||
void add_index_changed_callback(std::function<void(uint32_t)> callback);
|
||||
void set_focus_callback(std::function<void(bool)> callback);
|
||||
size_t num_options() const { return options.size(); }
|
||||
RadioOption* get_option_element(size_t option_index) { return options[option_index]; }
|
||||
RadioOption* get_current_option_element() { return options.empty() ? nullptr : options[index]; }
|
||||
|
@@ -25,7 +25,7 @@ namespace recompui {
|
||||
}
|
||||
}
|
||||
|
||||
void Slider::bar_clicked(float x, float) {
|
||||
void Slider::bar_pressed(float x, float) {
|
||||
update_value_from_mouse(x);
|
||||
}
|
||||
|
||||
@@ -81,6 +81,9 @@ namespace recompui {
|
||||
if (active) {
|
||||
queue_update();
|
||||
}
|
||||
if (focus_callback != nullptr) {
|
||||
focus_callback(active);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EventType::Update:
|
||||
@@ -151,7 +154,7 @@ namespace recompui {
|
||||
|
||||
slider_element = context.create_element<Clickable>(this, true);
|
||||
slider_element->set_flex(1.0f, 0.0f);
|
||||
slider_element->add_pressed_callback([this](float x, float y){ bar_clicked(x, y); focus(); });
|
||||
slider_element->add_pressed_callback([this](float x, float y){ bar_pressed(x, y); focus(); });
|
||||
slider_element->add_dragged_callback([this](float x, float y, recompui::DragPhase phase){ bar_dragged(x, y, phase); focus(); });
|
||||
|
||||
{
|
||||
@@ -160,7 +163,7 @@ namespace recompui {
|
||||
bar_element->set_height(2.0f);
|
||||
bar_element->set_margin_top(8.0f);
|
||||
bar_element->set_background_color(Color{ 255, 255, 255, 50 });
|
||||
bar_element->add_pressed_callback([this](float x, float y){ bar_clicked(x, y); focus(); });
|
||||
bar_element->add_pressed_callback([this](float x, float y){ bar_pressed(x, y); focus(); });
|
||||
bar_element->add_dragged_callback([this](float x, float y, recompui::DragPhase phase){ bar_dragged(x, y, phase); focus(); });
|
||||
|
||||
circle_element = context.create_element<Clickable>(bar_element, true);
|
||||
@@ -219,6 +222,10 @@ namespace recompui {
|
||||
value_changed_callbacks.emplace_back(callback);
|
||||
}
|
||||
|
||||
void Slider::set_focus_callback(std::function<void(bool)> callback) {
|
||||
focus_callback = callback;
|
||||
}
|
||||
|
||||
void Slider::do_step(bool increment) {
|
||||
double new_value = value;
|
||||
if (increment) {
|
||||
|
@@ -23,9 +23,10 @@ namespace recompui {
|
||||
double max_value = 100.0;
|
||||
double step_value = 0.0;
|
||||
std::vector<std::function<void(double)>> value_changed_callbacks;
|
||||
std::function<void(bool)> focus_callback = nullptr;
|
||||
|
||||
void set_value_internal(double v, bool setup, bool trigger_callbacks);
|
||||
void bar_clicked(float x, float y);
|
||||
void bar_pressed(float x, float y);
|
||||
void bar_dragged(float x, float y, DragPhase phase);
|
||||
void circle_dragged(float x, float y, DragPhase phase);
|
||||
void update_value_from_mouse(float x);
|
||||
@@ -51,6 +52,7 @@ namespace recompui {
|
||||
double get_step_value() const;
|
||||
void add_value_changed_callback(std::function<void(double)> callback);
|
||||
void do_step(bool increment);
|
||||
void set_focus_callback(std::function<void(bool)> callback);
|
||||
};
|
||||
|
||||
} // namespace recompui
|
||||
|
@@ -16,12 +16,19 @@ namespace recompui {
|
||||
|
||||
break;
|
||||
}
|
||||
case EventType::Focus: {
|
||||
const EventFocus &event = std::get<EventFocus>(e.variant);
|
||||
if (focus_callback != nullptr) {
|
||||
focus_callback(event.active);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
TextInput::TextInput(Element *parent, bool text_visible) : Element(parent, Events(EventType::Text), "input") {
|
||||
TextInput::TextInput(Element *parent, bool text_visible) : Element(parent, Events(EventType::Text, EventType::Focus), "input") {
|
||||
if (!text_visible) {
|
||||
set_attribute("type", "password");
|
||||
}
|
||||
@@ -48,4 +55,7 @@ namespace recompui {
|
||||
text_changed_callbacks.emplace_back(callback);
|
||||
}
|
||||
|
||||
void TextInput::set_focus_callback(std::function<void(bool)> callback) {
|
||||
focus_callback = callback;
|
||||
}
|
||||
};
|
@@ -8,6 +8,7 @@ namespace recompui {
|
||||
private:
|
||||
std::string text;
|
||||
std::vector<std::function<void(const std::string &)>> text_changed_callbacks;
|
||||
std::function<void(bool)> focus_callback = nullptr;
|
||||
protected:
|
||||
virtual void process_event(const Event &e) override;
|
||||
std::string_view get_type_name() override { return "TextInput"; }
|
||||
@@ -16,6 +17,7 @@ namespace recompui {
|
||||
void set_text(std::string_view text);
|
||||
const std::string &get_text();
|
||||
void add_text_changed_callback(std::function<void(const std::string &)> callback);
|
||||
void set_focus_callback(std::function<void(bool)> callback);
|
||||
};
|
||||
|
||||
} // namespace recompui
|
||||
|
@@ -33,6 +33,7 @@ namespace recompui {
|
||||
Text,
|
||||
Update,
|
||||
Navigate,
|
||||
MouseButton,
|
||||
Count
|
||||
};
|
||||
|
||||
@@ -50,6 +51,13 @@ namespace recompui {
|
||||
Left
|
||||
};
|
||||
|
||||
enum class MouseButton {
|
||||
Left,
|
||||
Right,
|
||||
Middle,
|
||||
Count
|
||||
};
|
||||
|
||||
template <typename Enum, typename = std::enable_if_t<std::is_enum_v<Enum>>>
|
||||
constexpr uint32_t Events(Enum first) {
|
||||
return 1u << static_cast<uint32_t>(first);
|
||||
@@ -91,7 +99,14 @@ namespace recompui {
|
||||
NavDirection direction;
|
||||
};
|
||||
|
||||
using EventVariant = std::variant<EventClick, EventFocus, EventHover, EventEnable, EventDrag, EventText, EventNavigate, std::monostate>;
|
||||
struct EventMouseButton {
|
||||
float x;
|
||||
float y;
|
||||
MouseButton button;
|
||||
bool pressed;
|
||||
};
|
||||
|
||||
using EventVariant = std::variant<EventClick, EventFocus, EventHover, EventEnable, EventDrag, EventText, EventNavigate, EventMouseButton, std::monostate>;
|
||||
|
||||
struct Event {
|
||||
EventType type;
|
||||
@@ -153,6 +168,13 @@ namespace recompui {
|
||||
e.variant = EventNavigate{ direction };
|
||||
return e;
|
||||
}
|
||||
|
||||
static Event mousebutton_event(float x, float y, MouseButton button, bool pressed) {
|
||||
Event e;
|
||||
e.type = EventType::MouseButton;
|
||||
e.variant = EventMouseButton{ x, y, button, pressed };
|
||||
return e;
|
||||
}
|
||||
};
|
||||
|
||||
enum class Display {
|
||||
|
@@ -22,7 +22,7 @@ Rml::DataModelHandle sound_options_model_handle;
|
||||
// True if controller config menu is open, false if keyboard config menu is open, undefined otherwise
|
||||
bool configuring_controller = false;
|
||||
|
||||
static int config_tab_to_index(recompui::ConfigTab tab) {
|
||||
int recompui::config_tab_to_index(recompui::ConfigTab tab) {
|
||||
switch (tab) {
|
||||
case recompui::ConfigTab::General:
|
||||
return 0;
|
||||
@@ -472,7 +472,7 @@ class ConfigTabsetListener : public Rml::EventListener {
|
||||
void ProcessEvent(Rml::Event& event) override {
|
||||
if (event.GetId() == Rml::EventId::Tabchange) {
|
||||
int tab_index = event.GetParameter<int>("tab_index", 0);
|
||||
bool in_mod_tab = (tab_index == config_tab_to_index(recompui::ConfigTab::Mods));
|
||||
bool in_mod_tab = (tab_index == recompui::config_tab_to_index(recompui::ConfigTab::Mods));
|
||||
if (in_mod_tab) {
|
||||
recompui::set_config_tabset_mod_nav();
|
||||
}
|
||||
|
@@ -13,6 +13,9 @@ namespace recompui {
|
||||
void ConfigOptionElement::process_event(const Event &e) {
|
||||
switch (e.type) {
|
||||
case EventType::Hover:
|
||||
if (hover_callback == nullptr) {
|
||||
break;
|
||||
}
|
||||
hover_callback(this, std::get<EventHover>(e.variant).active);
|
||||
break;
|
||||
case EventType::Update:
|
||||
@@ -53,6 +56,10 @@ void ConfigOptionElement::set_hover_callback(std::function<void(ConfigOptionElem
|
||||
hover_callback = callback;
|
||||
}
|
||||
|
||||
void ConfigOptionElement::set_focus_callback(std::function<void(const std::string &, bool)> callback) {
|
||||
focus_callback = callback;
|
||||
}
|
||||
|
||||
const std::string &ConfigOptionElement::get_description() const {
|
||||
return description;
|
||||
}
|
||||
@@ -73,6 +80,9 @@ ConfigOptionSlider::ConfigOptionSlider(Element *parent, double value, double min
|
||||
slider->set_step_value(step_value);
|
||||
slider->set_value(value);
|
||||
slider->add_value_changed_callback([this](double v){ slider_value_changed(v); });
|
||||
slider->set_focus_callback([this](bool active) {
|
||||
focus_callback(option_id, active);
|
||||
});
|
||||
}
|
||||
|
||||
// ConfigOptionTextInput
|
||||
@@ -88,6 +98,9 @@ ConfigOptionTextInput::ConfigOptionTextInput(Element *parent, std::string_view v
|
||||
text_input->set_max_width(400.0f);
|
||||
text_input->set_text(value);
|
||||
text_input->add_text_changed_callback([this](const std::string &text){ text_changed(text); });
|
||||
text_input->set_focus_callback([this](bool active) {
|
||||
focus_callback(option_id, active);
|
||||
});
|
||||
}
|
||||
|
||||
// ConfigOptionRadio
|
||||
@@ -100,6 +113,9 @@ ConfigOptionRadio::ConfigOptionRadio(Element *parent, uint32_t value, const std:
|
||||
this->callback = callback;
|
||||
|
||||
radio = get_current_context().create_element<Radio>(this);
|
||||
radio->set_focus_callback([this](bool active) {
|
||||
focus_callback(option_id, active);
|
||||
});
|
||||
radio->add_index_changed_callback([this](uint32_t index){ index_changed(index); });
|
||||
for (std::string_view option : options) {
|
||||
radio->add_option(option);
|
||||
@@ -122,19 +138,19 @@ void ConfigSubMenu::back_button_pressed() {
|
||||
recompui::focus_mod_configure_button();
|
||||
}
|
||||
|
||||
void ConfigSubMenu::option_hovered(ConfigOptionElement *option, bool active) {
|
||||
void ConfigSubMenu::set_description_option_element(ConfigOptionElement *option, bool active) {
|
||||
if (active) {
|
||||
hover_option_elements.emplace(option);
|
||||
description_option_element = option;
|
||||
}
|
||||
else {
|
||||
hover_option_elements.erase(option);
|
||||
else if (description_option_element == option) {
|
||||
description_option_element = nullptr;
|
||||
}
|
||||
|
||||
if (hover_option_elements.empty()) {
|
||||
if (description_option_element == nullptr) {
|
||||
description_label->set_text("");
|
||||
}
|
||||
else {
|
||||
description_label->set_text((*hover_option_elements.begin())->get_description());
|
||||
description_label->set_text(description_option_element->get_description());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,8 +186,10 @@ ConfigSubMenu::ConfigSubMenu(Element *parent) : Element(parent) {
|
||||
config_scroll_container = context.create_element<ScrollContainer>(config_container, ScrollDirection::Vertical);
|
||||
}
|
||||
|
||||
description_label = context.create_element<Label>(body_container, "Description", LabelStyle::Small);
|
||||
description_label = context.create_element<Label>(body_container, "", LabelStyle::Small);
|
||||
description_label->set_min_width(800.0f);
|
||||
description_label->set_padding_left(16.0f);
|
||||
description_label->set_padding_right(16.0f);
|
||||
}
|
||||
|
||||
recompui::get_current_context().set_autofocus_element(back_button);
|
||||
@@ -188,14 +206,15 @@ void ConfigSubMenu::enter(std::string_view title) {
|
||||
void ConfigSubMenu::clear_options() {
|
||||
config_scroll_container->clear_children();
|
||||
config_option_elements.clear();
|
||||
hover_option_elements.clear();
|
||||
description_option_element = nullptr;
|
||||
}
|
||||
|
||||
void ConfigSubMenu::add_option(ConfigOptionElement *option, std::string_view id, std::string_view name, std::string_view description) {
|
||||
option->set_option_id(id);
|
||||
option->set_name(name);
|
||||
option->set_description(description);
|
||||
option->set_hover_callback([this](ConfigOptionElement *option, bool active){ option_hovered(option, active); });
|
||||
option->set_hover_callback([this](ConfigOptionElement *option, bool active){ set_description_option_element(option, active); });
|
||||
option->set_focus_callback([this, option](const std::string &id, bool active) { set_description_option_element(option, active); });
|
||||
if (config_option_elements.empty()) {
|
||||
back_button->set_nav(NavDirection::Down, option->get_focus_element());
|
||||
option->set_nav(NavDirection::Up, back_button);
|
||||
|
@@ -20,6 +20,7 @@ protected:
|
||||
std::string name;
|
||||
std::string description;
|
||||
std::function<void(ConfigOptionElement *, bool)> hover_callback = nullptr;
|
||||
std::function<void(const std::string &, bool)> focus_callback = nullptr;
|
||||
|
||||
virtual void process_event(const Event &e) override;
|
||||
std::string_view get_type_name() override { return "ConfigOptionElement"; }
|
||||
@@ -30,6 +31,7 @@ public:
|
||||
void set_name(std::string_view name);
|
||||
void set_description(std::string_view description);
|
||||
void set_hover_callback(std::function<void(ConfigOptionElement *, bool)> callback);
|
||||
void set_focus_callback(std::function<void(const std::string &, bool)> callback);
|
||||
const std::string &get_description() const;
|
||||
void set_nav_auto(NavDirection dir) override { get_focus_element()->set_nav_auto(dir); }
|
||||
void set_nav_none(NavDirection dir) override { get_focus_element()->set_nav_none(dir); }
|
||||
@@ -84,10 +86,10 @@ private:
|
||||
Container *config_container = nullptr;
|
||||
ScrollContainer *config_scroll_container = nullptr;
|
||||
std::vector<ConfigOptionElement *> config_option_elements;
|
||||
std::unordered_set<ConfigOptionElement *> hover_option_elements;
|
||||
ConfigOptionElement * description_option_element = nullptr;
|
||||
|
||||
void back_button_pressed();
|
||||
void option_hovered(ConfigOptionElement *option, bool active);
|
||||
void set_description_option_element(ConfigOptionElement *option, bool active);
|
||||
void add_option(ConfigOptionElement *option, std::string_view id, std::string_view name, std::string_view description);
|
||||
protected:
|
||||
std::string_view get_type_name() override { return "ConfigSubMenu"; }
|
||||
|
@@ -8,6 +8,18 @@ namespace recompui {
|
||||
static const std::u8string OldExtension = u8".old";
|
||||
static const std::u8string NewExtension = u8".new";
|
||||
|
||||
static bool is_dynamic_lib(const std::filesystem::path &file_path) {
|
||||
#if defined(_WIN32)
|
||||
return file_path.extension() == ".dll";
|
||||
#elif defined(__linux__)
|
||||
return file_path.extension() == ".so" || file_path.filename().string().find(".so.") != std::string::npos;
|
||||
#elif defined(__APPLE__)
|
||||
return file_path.extension() == ".dylib";
|
||||
#else
|
||||
static_assert(false, "Unimplemented for this platform.");
|
||||
#endif
|
||||
}
|
||||
|
||||
size_t zip_write_func(void *opaque, mz_uint64 offset, const void *bytes, size_t count) {
|
||||
std::ofstream &stream = *(std::ofstream *)(opaque);
|
||||
stream.seekp(offset, std::ios::beg);
|
||||
@@ -187,15 +199,8 @@ namespace recompui {
|
||||
first_nrm_iterator = std::prev(result.pending_installations.end());
|
||||
}
|
||||
}
|
||||
#if defined(_WIN32)
|
||||
else if (target_path.extension() == ".dll") {
|
||||
#elif defined(__linux__)
|
||||
else if (target_path.extension() == ".so") {
|
||||
#elif defined(__APPLE__)
|
||||
else if (target_path.extension() == ".dylib") {
|
||||
#else
|
||||
static_assert(false, "Unimplemented for this platform."); {
|
||||
#endif
|
||||
|
||||
if (is_dynamic_lib(target_path)) {
|
||||
std::filesystem::path target_write_path = target_path.u8string() + NewExtension;
|
||||
std::ofstream output_stream(target_write_path, std::ios::binary);
|
||||
if (!output_stream.is_open()) {
|
||||
@@ -281,6 +286,13 @@ namespace recompui {
|
||||
void ModInstaller::start_mod_installation(const std::list<std::filesystem::path> &file_paths, std::function<void(std::filesystem::path, size_t, size_t)> progress_callback, Result &result) {
|
||||
result = Result();
|
||||
|
||||
for (const std::filesystem::path &path : file_paths) {
|
||||
if (is_dynamic_lib(path)) {
|
||||
result.error_messages.emplace_back("The provided mod(s) must be installed without extracting the ZIP file(s). Please install the mod ZIP file(s) directly.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (const std::filesystem::path &path : file_paths) {
|
||||
recomp::mods::ModOpenError open_error;
|
||||
recomp::mods::ZipModFileHandle file_handle(path, open_error);
|
||||
|
@@ -589,7 +589,10 @@ void ModMenu::create_mod_list() {
|
||||
install_mods_button->set_nav_manual(NavDirection::Up, mod_tab_id);
|
||||
}
|
||||
|
||||
Rml::ElementTabSet* tabset = recompui::get_config_tabset();
|
||||
if (tabset && tabset->GetActiveTab() == recompui::config_tab_to_index(ConfigTab::Mods)) {
|
||||
recompui::set_config_tabset_mod_nav();
|
||||
}
|
||||
|
||||
// Add one extra spacer at the bottom.
|
||||
ModEntrySpacer *spacer = context.create_element<ModEntrySpacer>(list_scroll_container);
|
||||
|
@@ -585,6 +585,15 @@ void draw_hook(RT64::RenderCommandList* command_list, RT64::RenderFramebuffer* s
|
||||
while (recompui::try_deque_event(cur_event)) {
|
||||
bool context_capturing_input = recompui::is_context_capturing_input();
|
||||
bool context_capturing_mouse = recompui::is_context_capturing_mouse();
|
||||
|
||||
// Handle up button events even when input is disabled to avoid missing them during binding.
|
||||
if (cur_event.type == SDL_EventType::SDL_CONTROLLERBUTTONUP) {
|
||||
int sdl_key = cont_button_to_key(cur_event.cbutton);
|
||||
if (sdl_key == latest_controller_key_pressed) {
|
||||
latest_controller_key_pressed = SDLK_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
if (!recomp::all_input_disabled()) {
|
||||
bool is_mouse_input = false;
|
||||
// Implement some additional behavior for specific events on top of what RmlUi normally does with them.
|
||||
@@ -630,13 +639,6 @@ void draw_hook(RT64::RenderCommandList* command_list, RT64::RenderFramebuffer* s
|
||||
cont_interacted = true;
|
||||
break;
|
||||
}
|
||||
case SDL_EventType::SDL_CONTROLLERBUTTONUP: {
|
||||
int sdl_key = cont_button_to_key(cur_event.cbutton);
|
||||
if (sdl_key == latest_controller_key_pressed) {
|
||||
latest_controller_key_pressed = SDLK_UNKNOWN;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SDL_EventType::SDL_KEYDOWN:
|
||||
non_mouse_interacted = true;
|
||||
kb_interacted = true;
|
||||
|
Reference in New Issue
Block a user