mirror of
https://github.com/Zelda64Recomp/Zelda64Recomp.git
synced 2025-10-30 08:03:03 +00:00
Implement label and textinput in mod UI API
This commit is contained in:
parent
06250b2a4e
commit
3863c954c2
8 changed files with 136 additions and 27 deletions
|
|
@ -189,6 +189,7 @@ set (SOURCES
|
||||||
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_radio.cpp
|
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_radio.cpp
|
||||||
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_scroll_container.cpp
|
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_scroll_container.cpp
|
||||||
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_slider.cpp
|
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_slider.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_span.cpp
|
||||||
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_style.cpp
|
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_style.cpp
|
||||||
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_text_input.cpp
|
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_text_input.cpp
|
||||||
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_toggle.cpp
|
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_toggle.cpp
|
||||||
|
|
|
||||||
|
|
@ -179,7 +179,7 @@ RECOMP_PATCH void Play_Init(GameState* thisx) {
|
||||||
if (CHECK_EVENTINF(EVENTINF_TRIGGER_DAYTELOP)) {
|
if (CHECK_EVENTINF(EVENTINF_TRIGGER_DAYTELOP)) {
|
||||||
CLEAR_EVENTINF(EVENTINF_TRIGGER_DAYTELOP);
|
CLEAR_EVENTINF(EVENTINF_TRIGGER_DAYTELOP);
|
||||||
STOP_GAMESTATE(&this->state);
|
STOP_GAMESTATE(&this->state);
|
||||||
// Use non-relocatable reference to DayTelop_Init instead.
|
// @recomp Use non-relocatable reference to DayTelop_Init instead.
|
||||||
SET_NEXT_GAMESTATE(&this->state, DayTelop_Init_NORELOCATE, sizeof(DayTelopState));
|
SET_NEXT_GAMESTATE(&this->state, DayTelop_Init_NORELOCATE, sizeof(DayTelopState));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -195,7 +195,7 @@ RECOMP_PATCH void Play_Init(GameState* thisx) {
|
||||||
if (gSaveContext.save.entrance == -1) {
|
if (gSaveContext.save.entrance == -1) {
|
||||||
gSaveContext.save.entrance = 0;
|
gSaveContext.save.entrance = 0;
|
||||||
STOP_GAMESTATE(&this->state);
|
STOP_GAMESTATE(&this->state);
|
||||||
// Use non-relocatable reference to TitleSetup_Init instead.
|
// @recomp Use non-relocatable reference to TitleSetup_Init instead.
|
||||||
SET_NEXT_GAMESTATE(&this->state, TitleSetup_Init_NORELOCATE, sizeof(TitleSetupState));
|
SET_NEXT_GAMESTATE(&this->state, TitleSetup_Init_NORELOCATE, sizeof(TitleSetupState));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -247,9 +247,18 @@ bool Element::is_enabled() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Element::set_text(std::string_view text) {
|
void Element::set_text(std::string_view text) {
|
||||||
|
// TODO escape this
|
||||||
base->SetInnerRML(std::string(text));
|
base->SetInnerRML(std::string(text));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string Element::get_input_text() {
|
||||||
|
return base->GetAttribute("value", std::string{});
|
||||||
|
}
|
||||||
|
|
||||||
|
void Element::set_input_text(std::string_view val) {
|
||||||
|
base->SetAttribute("value", std::string{ val });
|
||||||
|
}
|
||||||
|
|
||||||
void Element::set_src(std::string_view src) {
|
void Element::set_src(std::string_view src) {
|
||||||
base->SetAttribute("src", std::string(src));
|
base->SetAttribute("src", std::string(src));
|
||||||
}
|
}
|
||||||
|
|
@ -317,8 +326,8 @@ void Element::queue_update() {
|
||||||
cur_context.queue_element_update(resource_id);
|
cur_context.queue_element_update(resource_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Element::register_callback(PTR(void) callback, PTR(void) userdata) {
|
void Element::register_callback(ContextId context, PTR(void) callback, PTR(void) userdata) {
|
||||||
callbacks.emplace_back(UICallback{.callback = callback, .userdata = userdata});
|
callbacks.emplace_back(UICallback{.context = context, .callback = callback, .userdata = userdata});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
|
|
||||||
namespace recompui {
|
namespace recompui {
|
||||||
struct UICallback {
|
struct UICallback {
|
||||||
|
ContextId context;
|
||||||
PTR(void) callback;
|
PTR(void) callback;
|
||||||
PTR(void) userdata;
|
PTR(void) userdata;
|
||||||
};
|
};
|
||||||
|
|
@ -63,6 +64,8 @@ public:
|
||||||
void set_enabled(bool enabled);
|
void set_enabled(bool enabled);
|
||||||
bool is_enabled() const;
|
bool is_enabled() const;
|
||||||
void set_text(std::string_view text);
|
void set_text(std::string_view text);
|
||||||
|
std::string get_input_text();
|
||||||
|
void set_input_text(std::string_view text);
|
||||||
void set_src(std::string_view src);
|
void set_src(std::string_view src);
|
||||||
void set_style_enabled(std::string_view style_name, bool enabled);
|
void set_style_enabled(std::string_view style_name, bool enabled);
|
||||||
bool is_element() override { return true; }
|
bool is_element() override { return true; }
|
||||||
|
|
@ -73,7 +76,7 @@ public:
|
||||||
float get_client_width();
|
float get_client_width();
|
||||||
float get_client_height();
|
float get_client_height();
|
||||||
void queue_update();
|
void queue_update();
|
||||||
void register_callback(PTR(void) callback, PTR(void) userdata);
|
void register_callback(ContextId context, PTR(void) callback, PTR(void) userdata);
|
||||||
};
|
};
|
||||||
|
|
||||||
void queue_ui_callback(recompui::ResourceId resource, const Event& e, const UICallback& callback);
|
void queue_ui_callback(recompui::ResourceId resource, const Event& e, const UICallback& callback);
|
||||||
|
|
|
||||||
15
src/ui/elements/ui_span.cpp
Normal file
15
src/ui/elements/ui_span.cpp
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
#include "ui_span.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
namespace recompui {
|
||||||
|
|
||||||
|
Span::Span(Element *parent) : Element(parent, 0, "span") {
|
||||||
|
set_font_style(FontStyle::Normal);
|
||||||
|
}
|
||||||
|
|
||||||
|
Span::Span(Element *parent, const std::string &text) : Span(parent) {
|
||||||
|
set_text(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
14
src/ui/elements/ui_span.h
Normal file
14
src/ui/elements/ui_span.h
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ui_element.h"
|
||||||
|
#include "ui_label.h"
|
||||||
|
|
||||||
|
namespace recompui {
|
||||||
|
|
||||||
|
class Span : public Element {
|
||||||
|
public:
|
||||||
|
Span(Element *parent);
|
||||||
|
Span(Element *parent, const std::string &text);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace recompui
|
||||||
|
|
@ -12,6 +12,7 @@
|
||||||
#include "elements/ui_radio.h"
|
#include "elements/ui_radio.h"
|
||||||
#include "elements/ui_scroll_container.h"
|
#include "elements/ui_scroll_container.h"
|
||||||
#include "elements/ui_slider.h"
|
#include "elements/ui_slider.h"
|
||||||
|
#include "elements/ui_span.h"
|
||||||
#include "elements/ui_style.h"
|
#include "elements/ui_style.h"
|
||||||
#include "elements/ui_text_input.h"
|
#include "elements/ui_text_input.h"
|
||||||
#include "elements/ui_toggle.h"
|
#include "elements/ui_toggle.h"
|
||||||
|
|
@ -19,6 +20,7 @@
|
||||||
|
|
||||||
#include "librecomp/overlays.hpp"
|
#include "librecomp/overlays.hpp"
|
||||||
#include "librecomp/helpers.hpp"
|
#include "librecomp/helpers.hpp"
|
||||||
|
#include "librecomp/addresses.hpp"
|
||||||
#include "ultramodern/error_handling.hpp"
|
#include "ultramodern/error_handling.hpp"
|
||||||
|
|
||||||
using namespace recompui;
|
using namespace recompui;
|
||||||
|
|
@ -32,26 +34,6 @@ ContextId get_context(uint8_t* rdram, recomp_context* ctx) {
|
||||||
return ContextId{ .slot_id = context_id };
|
return ContextId{ .slot_id = context_id };
|
||||||
}
|
}
|
||||||
|
|
||||||
template <int arg_index>
|
|
||||||
std::string arg_string(uint8_t* rdram, recomp_context* ctx) {
|
|
||||||
PTR(char) str = _arg<arg_index, PTR(char)>(rdram, ctx);
|
|
||||||
|
|
||||||
// Get the length of the byteswapped string.
|
|
||||||
size_t len = 0;
|
|
||||||
while (MEM_B(str, len) != 0x00) {
|
|
||||||
len++;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string ret{};
|
|
||||||
ret.reserve(len + 1);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < len; i++) {
|
|
||||||
ret += (char)MEM_B(str, i);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <int arg_index>
|
template <int arg_index>
|
||||||
ResourceId arg_resource_id(uint8_t* rdram, recomp_context* ctx) {
|
ResourceId arg_resource_id(uint8_t* rdram, recomp_context* ctx) {
|
||||||
uint32_t slot_id = _arg<arg_index, uint32_t>(rdram, ctx);
|
uint32_t slot_id = _arg<arg_index, uint32_t>(rdram, ctx);
|
||||||
|
|
@ -106,6 +88,17 @@ void return_resource(recomp_context* ctx, ResourceId resource) {
|
||||||
_return<uint32_t>(ctx, resource.slot_id);
|
_return<uint32_t>(ctx, resource.slot_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void return_string(uint8_t* rdram, recomp_context* ctx, const std::string& ret) {
|
||||||
|
gpr addr = (reinterpret_cast<uint8_t*>(recomp::alloc(rdram, ret.size() + 1)) - rdram) + 0xFFFFFFFF80000000ULL;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < ret.size(); i++) {
|
||||||
|
MEM_B(i, addr) = ret[i];
|
||||||
|
}
|
||||||
|
MEM_B(ret.size(), addr) = '\x00';
|
||||||
|
|
||||||
|
_return<PTR(char)>(ctx, addr);
|
||||||
|
}
|
||||||
|
|
||||||
// Contexts
|
// Contexts
|
||||||
void recompui_create_context(uint8_t* rdram, recomp_context* ctx) {
|
void recompui_create_context(uint8_t* rdram, recomp_context* ctx) {
|
||||||
(void)rdram;
|
(void)rdram;
|
||||||
|
|
@ -161,10 +154,37 @@ void recompui_create_element(uint8_t* rdram, recomp_context* ctx) {
|
||||||
return_resource(ctx, ret->get_resource_id());
|
return_resource(ctx, ret->get_resource_id());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void recompui_create_label(uint8_t* rdram, recomp_context* ctx) {
|
||||||
|
ContextId ui_context = get_context(rdram, ctx);
|
||||||
|
Element* parent = arg_element<1>(rdram, ctx, ui_context);
|
||||||
|
std::string text = _arg_string<2>(rdram, ctx);
|
||||||
|
uint32_t style = _arg<3, uint32_t>(rdram, ctx);
|
||||||
|
|
||||||
|
Element* ret = ui_context.create_element<Label>(parent, text, static_cast<LabelStyle>(style));
|
||||||
|
return_resource(ctx, ret->get_resource_id());
|
||||||
|
}
|
||||||
|
|
||||||
|
void recompui_create_span(uint8_t* rdram, recomp_context* ctx) {
|
||||||
|
ContextId ui_context = get_context(rdram, ctx);
|
||||||
|
Element* parent = arg_element<1>(rdram, ctx, ui_context);
|
||||||
|
std::string text = _arg_string<2>(rdram, ctx);
|
||||||
|
|
||||||
|
Element* ret = ui_context.create_element<Span>(parent, text);
|
||||||
|
return_resource(ctx, ret->get_resource_id());
|
||||||
|
}
|
||||||
|
|
||||||
|
void recompui_create_textinput(uint8_t* rdram, recomp_context* ctx) {
|
||||||
|
ContextId ui_context = get_context(rdram, ctx);
|
||||||
|
Element* parent = arg_element<1>(rdram, ctx, ui_context);
|
||||||
|
|
||||||
|
Element* ret = ui_context.create_element<TextInput>(parent);
|
||||||
|
return_resource(ctx, ret->get_resource_id());
|
||||||
|
}
|
||||||
|
|
||||||
void recompui_create_button(uint8_t* rdram, recomp_context* ctx) {
|
void recompui_create_button(uint8_t* rdram, recomp_context* ctx) {
|
||||||
ContextId ui_context = get_context(rdram, ctx);
|
ContextId ui_context = get_context(rdram, ctx);
|
||||||
Element* parent = arg_element<1>(rdram, ctx, ui_context);
|
Element* parent = arg_element<1>(rdram, ctx, ui_context);
|
||||||
std::string text = arg_string<2>(rdram, ctx);
|
std::string text = _arg_string<2>(rdram, ctx);
|
||||||
uint32_t style = _arg<3, uint32_t>(rdram, ctx);
|
uint32_t style = _arg<3, uint32_t>(rdram, ctx);
|
||||||
|
|
||||||
Button* ret = ui_context.create_element<Button>(parent, text, static_cast<ButtonStyle>(style));
|
Button* ret = ui_context.create_element<Button>(parent, text, static_cast<ButtonStyle>(style));
|
||||||
|
|
@ -697,7 +717,45 @@ void recompui_set_tab_index(uint8_t* rdram, recomp_context* ctx) {
|
||||||
resource->set_tab_index(static_cast<TabIndex>(tab_index));
|
resource->set_tab_index(static_cast<TabIndex>(tab_index));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Getters
|
||||||
|
void recompui_get_input_text(uint8_t* rdram, recomp_context* ctx) {
|
||||||
|
Style* resource = arg_style<0>(rdram, ctx);
|
||||||
|
|
||||||
|
if (!resource->is_element()) {
|
||||||
|
recompui::message_box("Fatal error in mod - attempted to get text of non-element");
|
||||||
|
assert(false);
|
||||||
|
ultramodern::error_handling::quick_exit(__FILE__, __LINE__, __FUNCTION__);
|
||||||
|
}
|
||||||
|
|
||||||
|
Element* element = static_cast<Element*>(resource);
|
||||||
|
std::string ret = element->get_input_text();
|
||||||
|
return_string(rdram, ctx, ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setters
|
||||||
|
void recompui_set_input_text(uint8_t* rdram, recomp_context* ctx) {
|
||||||
|
Style* resource = arg_style<0>(rdram, ctx);
|
||||||
|
|
||||||
|
if (!resource->is_element()) {
|
||||||
|
recompui::message_box("Fatal error in mod - attempted to set text of non-element");
|
||||||
|
assert(false);
|
||||||
|
ultramodern::error_handling::quick_exit(__FILE__, __LINE__, __FUNCTION__);
|
||||||
|
}
|
||||||
|
|
||||||
|
Element* element = static_cast<Element*>(resource);
|
||||||
|
element->set_input_text(_arg_string<1>(rdram, ctx));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Callbacks
|
||||||
void recompui_register_callback(uint8_t* rdram, recomp_context* ctx) {
|
void recompui_register_callback(uint8_t* rdram, recomp_context* ctx) {
|
||||||
|
ContextId ui_context = recompui::get_current_context();
|
||||||
|
|
||||||
|
if (ui_context == ContextId::null()) {
|
||||||
|
recompui::message_box("Fatal error in mod - attempted to register callback with no active context");
|
||||||
|
assert(false);
|
||||||
|
ultramodern::error_handling::quick_exit(__FILE__, __LINE__, __FUNCTION__);
|
||||||
|
}
|
||||||
|
|
||||||
Style* resource = arg_style<0>(rdram, ctx);
|
Style* resource = arg_style<0>(rdram, ctx);
|
||||||
|
|
||||||
if (!resource->is_element()) {
|
if (!resource->is_element()) {
|
||||||
|
|
@ -710,7 +768,7 @@ void recompui_register_callback(uint8_t* rdram, recomp_context* ctx) {
|
||||||
PTR(void) callback = _arg<1, PTR(void)>(rdram, ctx);
|
PTR(void) callback = _arg<1, PTR(void)>(rdram, ctx);
|
||||||
PTR(void) userdata = _arg<2, PTR(void)>(rdram, ctx);
|
PTR(void) userdata = _arg<2, PTR(void)>(rdram, ctx);
|
||||||
|
|
||||||
element->register_callback(callback, userdata);
|
element->register_callback(ui_context, callback, userdata);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define REGISTER_FUNC(name) recomp::overlays::register_base_export(#name, name)
|
#define REGISTER_FUNC(name) recomp::overlays::register_base_export(#name, name)
|
||||||
|
|
@ -724,6 +782,9 @@ void recompui::register_ui_exports() {
|
||||||
REGISTER_FUNC(recompui_hide_context);
|
REGISTER_FUNC(recompui_hide_context);
|
||||||
REGISTER_FUNC(recompui_create_style);
|
REGISTER_FUNC(recompui_create_style);
|
||||||
REGISTER_FUNC(recompui_create_element);
|
REGISTER_FUNC(recompui_create_element);
|
||||||
|
REGISTER_FUNC(recompui_create_label);
|
||||||
|
// REGISTER_FUNC(recompui_create_span);
|
||||||
|
REGISTER_FUNC(recompui_create_textinput);
|
||||||
REGISTER_FUNC(recompui_create_button);
|
REGISTER_FUNC(recompui_create_button);
|
||||||
REGISTER_FUNC(recompui_set_position);
|
REGISTER_FUNC(recompui_set_position);
|
||||||
REGISTER_FUNC(recompui_set_left);
|
REGISTER_FUNC(recompui_set_left);
|
||||||
|
|
@ -794,5 +855,7 @@ void recompui::register_ui_exports() {
|
||||||
REGISTER_FUNC(recompui_set_column_gap);
|
REGISTER_FUNC(recompui_set_column_gap);
|
||||||
REGISTER_FUNC(recompui_set_drag);
|
REGISTER_FUNC(recompui_set_drag);
|
||||||
REGISTER_FUNC(recompui_set_tab_index);
|
REGISTER_FUNC(recompui_set_tab_index);
|
||||||
|
REGISTER_FUNC(recompui_get_input_text);
|
||||||
|
REGISTER_FUNC(recompui_set_input_text);
|
||||||
REGISTER_FUNC(recompui_register_callback);
|
REGISTER_FUNC(recompui_register_callback);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -106,11 +106,15 @@ extern "C" void recomp_run_ui_callbacks(uint8_t* rdram, recomp_context* ctx) {
|
||||||
|
|
||||||
while (queued_callbacks.try_dequeue(cur_callback)) {
|
while (queued_callbacks.try_dequeue(cur_callback)) {
|
||||||
if (convert_event(cur_callback.event, *event_data)) {
|
if (convert_event(cur_callback.event, *event_data)) {
|
||||||
|
recompui::ContextId cur_context = cur_callback.callback.context;
|
||||||
|
cur_context.open();
|
||||||
|
|
||||||
ctx->r4 = static_cast<int32_t>(cur_callback.resource.slot_id);
|
ctx->r4 = static_cast<int32_t>(cur_callback.resource.slot_id);
|
||||||
ctx->r5 = stack_frame;
|
ctx->r5 = stack_frame;
|
||||||
ctx->r6 = cur_callback.callback.userdata;
|
ctx->r6 = cur_callback.callback.userdata;
|
||||||
|
|
||||||
LOOKUP_FUNC(cur_callback.callback.callback)(rdram, ctx);
|
LOOKUP_FUNC(cur_callback.callback.callback)(rdram, ctx);
|
||||||
|
cur_context.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue