Initial Cross Platform Work

This commit is contained in:
2025-04-24 16:39:24 +02:00
parent dbffb7f316
commit 13c2869ba8
170 changed files with 18611 additions and 10292 deletions

View File

@ -1,138 +0,0 @@
/*
MIT License
Copyright (c) 2024 - 2025 tobid7
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <3ds.h>
#include <pd/app/app.hpp>
#include <pd/core/sys.hpp>
#include <pd/lib3ds/hwinfo.hpp>
namespace PD {
int App::too;
void App::Run() {
this->PreInit();
this->Init();
last_time = Sys::GetNanoTime();
while (aptMainLoop()) {
input_mgr->Update();
u64 current = Sys::GetNanoTime();
float dt = static_cast<float>(current - last_time) / 1000000.f;
app_time->Update();
last_time = current;
fps = 1000.f / dt;
if (runtimeflags & AppFLags_UserLoop) {
PD::TT::Beg("App_MainLoop");
if (!this->MainLoop(dt, app_time->GetSeconds())) {
break;
}
PD::TT::End("App_MainLoop");
}
PD::TT::Beg("Ovl_Update");
if (runtimeflags & AppFlags_HandleOverlays &&
SafeInitFlags & AppInitFlags_InitLithium) {
renderer->Layer(90);
overlay_mgr->Update(dt);
}
if (runtimeflags & AppFlags_HandleMessageMgr &&
SafeInitFlags & AppInitFlags_InitLithium) {
/// Messages have their own special Layer
renderer->Layer(93);
msg_mgr->Update(dt);
}
PD::TT::End("Ovl_Update");
if (runtimeflags & AppFlags_HandleRendering &&
SafeInitFlags & AppInitFlags_InitLithium) {
renderer->PrepareRender();
renderer->Render(Top);
renderer->Render(Bottom);
renderer->FinalizeRender();
}
}
this->Deinit();
this->PostDeinit();
}
std::string App::GetDataDirectory() {
Assert(SafeInitFlags & AppInitFlags_UnnamedOption1,
"Data Dir is not enabled!");
return "sdmc:/palladium/apps/" + name;
}
void App::PreInit() {
/// Create a Copy that won't get edit
SafeInitFlags = InitFlags;
osSetSpeedupEnable(InitFlags & AppInitFlags_New3dsMode);
if (InitFlags & AppInitFlags_InitGraphics) {
gfxInitDefault();
if (!(InitFlags & AppInitFlags_InitGraphicsNoC3D)) {
C3D_Init(C3D_DEFAULT_CMDBUF_SIZE);
}
}
cfguInit();
if (InitFlags & AppInitFlags_MountRomfs) {
romfsInit();
}
if (InitFlags & AppInitFlags_InitHwInfo) {
PD::HwInfo::Init();
}
if (InitFlags & AppInitFlags_UnnamedOption1) {
std::filesystem::create_directories("sdmc:/palladium/apps/" + name);
}
input_mgr = PD::New<CtrHid>();
if (InitFlags & AppInitFlags_InitLithium) {
Assert(!(InitFlags & AppInitFlags_InitGraphicsNoC3D),
"InitGraphicsNoC3D is not compatible with InitLithium!");
Top = Screen::New(Screen::Top);
Bottom = Screen::New(Screen::Bottom);
renderer = LI::Renderer::New();
renderer->RegisterScreen(false, Top);
renderer->RegisterScreen(true, Bottom);
renderer->OnScreen(Top);
msg_mgr = MessageMgr::New(renderer);
overlay_mgr = OverlayMgr::New(renderer, input_mgr);
}
app_time = Timer::New();
}
void App::PostDeinit() {
renderer = nullptr;
msg_mgr = nullptr;
overlay_mgr = nullptr;
input_mgr = nullptr;
if (SafeInitFlags & AppInitFlags_InitGraphics) {
if (!(SafeInitFlags & AppInitFlags_InitGraphicsNoC3D)) {
C3D_Fini();
}
gfxExit();
}
if (SafeInitFlags & AppInitFlags_InitHwInfo) {
PD::HwInfo::Deinit();
}
cfguExit();
if (SafeInitFlags & AppInitFlags_MountRomfs) {
romfsExit();
}
}
} // namespace PD

View File

@ -1,61 +0,0 @@
/*
MIT License
Copyright (c) 2024 - 2025 René Amthor (tobid7)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <pd/app/lang.hpp>
#include <pd/external/json.hpp>
namespace PD {
void Lang::LoadFile(const std::string &path) {
nlohmann::json js;
std::fstream iff(path, std::ios::in);
if (iff.is_open()) {
js = nlohmann::json::parse(iff);
iff.close();
if (js.is_discarded()) {
return;
}
if (!js.contains("ver") || !js.contains("id") || !js.contains("name") ||
!js.contains("author") || !js.contains("keys")) {
return;
}
if (js["ver"].get<int>() != ver) {
return;
}
lang_id = js["id"];
lang_name = js["name"];
lang_author = js["author"];
ltable.clear();
for (auto k : js["keys"].items()) {
ltable.insert(std::make_pair(k.key(), k.value().get<std::string>()));
}
}
}
const std::string &Lang::Get(const std::string &k) {
auto e = ltable.find(k);
if (e != ltable.end()) {
return e->second;
}
return k;
}
} // namespace PD

View File

@ -24,8 +24,8 @@ SOFTWARE.
#include <pd/core/bit_util.hpp>
namespace PD::BitUtil {
bool IsSingleBit(u32 v) { return v && !(v & (v - 1)); }
u32 GetPow2(u32 v) {
PD_CORE_API bool IsSingleBit(u32 v) { return v && !(v & (v - 1)); }
PD_CORE_API u32 GetPow2(u32 v) {
v--;
v |= v >> 1;
v |= v >> 2;

View File

@ -33,7 +33,7 @@ static const std::map<char, int> HEX_DEC = {
{'c', 12}, {'d', 13}, {'e', 14}, {'f', 15}, {'A', 10}, {'B', 11},
{'C', 12}, {'D', 13}, {'E', 14}, {'F', 15}};
Color& Color::Hex(const std::string& hex) {
PD_CORE_API Color& Color::Hex(const std::string& hex) {
#ifdef PD_NO_SAFE_CODE
/// Safetey check (not required if you programm well xd)
if (hex.length() != 7 || hex.length() != 9 || hex.length() != 6 ||
@ -58,7 +58,7 @@ Color& Color::Hex(const std::string& hex) {
return *this;
}
std::string Color::Hex(bool rgba) const {
PD_CORE_API std::string Color::Hex(bool rgba) const {
std::stringstream s;
s << "#";
s << std::hex << std::setw(2) << std::setfill('0') << m_r;

View File

@ -35,13 +35,21 @@ SOFTWARE.
#define PALLADIUM_GIT_BRANCH "unknown"
#endif
const std::string PD::LibInfo::CompiledWith() {
PD_CORE_API const std::string PD::LibInfo::CompiledWith() {
return Strings::GetCompilerVersion();
}
const std::string PD::LibInfo::CxxVersion() {
PD_CORE_API const std::string PD::LibInfo::CxxVersion() {
return "CPP: " + std::to_string(__cplusplus);
}
const std::string PD::LibInfo::BuildTime() { return __DATE__ " - " __TIME__; }
const std::string PD::LibInfo::Version() { return PALLADIUM_VERSION; }
const std::string PD::LibInfo::Commit() { return PALLADIUM_GIT_COMMIT; }
const std::string PD::LibInfo::Branch() { return PALLADIUM_GIT_BRANCH; }
PD_CORE_API const std::string PD::LibInfo::BuildTime() {
return __DATE__ " - " __TIME__;
}
PD_CORE_API const std::string PD::LibInfo::Version() {
return PALLADIUM_VERSION;
}
PD_CORE_API const std::string PD::LibInfo::Commit() {
return PALLADIUM_GIT_COMMIT;
}
PD_CORE_API const std::string PD::LibInfo::Branch() {
return PALLADIUM_GIT_BRANCH;
}

View File

@ -21,16 +21,18 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <pd/drivers/hid.hpp>
#include <pd/core/hid_driver.hpp>
/// Reform of the RenderD7 095 Hid Api
/// Using Custom Keybindings for future
/// Porting of the library
namespace PD {
bool Hid::IsEvent(Event e, Key keys) { return key_events[0][e] & keys; }
PD_CORE_API bool Hid::IsEvent(Event e, Key keys) {
return key_events[0][e] & keys;
}
void Hid::SwappyTable() {
PD_CORE_API void Hid::SwappyTable() {
auto tkd = key_events[1][Event_Down];
auto tkh = key_events[1][Event_Held];
auto tku = key_events[1][Event_Up];

View File

@ -25,7 +25,7 @@ SOFTWARE.
namespace PD {
namespace IO {
std::vector<u8> LoadFile2Mem(const std::string& path) {
PD_CORE_API std::vector<u8> LoadFile2Mem(const std::string& path) {
std::ifstream iff(path, std::ios::binary);
if (!iff) {
return std::vector<u8>();
@ -38,7 +38,7 @@ std::vector<u8> LoadFile2Mem(const std::string& path) {
iff.close();
return res;
}
u32 HashMemory(const std::vector<u8>& data) {
PD_CORE_API u32 HashMemory(const std::vector<u8>& data) {
u32 hash = 4477;
for (auto& it : data) {
hash = (hash * 33) + it;

View File

@ -1,50 +1,56 @@
/*
MIT License
Copyright (c) 2024 - 2025 René Amthor (tobid7)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <3ds.h>
#include <pd/app/error.hpp>
namespace PD {
void Error(const std::string& msg) {
gfxInitDefault();
consoleInit(GFX_TOP, NULL);
std::cout << "Palladium - ERROR MANAGER\n" << std::endl;
std::cout << msg << std::endl << std::endl;
std::cout << "Press Start to Exit" << std::endl;
while (aptMainLoop()) {
hidScanInput();
if (hidKeysDown() & KEY_START) {
break;
}
gfxSwapBuffers();
}
gfxExit();
exit(0);
}
void Assert(bool v, const std::string& msg) {
if (v == false) {
Error("Assert Failed:\n" + msg);
}
}
/*
MIT License
Copyright (c) 2024 - 2025 tobid7
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <pd/core/mat.hpp>
namespace PD {
PD_CORE_API void Mat4::Zeros() {
for (int i = 0; i < 16; i++) {
m[i] = 0.0f;
}
}
PD_CORE_API void Mat4::Ortho(float left, float right, float bottom, float top,
float near, float far) {
m[0] = 2.0f / (right - left);
m[1] = 0.0f;
m[2] = 0.0f;
m[3] = -(right + left) / (right - left);
m[4] = 0.0f;
m[5] = 2.0f / (top - bottom);
m[6] = 0.0f;
m[7] = -(top + bottom) / (top - bottom);
m[8] = 0.0f;
m[9] = 0.0f;
m[10] = -2.0f / (far - near);
m[11] = -(far + near) / (far - near);
m[12] = 0.0f;
m[13] = 0.0f;
m[14] = 0.0f;
m[15] = 1.0f;
}
} // namespace PD

View File

@ -24,8 +24,8 @@ SOFTWARE.
#include <pd/core/strings.hpp>
namespace PD::Strings {
bool StringEndsWith(const std::string& str,
const std::vector<std::string>& exts) {
PD_CORE_API bool StringEndsWith(const std::string& str,
const std::vector<std::string>& exts) {
// Changed order to not do an substr on empty string
if (str.empty()) {
return false;
@ -44,13 +44,48 @@ bool StringEndsWith(const std::string& str,
return false;
}
std::wstring MakeWstring(const std::string& s) {
// As std::wstring(s.begin(), s.end()); doesn't convert it
// Normally this should not be done like this but it works
return std::filesystem::path(s).wstring();
PD_CORE_API std::wstring MakeWstring(const std::string& s) {
// Manually convert to wstring as they removed wstring_convert :(
std::wstring result;
size_t i = 0;
while (i < s.size()) {
uint8_t ch = static_cast<uint8_t>(s[i]);
if (ch < 0x80) { // 1-byte chsr
result += static_cast<wchar_t>(ch);
i++;
} else if ((ch >> 5) == 0b110) { // 2-byte char
if (i + 1 >= s.size()) {
return L""; // return empty if error
}
wchar_t wc = ((ch & 0x1F) << 6) | (s[i + 1] & 0x3F);
result += wc;
i += 2;
} else if ((ch >> 4) == 0b1110) { // 3-byte char
if (i + 2 >= s.size()) {
return L""; // return empty if error
}
wchar_t wc =
((ch & 0x0F) << 12) | ((s[i + 1] & 0x3F) << 6) | (s[i + 2] & 0x3F);
result += wc;
i += 3;
} else if ((ch >> 3) == 0b11110) { // 4-byte char
if (i + 3 >= s.size()) {
return L""; // return empty if error
}
uint32_t codepoint = ((ch & 0x07) << 18) | ((s[i + 1] & 0x3F) << 12) |
((s[i + 2] & 0x3F) << 6) | (s[i + 3] & 0x3F);
codepoint -= 0x10000;
result += static_cast<wchar_t>(0xD800 + (codepoint >> 10));
result += static_cast<wchar_t>(0xDC00 + (codepoint & 0x3FF));
i += 4;
} else {
return L"";
}
}
return result;
}
const std::string FormatNanos(unsigned long long nanos) {
PD_CORE_API const std::string FormatNanos(unsigned long long nanos) {
// Based on some code of my minecraft plugins
if (nanos < 1000) {
return std::format("{}ns", nanos);
@ -71,7 +106,7 @@ const std::string FormatNanos(unsigned long long nanos) {
return "";
}
const std::string FormatMillis(unsigned long long millis) {
PD_CORE_API const std::string FormatMillis(unsigned long long millis) {
// Original Code can be found in some of my mv plugins
if (millis < 1000) {
return std::format("{}ms", millis);
@ -86,7 +121,7 @@ const std::string FormatMillis(unsigned long long millis) {
return "";
}
const std::string FormatBytes(unsigned long long bytes) {
PD_CORE_API const std::string FormatBytes(unsigned long long bytes) {
static const std::vector<std::string> endings = {
"B", "KB", "MB", "GB", "TB", "Unk",
};
@ -102,8 +137,8 @@ const std::string FormatBytes(unsigned long long bytes) {
return std::format("{:.1f} {}", b, endings[i]);
}
const std::string GetFileName(const std::string& path,
const std::string& saperators) {
PD_CORE_API const std::string GetFileName(const std::string& path,
const std::string& saperators) {
auto pos = path.find_last_of(saperators);
if (pos != path.npos) {
return path.substr(pos + 1);
@ -112,7 +147,7 @@ const std::string GetFileName(const std::string& path,
return path;
}
const std::string PathRemoveExtension(const std::string& path) {
PD_CORE_API const std::string PathRemoveExtension(const std::string& path) {
auto pos = path.find_last_of('.');
if (pos != path.npos) {
return path.substr(0, pos);
@ -121,7 +156,7 @@ const std::string PathRemoveExtension(const std::string& path) {
return path;
}
u32 FastHash(const std::string& s) {
PD_CORE_API u32 FastHash(const std::string& s) {
u32 hash = 5381;
for (auto& it : s) {
hash = (hash * 33) + static_cast<u8>(it);

View File

@ -26,17 +26,17 @@ SOFTWARE.
namespace PD::Sys {
TraceMap pd_sys_tm;
u64 GetTime() {
PD_CORE_API u64 GetTime() {
return std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now().time_since_epoch())
.count();
}
u64 GetNanoTime() {
PD_CORE_API u64 GetNanoTime() {
return std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::steady_clock::now().time_since_epoch())
.count();
}
TT::Res::Ref& GetTraceRef(const std::string& id) {
PD_CORE_API TT::Res::Ref& GetTraceRef(const std::string& id) {
// Auto Generate a New if doesnt exist
if (pd_sys_tm.find(id) == pd_sys_tm.end()) {
pd_sys_tm[id] = TT::Res::New();
@ -44,8 +44,8 @@ TT::Res::Ref& GetTraceRef(const std::string& id) {
}
return pd_sys_tm[id];
}
bool TraceExist(const std::string& id) {
PD_CORE_API bool TraceExist(const std::string& id) {
return pd_sys_tm.find(id) != pd_sys_tm.end();
}
TraceMap& GetTraceMap() { return pd_sys_tm; }
PD_CORE_API TraceMap& GetTraceMap() { return pd_sys_tm; }
} // namespace PD::Sys

View File

@ -25,25 +25,25 @@ SOFTWARE.
#include <pd/core/timer.hpp>
namespace PD {
Timer::Timer(bool autostart) {
PD_CORE_API Timer::Timer(bool autostart) {
is_running = autostart;
Reset();
}
void Timer::Reset() {
PD_CORE_API void Timer::Reset() {
start = Sys::GetTime();
now = start;
}
void Timer::Update() {
PD_CORE_API void Timer::Update() {
if (is_running) {
now = Sys::GetTime();
}
}
void Timer::Pause() { is_running = false; }
void Timer::Rseume() { is_running = true; }
bool Timer::IsRunning() const { return is_running; }
u64 Timer::Get() { return now - start; }
double Timer::GetSeconds() { return double(Get()) / 1000.0; }
PD_CORE_API void Timer::Pause() { is_running = false; }
PD_CORE_API void Timer::Rseume() { is_running = true; }
PD_CORE_API bool Timer::IsRunning() const { return is_running; }
PD_CORE_API u64 Timer::Get() { return now - start; }
PD_CORE_API double Timer::GetSeconds() { return double(Get()) / 1000.0; }
} // namespace PD

View File

@ -25,12 +25,12 @@ SOFTWARE.
#include <pd/core/timetrace.hpp>
namespace PD::TT {
void Beg(const std::string& id) {
PD_CORE_API void Beg(const std::string& id) {
auto trace = Sys::GetTraceRef(id);
trace->SetStart(PD::Sys::GetNanoTime());
}
void End(const std::string& id) {
PD_CORE_API void End(const std::string& id) {
auto trace = Sys::GetTraceRef(id);
trace->SetEnd(PD::Sys::GetNanoTime());
}

View File

@ -1,4 +1,4 @@
#define STB_IMAGE_IMPLEMENTATION
#include <pd/external/stb_image.h>
#define STB_TRUETYPE_IMPLEMENTATION
#define STB_IMAGE_IMPLEMENTATION
#include <pd/external/stb_image.h>
#define STB_TRUETYPE_IMPLEMENTATION
#include <pd/external/stb_truetype.h>

View File

@ -22,6 +22,10 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifdef PD_IMAGE_BUILD_SHARED
#define STB_IMAGE_IMPLEMENTATION
#endif
#include <pd/external/stb_image.h>
#include <cstring>
@ -30,31 +34,121 @@ SOFTWARE.
#include <pd/image/img_convert.hpp>
namespace PD {
void Image::Load(const std::string& path) {
u8* img = stbi_load(path.c_str(), &w, &h, &fmt, 4);
PD_IMAGE_API void Image::Load(const std::string& path) {
u8* img = stbi_load(path.c_str(), &pWidth, &pHeight, &fmt, 4);
if (fmt == 3) {
stbi_image_free(img);
img = stbi_load(path.c_str(), &w, &h, &fmt, 3);
buffer.resize(w * h * 4);
PD::ImgConvert::RGB24toRGBA32(
buffer, std::vector<u8>(img, img + (w * h * 3)), w, h);
} else {
buffer.assign(img, img + (w * h * 4));
img = stbi_load(path.c_str(), &pWidth, &pHeight, &fmt, 3);
pBuffer = std::vector<PD::u8>(img, img + (pWidth * pHeight * 3));
pFmt = RGB;
stbi_image_free(img);
} else if (fmt == 4) {
pBuffer = std::vector<PD::u8>(img, img + (pWidth * pHeight * 4));
pFmt = RGBA;
stbi_image_free(img);
}
}
void Image::Load(const std::vector<u8>& buf) {
u8* img = stbi_load_from_memory(buf.data(), buf.size(), &w, &h, &fmt, 4);
PD_IMAGE_API void Image::Load(const std::vector<u8>& buf) {
u8* img =
stbi_load_from_memory(buf.data(), buf.size(), &pWidth, &pHeight, &fmt, 4);
if (fmt == 3) {
stbi_image_free(img);
img = stbi_load_from_memory(buf.data(), buf.size(), &w, &h, &fmt, 3);
buffer.resize(w * h * 4);
PD::ImgConvert::RGB24toRGBA32(
buffer, std::vector<u8>(img, img + (w * h * 3)), w, h);
} else {
buffer.assign(img, img + (w * h * 4));
img = stbi_load_from_memory(buf.data(), buf.size(), &pWidth, &pHeight, &fmt,
3);
pBuffer = std::vector<PD::u8>(img, img + (pWidth * pHeight * 3));
pFmt = RGB;
stbi_image_free(img);
} else if (fmt == 4) {
pBuffer = std::vector<PD::u8>(img, img + (pWidth * pHeight * 4));
stbi_image_free(img);
pFmt = RGBA;
}
}
PD_IMAGE_API void Image::Copy(const std::vector<u8>& buf, int w, int h,
int bpp) {
this->fmt = bpp;
if (buf.size() != (size_t)w * h * bpp) {
// Size Error
return;
}
this->pBuffer.resize(w * h * bpp);
for (size_t i = 0; i < this->pBuffer.size(); i++) {
pBuffer[i] = buf[i];
}
}
PD_IMAGE_API void Image::Convert(Image::Ref img, Image::Format dst) {
if (img->Fmt() == dst) {
return;
} else if (img->Fmt() == Image::RGB && dst == Image::BGR) {
ImgConvert::ReverseBuf(img->pBuffer, 3, img->pWidth, img->pHeight);
} else if (img->Fmt() == Image::RGB && dst == Image::RGBA) {
std::vector<PD::u8> cpy = img->pBuffer;
img->pBuffer.resize(img->pWidth * img->pHeight * 4);
ImgConvert::RGB24toRGBA32(img->pBuffer, cpy, img->pWidth, img->pHeight);
} else if (img->Fmt() == Image::RGBA && dst == Image::RGB) {
std::vector<PD::u8> cpy = img->pBuffer;
img->pBuffer.resize(img->pWidth * img->pHeight * 3);
ImgConvert::RGB32toRGBA24(img->pBuffer, cpy, img->pWidth, img->pHeight);
} else if (img->Fmt() == Image::RGBA && dst == Image::RGB565) {
Convert(img, Image::RGB);
Convert(img, Image::RGB565);
} else if (img->Fmt() == Image::RGB && dst == Image::RGB565) {
auto f = [](u8 r, u8 g, u8 b) -> u16 {
u16 _r = (r >> 3);
u16 _g = (g >> 2);
u16 _b = (b >> 3);
return (_r << 11) | (_g << 5) | _b;
};
std::vector<PD::u8> cpy = img->pBuffer;
img->pBuffer.resize(img->pWidth * img->pHeight * 2);
for (int y = 0; y < img->pWidth; y++) {
for (int x = 0; x < img->pHeight; x++) {
int src = (y * img->pWidth + x) * 3;
int dst = (y * img->pWidth + x) * 2;
u16 new_px = f(cpy[src + 0], cpy[src + 1], cpy[src + 2]);
img->pBuffer[dst + 0] = new_px >> 8;
img->pBuffer[dst + 1] = new_px & 0xff;
}
}
}
}
PD_IMAGE_API int Image::Fmt2Bpp(Format fmt) {
switch (fmt) {
case RGBA:
case ABGR:
return 4;
break;
case RGB:
case BGR:
return 3;
break;
case RGB565:
return 2;
break;
default:
return 0;
break;
}
}
PD_IMAGE_API void Image::ReTile(Image::Ref img,
std::function<u32(int x, int y, int w)> src,
std::function<u32(int x, int y, int w)> dst) {
std::vector<PD::u8> cpy = img->pBuffer;
/** could use fmt here but for 565 that woulnt work as it is not supported by
* file loading where fmt is used */
int bpp = Fmt2Bpp(img->pFmt);
for (int y = 0; y < img->pHeight; y++) {
for (int x = 0; x < img->pWidth; x++) {
int src_idx = src(x, y, img->pWidth);
int dst_idx = dst(x, y, img->pWidth);
for (int i = 0; i < bpp; i++) {
img->pBuffer[dst_idx + i] = cpy[src_idx + i];
}
}
}
}
void Image::Copy(const std::vector<u8>& buf, int w, int h, int fmt) {}
} // namespace PD

View File

@ -29,7 +29,7 @@ SOFTWARE.
namespace PD {
namespace ImgBlur {
std::vector<float> GaussianKernel(int r, float si) {
PD_IMAGE_API std::vector<float> GaussianKernel(int r, float si) {
/// Define radius as r to be shorter
int size = 2 * r + 1;
std::vector<float> kernel(size);
@ -45,13 +45,15 @@ std::vector<float> GaussianKernel(int r, float si) {
}
return kernel;
}
void GaussianBlur(std::vector<u8> &buf, int w, int h, float radius, float si,
std::function<int(int, int, int)> idxfn) {
PD_IMAGE_API void GaussianBlur(std::vector<u8> &buf, int w, int h, float radius,
float si,
std::function<int(int, int, int)> idxfn) {
GaussianBlur(buf.data(), w, h, 4, radius, si, idxfn);
}
void GaussianBlur(void *buf, int w, int h, int bpp, float radius, float si,
std::function<int(int, int, int)> idxfn) {
PD_IMAGE_API void GaussianBlur(void *buf, int w, int h, int bpp, float radius,
float si,
std::function<int(int, int, int)> idxfn) {
if (bpp != 4 && bpp != 3) {
return;
}

View File

@ -24,8 +24,9 @@ SOFTWARE.
#include <pd/image/img_convert.hpp>
namespace PD::ImgConvert {
void RGB24toRGBA32(std::vector<u8> &out, const std::vector<u8> &in,
const int &w, const int &h) {
PD_IMAGE_API void RGB24toRGBA32(std::vector<u8> &out, const std::vector<u8> &in,
const int &w, const int &h) {
// Converts RGB24 to RGBA32
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
@ -38,7 +39,22 @@ void RGB24toRGBA32(std::vector<u8> &out, const std::vector<u8> &in,
}
}
}
void Reverse32(std::vector<u8> &buf, const int &w, const int &h) {
PD_IMAGE_API void RGB32toRGBA24(std::vector<u8> &out, const std::vector<u8> &in,
const int &w, const int &h) {
// Converts RGB24 to RGBA32
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
int src = (y * w + x) * 4;
int dst = (y * w + x) * 3;
out[dst + 0] = in[src + 0];
out[dst + 1] = in[src + 1];
out[dst + 2] = in[src + 2];
}
}
}
PD_IMAGE_API void Reverse32(std::vector<u8> &buf, const int &w, const int &h) {
for (int x = 0; x < w; x++) {
for (int y = 0; y < h; y++) {
int i = y * w + x;
@ -51,4 +67,71 @@ void Reverse32(std::vector<u8> &buf, const int &w, const int &h) {
}
}
}
PD_IMAGE_API void ReverseBuf(std::vector<u8> &buf, size_t bpp, int w, int h) {
std::vector<u8> cpy = buf;
for (int x = 0; x < w; x++) {
for (int y = 0; y < h; y++) {
int pos = (y * w + x) * bpp;
for (size_t i = 0; i < bpp; i++) {
buf[pos + bpp - 1 - i] = cpy[pos + i];
}
}
}
}
PD_IMAGE_API void RGB24toRGBA32(PD::Vec<u8> &out, const PD::Vec<u8> &in,
const int &w, const int &h) {
// Converts RGB24 to RGBA32
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
int src = (y * w + x) * 3;
int dst = (y * w + x) * 4;
out[dst + 0] = in[src + 0];
out[dst + 1] = in[src + 1];
out[dst + 2] = in[src + 2];
out[dst + 3] = 255;
}
}
}
PD_IMAGE_API void RGB32toRGBA24(PD::Vec<u8> &out, const PD::Vec<u8> &in,
const int &w, const int &h) {
// Converts RGB24 to RGBA32
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
int src = (y * w + x) * 4;
int dst = (y * w + x) * 3;
out[dst + 0] = in[src + 0];
out[dst + 1] = in[src + 1];
out[dst + 2] = in[src + 2];
}
}
}
PD_IMAGE_API void Reverse32(PD::Vec<u8> &buf, const int &w, const int &h) {
for (int x = 0; x < w; x++) {
for (int y = 0; y < h; y++) {
int i = y * w + x;
u8 t0 = buf[i + 0];
u8 t1 = buf[i + 1];
buf[i + 0] = buf[i + 3];
buf[i + 1] = buf[i + 2];
buf[i + 3] = t0;
buf[i + 2] = t1;
}
}
}
PD_IMAGE_API void ReverseBuf(PD::Vec<u8> &buf, size_t bpp, int w, int h) {
PD::Vec<u8> cpy = buf;
for (int x = 0; x < w; x++) {
for (int y = 0; y < h; y++) {
int pos = (y * w + x) * bpp;
for (size_t i = 0; i < bpp; i++) {
buf[pos + bpp - 1 - i] = cpy[pos + i];
}
}
}
}
} // namespace PD::ImgConvert

View File

@ -1,87 +0,0 @@
/*
MIT License
Copyright (c) 2024 - 2025 René Amthor (tobid7)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <3ds.h>
#include <pd/lib3ds/drv_hid.hpp>
/// Reform of the RenderD7 095 Hid Api
/// Using Custom Keybindings for future
/// Porting of the library
namespace PD {
CtrHid::CtrHid() {
binds[KEY_A] = A;
binds[KEY_B] = B;
binds[KEY_X] = X;
binds[KEY_Y] = Y;
binds[KEY_START] = Start;
binds[KEY_SELECT] = Select;
binds[KEY_L] = L;
binds[KEY_R] = R;
binds[KEY_DUP] = DUp;
binds[KEY_DDOWN] = DDown;
binds[KEY_DLEFT] = DLeft;
binds[KEY_DRIGHT] = DRight;
binds[KEY_CPAD_UP] = CPUp;
binds[KEY_CPAD_DOWN] = CPDown;
binds[KEY_CPAD_LEFT] = CPLeft;
binds[KEY_CPAD_RIGHT] = CPRight;
binds[KEY_CSTICK_UP] = CSUp;
binds[KEY_CSTICK_DOWN] = CSDown;
binds[KEY_CSTICK_LEFT] = CSLeft;
binds[KEY_CSTICK_RIGHT] = CSRight;
binds[KEY_ZL] = ZL;
binds[KEY_ZR] = ZR;
binds[KEY_TOUCH] = Touch;
}
void CtrHid::Update() {
hidScanInput();
for (int i = 0; i < 2; i++) {
key_events[i][Event_Down] = 0;
key_events[i][Event_Held] = 0;
key_events[i][Event_Up] = 0;
}
u32 kd = hidKeysDown();
u32 kh = hidKeysHeld();
u32 ku = hidKeysUp();
for (auto &b : binds) {
if (b.first & kd) {
key_events[0][Event_Down] |= b.second;
}
if (b.first & kh) {
key_events[0][Event_Held] |= b.second;
}
if (b.first & ku) {
key_events[0][Event_Up] |= b.second;
}
}
if (locked) {
SwappyTable();
}
touchPosition t;
hidTouchRead(&t);
touch[1] = touch[0]; // Cycle touch pos
touch[0] = vec2(t.px, t.py);
}
} // namespace PD

View File

@ -1,80 +0,0 @@
/*
MIT License
Copyright (c) 2024 - 2025 René Amthor (tobid7)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <pd/lib3ds/gamepad_icons.hpp>
namespace PD {
namespace GamePadIcons {
/// For Reference see docs at 3DBrew
std::unordered_map<ID, std::string> gp_icons{
{A, "\uE000"},
{B, "\uE001"},
{X, "\uE002"},
{Y, "\uE003"},
{L, "\uE004"},
{R, "\uE005"},
{Dpad, "\uE006"},
{Start, "\uE045"},
{Select, "\uE046"},
{Home, "\uE073"},
{Steps, "\uE074"},
{PlayCoin, "\uE075"},
{AnalogStick, "\uE077"},
{Power3DS, "\uE078"},
{DpadUp, "\uE079"},
{DpadDown, "\uE07A"},
{DpadLeft, "\uE07B"},
{DpadRight, "\uE07C"},
{DpadHorizontal, "\uE07D"},
{DpadVertical, "\uE07E"},
};
std::unordered_map<Hid::Key, ID> gp_link{
{Hid::A, ID::A},
{Hid::B, ID::B},
{Hid::X, ID::X},
{Hid::Y, ID::Y},
{Hid::L, ID::L},
{Hid::R, ID::R},
{Hid::Start, ID::Start},
{Hid::Select, ID::Select},
{Hid::DUp, ID::DpadUp},
{Hid::DDown, ID::DpadDown},
{Hid::DLeft, ID::DpadLeft},
{Hid::DRight, ID::DpadRight},
};
std::string GetIcon(ID id) {
auto e = gp_icons.find(id);
if (e == gp_icons.end()) {
return "";
}
return e->second;
}
std::string GetIcon(Hid::Key key) {
auto e = gp_link.find(key);
if (e == gp_link.end()) {
return "";
}
return gp_icons[gp_link[key]];
}
} // namespace GamePadIcons
} // namespace PD

View File

@ -1,79 +0,0 @@
/*
MIT License
Copyright (c) 2024 - 2025 René Amthor (tobid7)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <3ds.h>
#include <pd/lib3ds/os.hpp>
namespace PD {
namespace Ctr {
std::string GetSystemLanguage() {
u8 language = 0;
Result res = CFGU_GetSystemLanguage(&language);
if (R_FAILED(res)) {
return "en";
}
switch (language) {
case 0:
return "jp"; // Japanese
break;
case 1:
return "en"; // English
break;
case 2:
return "fr"; // French
break;
case 3:
return "de"; // German
break;
case 4:
return "it"; // Italian
break;
case 5:
return "es"; // Spanish
break;
case 6:
return "zh-CN"; // Chinese (Simplified)
break;
case 7:
return "ko"; // Korean
break;
case 8:
return "nl"; // Dutch
break;
case 9:
return "pt"; // Portuguese
break;
case 10:
return "ru"; // Russian
break;
case 11:
return "zh-TW"; // Chinese (Traditional)
break;
default:
return "en"; // Fall back to English if missing
break;
}
}
} // namespace Ctr
} // namespace PD

View File

@ -1,311 +0,0 @@
/*
MIT License
Copyright (c) 2024 - 2025 René Amthor (tobid7)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifdef PD_EXTENDED_DEBUG
#include <pd/lib3ds/result_decoder.hpp>
static const std::map<int, std::string> modules = {
{0, "common"},
{1, "kernel"},
{2, "util"},
{3, "file server"},
{4, "loader server"},
{5, "tcb"},
{6, "os"},
{7, "dbg"},
{8, "dmnt"},
{9, "pdn"},
{10, "gsp"},
{11, "i2c"},
{12, "gpio"},
{13, "dd"},
{14, "codec"},
{15, "spi"},
{16, "pxi"},
{17, "fs"},
{18, "di"},
{19, "hid"},
{20, "cam"},
{21, "pi"},
{22, "pm"},
{23, "pm_low"},
{24, "fsi"},
{25, "srv"},
{26, "ndm"},
{27, "nwm"},
{28, "soc"},
{29, "ldr"},
{30, "acc"},
{31, "romfs"},
{32, "am"},
{33, "hio"},
{34, "updater"},
{35, "mic"},
{36, "fnd"},
{37, "mp"},
{38, "mpwl"},
{39, "ac"},
{40, "http"},
{41, "dsp"},
{42, "snd"},
{43, "dlp"},
{44, "hio_low"},
{45, "csnd"},
{46, "ssl"},
{47, "am_low"},
{48, "nex"},
{49, "friends"},
{50, "rdt"},
{51, "applet"},
{52, "nim"},
{53, "ptm"},
{54, "midi"},
{55, "mc"},
{56, "swc"},
{57, "fatfs"},
{58, "ngc"},
{59, "card"},
{60, "cardnor"},
{61, "sdmc"},
{62, "boss"},
{63, "dbm"},
{64, "config"},
{65, "ps"},
{66, "cec"},
{67, "ir"},
{68, "uds"},
{69, "pl"},
{70, "cup"},
{71, "gyroscope"},
{72, "mcu"},
{73, "ns"},
{74, "news"},
{75, "ro"},
{76, "gd"},
{77, "card spi"},
{78, "ec"},
{79, "web browser"},
{80, "test"},
{81, "enc"},
{82, "pia"},
{83, "act"},
{84, "vctl"},
{85, "olv"},
{86, "neia"},
{87, "npns"},
{90, "avd"},
{91, "l2b"},
{92, "mvd"},
{93, "nfc"},
{94, "uart"},
{95, "spm"},
{96, "qtm"},
{97, "nfp"},
{254, "application"},
};
static const std::map<int, std::string> levels = {
{0, "Success"}, {1, "Info"}, {25, "Status"},
{26, "Temporary"}, {27, "Permanent"}, {28, "Usage"},
{29, "Reinitialize"}, {30, "Reset"}, {31, "Fatal"},
};
static const std::map<int, std::string> summaries = {
{0, "Success"},
{1, "Nothing happened"},
{2, "Would block"},
{3, "Out of resource"},
{4, "Not found"},
{5, "Invalid state"},
{6, "Not supported"},
{7, "Invalid argument"},
{8, "Wrong argument"},
{9, "Canceled"},
{10, "Status changed"},
{11, "Internal"},
{63, "Invalid result value"},
};
static const std::map<int, std::string> desccommon = {
{0, "Success"},
{1000, "Invalid selection"},
{1001, "Too large"},
{1002, "Not authorized"},
{1003, "Already done"},
{1004, "Invalid size"},
{1005, "Invalid enum value"},
{1006, "Invalid combination"},
{1007, "No data"},
{1008, "Busy"},
{1009, "Misaligned address"},
{1010, "Misaligned size"},
{1011, "Out of memory"},
{1012, "Not implemented"},
{1013, "Invalid address"},
{1014, "Invalid pointer"},
{1015, "Invalid handle"},
{1016, "Not initialized"},
{1017, "Already initialized"},
{1018, "Not found"},
{1019, "Cancel requested"},
{1020, "Already exists"},
{1021, "Out of range"},
{1022, "Timeout"},
{1023, "Invalid result value"},
};
static const std::map<int, std::string> desckernel = {
{2, "Invalid memory permissions."},
};
static const std::map<int, std::string> descos = {
{10, "Not enough memory."},
{26, "Session closed by remote."},
{47, "Invalid command header."},
};
// Need to Fix The Range based Values
static const std::map<int, std::string> descfs = {
{101, "Archive not mounted or mount-point not found."},
{120, "Title or object not found."},
{141, "Gamecard not inserted."},
{230, "Invalid open flags or permissions."},
{391, "NCCH hash check failed."},
{302, "RSA or AES-MAC verification failed."},
{395, "RomFS or Savedata hash check failed."},
{630, "Command not allowed, or missing permissions."},
{702, "Invalid path."},
{761, "Incorrect ExeFS read size."},
{100, "[Media] not found."},
{180, "Exists already."},
{200, "Not enough space."},
{220, "Invalidated archive."},
{230, "Unacceptable or write protected."},
{340, "0x01"},
{360, "Bad format."},
{390, "Verification failure."},
{400, "0x01"},
{600, "Out of resources."},
{630, "Access denied."},
{661, "0x01"},
{700, "Invalid argument."},
{730, "Not initialized."},
{750, "Already initialized."},
{760, "Not supported."},
{780, "0x01"},
};
static const std::map<int, std::string> descsrv = {
{5,
"Invalid string length (service name length is zero or longer than 8 "
"chars)."},
{6,
"Access to service denied (requested a service the application does "
"not have access to)."},
{7,
"String size does not match contents (service name contains unexpected "
"null byte)."},
};
static const std::map<int, std::string> descnwm = {
{2,
"This error usually indicates the wifi chipset in the console is dying "
"or dead."},
};
static const std::map<int, std::string> descam = {
{4, "Invalid ticket version."},
{32, "Empty CIA."},
{37, "Invalid NCCH."},
{39, "Invalid title version."},
{43, "Database doesn\"t exist, or it failed to open."},
{44, "Trying to uninstall system-app."},
{106,
"Invalid signature/CIA. Usually happens when developer UNITINFO is "
"enabled in Luma3DS."},
{393, "Invalid database."},
};
static const std::map<int, std::string> deschttp = {
{105, "Request timed out."},
};
static const std::map<int, std::string> descnim = {
{1,
"Invalid string IPC paramater (non null terminated at its indicated "
"length)."},
{12,
"Invalid country code returned by CFG module reading config save "
"0xB0000."},
{13,
"Zero string length console serial number or '000000000000000' "
"returned by CFG's SecureInfoGetSerialNo."},
{18,
"General data reading error of NIM's .dat files from its system save, "
"bad data or bad data lengths."},
{22,
"General invalid data or length of data returned from nintendo "
"servers. (Only applicable for some operations)"},
{25,
"IntegrityVerificationSeed is waiting on servers to be synced into "
"console. Can't processed with online services without sync being "
"completed first over IPC request."},
{26,
"Unavailable/unaccessable IntegrityVerificationSeed on Nintendo "
"servers. May happen if NIM is told to import "
"IntegrityVerificationSeed from servers at any time other than after "
"the successful System Transfer reboot."},
{27,
"Invalid country language code returned by CFG module reading config "
"save 0xA0002."},
{37,
"Service is in Standby Mode. (eShop ban? General service is down? "
"This caused by a server response flag on account information. "
"Account is not referring to NNID.)"},
{39, "HTTP Status non 200. (Only applicable for some operations)"},
{40, "General XML read/write error while processing Auto Delivery XMLs."},
{41,
"General XML read/write error while processing Auto Delivery XMLs. "
"(Stubbed virtual call was called)"},
{58,
"Invalid NPNS token returned by CFG module reading config save 0xF0006."},
{67, "HTTP Status 404 while trying to download a game's seed."},
{68, "HTTP Status 503 while trying to download a game's seed."},
};
static const std::map<int, std::string> descmvd = {
{271, "Invalid configuration."},
};
static const std::map<int, std::string> descqtm = {
{8, "Camera is already in use or busy."},
};
// Need to Fix The Range based Values
static const std::map<int, std::string> descapplication = {
{0,
"The application raised an error. Please consult the application's "
"source code or ask the author for assistance with it."},
{1024, "0x01"},
};
#endif

147
source/lithium/drawlist.cpp Normal file
View File

@ -0,0 +1,147 @@
#include <pd/lithium/drawlist.hpp>
#include <pd/lithium/renderer.hpp>
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
namespace PD {
namespace LI {
PD_LITHIUM_API Command::Ref DrawList::PreGenerateCmd() {
Command::Ref cmd = Command::New();
cmd->Layer = Layer;
cmd->Index = pDrawList.Size();
cmd->Tex = CurrentTex;
return cmd;
}
PD_LITHIUM_API void DrawList::PathArcToN(const fvec2& c, float radius,
float a_min, float a_max,
int segments) {
// Path.push_back(c);
PathReserve(segments + 1);
for (int i = 0; i < segments; i++) {
float a = a_min + ((float)i / (float)segments) * (a_max - a_min);
PathAdd(vec2(c.x + std::cos(a) * radius, c.y + std::sin(a) * radius));
}
}
PD_LITHIUM_API void DrawList::PathRect(fvec2 a, fvec2 b, float rounding,
u32 flags) {
if (rounding == 0.f) {
PathAdd(a);
PathAdd(vec2(b.x, a.y));
PathAdd(b);
PathAdd(vec2(a.x, b.y));
} else {
PathArcToN(vec2(a.x + rounding, a.y + rounding), rounding, 4 * 6, 4 * 9,
21);
PathArcToN(vec2(b.x - rounding, a.y + rounding), rounding, 4 * 9, 4 * 12,
21);
PathArcToN(vec2(b.x - rounding, b.y - rounding), rounding, 4 * 0, 4 * 3,
21);
PathArcToN(vec2(a.x + rounding, b.y - rounding), rounding, 4 * 3, 4 * 6,
21);
}
}
PD_LITHIUM_API void DrawList::DrawRect(const fvec2& pos, const fvec2& size,
u32 color, int thickness) {
PathRect(pos, pos + size);
// Flags is currently hardcoded (1 = close)
PathStroke(color, thickness, 1);
}
void DrawList::DrawRectFilled(const fvec2& pos, const fvec2& size, u32 color) {
PathRect(pos, pos + size);
PathFill(color);
}
PD_LITHIUM_API void DrawList::DrawTriangle(const fvec2& a, const fvec2& b,
const fvec2& c, u32 color,
int thickness) {
PathAdd(a);
PathAdd(b);
PathAdd(c);
PathStroke(color, thickness, 1);
}
PD_LITHIUM_API void DrawList::DrawTriangleFilled(const fvec2& a, const fvec2& b,
const fvec2& c, u32 color) {
PathAdd(a);
PathAdd(b);
PathAdd(c);
PathFill(color);
}
PD_LITHIUM_API void DrawList::DrawCircle(const fvec2& center, float rad,
u32 color, int num_segments,
int thickness) {
if (num_segments <= 0) {
// Auto Segment
} else {
float am = (M_PI * 2.0f) * ((float)num_segments) / (float)num_segments;
PathArcToN(center, rad, 0.f, am, num_segments);
}
DrawSolid(); // Only Solid Color Supported
PathStroke(color, thickness, (1 << 0));
}
PD_LITHIUM_API void DrawList::DrawCircleFilled(const fvec2& center, float rad,
u32 color, int num_segments) {
if (num_segments <= 0) {
// Auto Segment
} else {
float am = (M_PI * 2.0f) * ((float)num_segments) / (float)num_segments;
PathArcToN(center, rad, 0.f, am, num_segments);
}
PathFill(color);
}
// TODO: Don't render OOS
PD_LITHIUM_API void DrawList::DrawPolyLine(const Vec<fvec2>& points, u32 clr,
u32 flags, int thickness) {
if (points.Size() < 2) {
return;
}
CurrentTex = WhitePixel;
auto cmd = PreGenerateCmd();
bool close = (flags & (1 << 0));
int num_points = close ? (int)points.Size() : (int)points.Size() - 1;
if (flags & (1 << 1)) {
// TODO: Find a way to draw less garbage looking lines
} else {
// Non antialiased lines look awful when rendering with thickness != 1
for (int i = 0; i < num_points; i++) {
int j = (i + 1) == (int)points.Size() ? 0 : (i + 1);
auto line = Renderer::PrimLine(points[i], points[j], thickness);
Renderer::CmdQuad(cmd, line, vec4(0.f, 1.f, 1.f, 0.f), clr);
}
}
AddCommand(cmd);
}
PD_LITHIUM_API void DrawList::DrawConvexPolyFilled(const Vec<fvec2>& points,
u32 clr) {
if (points.Size() < 3) {
return; // Need at least three points
}
auto cmd = PreGenerateCmd();
Renderer::CmdConvexPolyFilled(cmd, points, clr, CurrentTex);
AddCommand(cmd);
}
PD_LITHIUM_API void DrawList::DrawText(const fvec2& pos,
const std::string& text, u32 color) {
if (!pCurrentFont) {
return;
}
PD::Vec<Command::Ref> cmds;
pCurrentFont->CmdTextEx(cmds, pos, color, pFontScale, text);
for (size_t i = 0; i < cmds.Size(); i++) {
cmds[i]->Index = pDrawList.Size();
cmds[i]->Layer = Layer;
AddCommand(cmds[i]);
}
}
} // namespace LI
} // namespace PD

View File

@ -1,239 +1,278 @@
/*
MIT License
Copyright (c) 2024 - 2025 René Amthor (tobid7)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <3ds.h>
#include <pd/external/stb_truetype.h>
#include <pd/core/bit_util.hpp>
#include <pd/core/io.hpp>
#include <pd/core/strings.hpp>
#include <pd/core/sys.hpp>
#include <pd/lithium/font.hpp>
namespace PD {
namespace LI {
void Font::LoadTTF(const std::string &path, int height) {
sysfont = false; // Not using System Font
TT::Scope st("LI_LoadTTF_" + path);
pixel_height = height; // Set internel pixel height
// Use NextPow2 to be able to use sizes between for example 16 and 32
// before it only was possible to use 8, 16, 32, 64 as size
int texszs = BitUtil::GetPow2(height * 16);
// Load stbtt
stbtt_fontinfo inf;
std::ifstream loader(path, std::ios::binary);
if (!loader.is_open()) return;
loader.seekg(0, std::ios::end);
size_t len = loader.tellg();
loader.seekg(0, std::ios::beg);
unsigned char *buffer = new unsigned char[len];
loader.read(reinterpret_cast<char *>(buffer), len);
loader.close();
stbtt_InitFont(&inf, buffer, 0);
std::vector<unsigned char> font_tex(texszs * texszs); // Create font Texture
float scale = stbtt_ScaleForPixelHeight(&inf, pixel_height);
int ascent, descent, lineGap;
stbtt_GetFontVMetrics(&inf, &ascent, &descent, &lineGap);
int baseline = static_cast<int>(ascent * scale);
std::map<u32, int> buf_cache; // Cache to not render same codepoint tex twice
/// Load Codepoints
auto tex = Texture::New();
vec2 off;
for (u32 ii = 0x0000; ii < 0xFFFF; ii++) {
int i = stbtt_FindGlyphIndex(&inf, ii);
if (i == 0) {
continue;
}
if (stbtt_IsGlyphEmpty(&inf, i)) {
continue;
}
Codepoint c;
int w = 0, h = 0, xo = 0, yo = 0;
unsigned char *bitmap =
stbtt_GetCodepointBitmap(&inf, scale, scale, i, &w, &h, &xo, &yo);
int x0, y0, x1, y1;
stbtt_GetCodepointBitmapBox(&inf, i, scale, scale, &x0, &y0, &x1, &y1);
// Check if Codepoint exists as hash and if it is use its already written
// data
u32 hashed_map = IO::HashMemory(std::vector<u8>(bitmap, bitmap + (w * h)));
if (buf_cache.find(hashed_map) != buf_cache.end()) {
c = GetCodepoint(buf_cache[hashed_map]);
c.cp(i);
cpmap[i] = c;
free(bitmap);
continue;
} else {
buf_cache[hashed_map] = i;
}
if (off[0] + w > texszs) {
off[1] += pixel_height;
off[0] = 0;
}
// Set UV Data
vec4 uvs;
uvs[0] = static_cast<float>(off.x() / (float)texszs);
uvs[1] = static_cast<float>(1.f - (off.y() / (float)texszs));
uvs[2] = static_cast<float>((float)(off.x() + w) / (float)texszs);
uvs[3] = static_cast<float>(1.f - (float)(off.y() + h) / (float)texszs);
c.uv(uvs);
c.tex(tex);
c.size(vec2(w, h));
c.off(baseline + yo);
// Render glyph
for (int y = 0; y < h; ++y) {
for (int x = 0; x < w; ++x) {
int map_pos = ((off[1] + y) * texszs + (off[0] + x));
font_tex[map_pos] = bitmap[x + y * w];
}
}
free(bitmap);
cpmap[i] = c;
// Small Patch to avoid some possible artifacts
off[0] += w + 1;
if (off[0] + w > texszs) {
off[1] += pixel_height;
if (off[1] + pixel_height > texszs) {
break;
}
off[0] = 0;
}
}
// Load the Texture and append to list
tex->LoadPixels(font_tex, texszs, texszs, Texture::A8, Texture::LINEAR);
textures.push_back(tex);
}
Font::Codepoint &Font::GetCodepoint(u32 cp) {
// Check if codepoijt exist or return a static invalid one
auto res = cpmap.find(cp);
if (res == cpmap.end()) {
static Codepoint invalid;
return invalid.invalid(true);
}
return res->second;
}
void Font::LoadSystemFont() {
TT::Scope st("LI_SystemFont"); // Trace loading time
sysfont = true; // Set as System Font
fontEnsureMapped(); // Call this to be sure the font is mapped
// Get some const references for system font loading
const auto fnt = fontGetSystemFont();
const auto fnt_info = fontGetInfo(fnt);
const auto glyph_info = fontGetGlyphInfo(fnt);
// Resize the Texture list by the num of sysfont textures
this->textures.resize(glyph_info->nSheets + 1);
/// Modify the Pixel Height by 1.1f to fit the
/// Size og ttf font Rendering
pixel_height = glyph_info->cellHeight * 1.1f;
// Load the Textures and make sure they don't auto unload
for (size_t i = 0; i < glyph_info->nSheets; i++) {
auto stex = Texture::New();
auto tx = new C3D_Tex;
tx->data = fontGetGlyphSheetTex(fnt, i);
tx->fmt = (GPU_TEXCOLOR)glyph_info->sheetFmt;
tx->size = glyph_info->sheetSize;
tx->width = glyph_info->sheetWidth;
tx->height = glyph_info->sheetHeight;
tx->param = GPU_TEXTURE_MAG_FILTER(GPU_LINEAR) |
GPU_TEXTURE_MIN_FILTER(GPU_LINEAR) |
GPU_TEXTURE_WRAP_S(GPU_REPEAT) | GPU_TEXTURE_WRAP_T(GPU_REPEAT);
tx->border = 0xffffffff;
tx->lodParam = 0;
stex->LoadExternal(tx, vec2(tx->width, tx->height), vec4(0, 1, 1, 0));
stex->AutoUnLoad(false);
textures[i] = stex;
}
std::vector<unsigned int> charSet;
// Write the Charset into a vector
for (auto cmap = fnt_info->cmap; cmap; cmap = cmap->next) {
if (cmap->mappingMethod == CMAP_TYPE_DIRECT) {
if (cmap->codeEnd >= cmap->codeBegin) {
charSet.reserve(charSet.size() + cmap->codeEnd - cmap->codeBegin + 1);
for (auto i = cmap->codeBegin; i <= cmap->codeEnd; ++i) {
if (cmap->indexOffset + (i - cmap->codeBegin) == 0xFFFF) break;
charSet.emplace_back(i);
}
}
} else if (cmap->mappingMethod == CMAP_TYPE_TABLE) {
if (cmap->codeEnd >= cmap->codeBegin) {
charSet.reserve(charSet.size() + cmap->codeEnd - cmap->codeBegin + 1);
for (auto i = cmap->codeBegin; i <= cmap->codeEnd; ++i) {
if (cmap->indexTable[i - cmap->codeBegin] == 0xFFFF) continue;
charSet.emplace_back(i);
}
}
} else if (cmap->mappingMethod == CMAP_TYPE_SCAN) {
charSet.reserve(charSet.size() + cmap->nScanEntries);
for (unsigned i = 0; i < cmap->nScanEntries; ++i) {
if (cmap->scanEntries[i].code >= cmap->codeBegin &&
cmap->scanEntries[i].code <= cmap->codeEnd) {
if (cmap->scanEntries[i].glyphIndex != 0xFFFF) {
charSet.emplace_back(cmap->scanEntries[i].code);
}
}
}
} else {
continue;
}
}
// Sort the charset and make sure all values are unique
std::sort(charSet.begin(), charSet.end());
charSet.erase(std::unique(charSet.begin(), charSet.end()));
// Setup the Codepoint map by the charset
for (auto cp : charSet) {
int gidx = fontGlyphIndexFromCodePoint(fnt, cp);
if (gidx >= 0xFFFF) continue;
Codepoint codepoint;
fontGlyphPos_s dat;
fontCalcGlyphPos(&dat, fnt, gidx, GLYPH_POS_CALC_VTXCOORD, 1.f, 1.f);
codepoint.cp(cp);
codepoint.uv(vec4(dat.texcoord.left, dat.texcoord.top, dat.texcoord.right,
dat.texcoord.bottom));
if (dat.sheetIndex < (int)textures.size()) {
codepoint.tex(textures[dat.sheetIndex]);
} else {
codepoint.invalid(true);
}
codepoint.size(vec2(dat.vtxcoord.right, dat.vtxcoord.bottom));
codepoint.off(0);
cpmap[cp] = codepoint;
}
}
} // namespace LI
} // namespace PD
/*
MIT License
Copyright (c) 2024 - 2025 René Amthor (tobid7)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifdef PD_LITHIUM_BUILD_SHARED
#define STB_TRUETYPE_IMPLEMENTATION
#endif
#include <pd/external/stb_truetype.h>
#include <pd/lithium/font.hpp>
#include <pd/lithium/renderer.hpp>
namespace PD {
namespace LI {
PD_LITHIUM_API void Font::LoadTTF(const std::string &path, int height) {
TT::Scope st("LI_LoadTTF_" + path);
PixelHeight = height; // Set internel pixel height
// Use NextPow2 to be able to use sizes between for example 16 and 32
// before it only was possible to use 8, 16, 32, 64 as size
int texszs = BitUtil::GetPow2(height * 16);
// Load stbtt
stbtt_fontinfo inf;
std::ifstream loader(path, std::ios::binary);
if (!loader.is_open()) return;
loader.seekg(0, std::ios::end);
size_t len = loader.tellg();
loader.seekg(0, std::ios::beg);
unsigned char *buffer = new unsigned char[len];
loader.read(reinterpret_cast<char *>(buffer), len);
loader.close();
stbtt_InitFont(&inf, buffer, 0);
// clang-format off
// Disable clang here cause dont want a garbage looking line
std::vector<PD::u8> font_tex(texszs * texszs * 4); // Create font Texture
// clang-format on
float scale = stbtt_ScaleForPixelHeight(&inf, PixelHeight);
int ascent, descent, lineGap;
stbtt_GetFontVMetrics(&inf, &ascent, &descent, &lineGap);
int baseline = static_cast<int>(ascent * scale);
std::map<u32, int> buf_cache; // Cache to not render same codepoint tex twice
/// Load Codepoints
auto tex = Texture::New();
fvec2 off;
for (u32 ii = 0x0000; ii < 0xFFFF; ii++) {
int i = stbtt_FindGlyphIndex(&inf, ii);
if (i == 0) {
continue;
}
if (stbtt_IsGlyphEmpty(&inf, i)) {
continue;
}
Codepoint c;
int w = 0, h = 0, xo = 0, yo = 0;
unsigned char *bitmap =
stbtt_GetCodepointBitmap(&inf, scale, scale, i, &w, &h, &xo, &yo);
int x0, y0, x1, y1;
stbtt_GetCodepointBitmapBox(&inf, i, scale, scale, &x0, &y0, &x1, &y1);
// Check if Codepoint exists as hash and if it is use its already written
// data
u32 hashed_map = IO::HashMemory(std::vector<u8>(bitmap, bitmap + (w * h)));
if (buf_cache.find(hashed_map) != buf_cache.end()) {
c = GetCodepoint(buf_cache[hashed_map]);
c.pCodepoint = i;
CodeMap[i] = c;
free(bitmap);
continue;
} else {
buf_cache[hashed_map] = i;
}
if (off.x + w > texszs) {
off.y += PixelHeight;
off.x = 0;
}
// Set UV Data
fvec4 uvs;
uvs.x = static_cast<float>(off.x) / texszs;
uvs.y = static_cast<float>(off.y) / texszs;
uvs.z = static_cast<float>((off.x + w) / texszs);
uvs.w = static_cast<float>((off.y + h) / texszs);
if (pBackend->Flags & LIBackendFlags_FlipUV_Y) {
uvs.y = 1.f - uvs.y;
uvs.w = 1.f - uvs.w;
}
c.SimpleUV = uvs;
c.Tex = tex;
c.Size = fvec2(w, h);
c.Offset = baseline + yo;
// Render glyph
for (int y = 0; y < h; ++y) {
for (int x = 0; x < w; ++x) {
int map_pos = (((off.y + y) * texszs + (off.x + x))) * 4;
font_tex[map_pos + 0] = 255;
font_tex[map_pos + 1] = 255;
font_tex[map_pos + 2] = 255;
font_tex[map_pos + 3] = bitmap[x + y * w];
}
}
free(bitmap);
CodeMap[i] = c;
// Small Patch to avoid some possible artifacts
off.x += w + 1;
if (off.x + w > texszs) {
off.y += PixelHeight;
if (off.y + PixelHeight > texszs) {
break;
}
off.x = 0;
}
}
// Load the Texture and append to list
{
auto t = pBackend->LoadTexture(font_tex, texszs, texszs, Texture::RGBA32,
Texture::LINEAR);
tex->CopyOther(t);
}
Textures.push_back(tex);
}
PD_LITHIUM_API Font::Codepoint &Font::GetCodepoint(u32 cp) {
// Check if codepoijt exist or return a static invalid one
auto res = CodeMap.find(cp);
if (res == CodeMap.end()) {
static Codepoint invalid;
invalid.pInvalid = true;
return invalid;
}
return res->second;
}
PD_LITHIUM_API fvec2 Font::GetTextBounds(const std::string &text, float scale) {
// Use wstring for exemple for german äöü
auto wtext = Strings::MakeWstring(text);
// Create a temp position and offset as [0, 0]
fvec2 res;
float x = 0;
// Curent Font Scale
float cfs = (DefaultPixelHeight * scale) / (float)PixelHeight;
float lh = (float)PixelHeight * cfs;
size_t index = 0;
for (auto &it : wtext) {
if (it == '\0') {
break;
}
index++;
auto cp = GetCodepoint(it);
if (cp.pInvalid && it != '\n' && it != '\t' && it != ' ') {
continue;
}
switch (it) {
case '\n':
res.y += lh;
res.x = std::max(res.x, x);
x = 0.f;
break;
case '\t':
x += 16 * cfs;
break;
case ' ':
x += 2 * cfs;
// Fall trough here to get the same result as in
// TextCommand if/else Section
default:
x += cp.Size.x * cfs;
if (index != wtext.size()) {
x += 2 * cfs;
}
break;
}
}
res.x = std::max(res.x, x);
res.y += lh;
return res;
}
PD_LITHIUM_API void Font::CmdTextEx(Vec<Command::Ref> &cmds, const fvec2 &pos,
u32 color, float scale,
const std::string &text, LITextFlags flags,
const fvec2 &box) {
fvec2 off;
float cfs = (DefaultPixelHeight * scale) / (float)PixelHeight;
float lh = (float)PixelHeight * cfs;
fvec2 td;
fvec2 rpos = pos;
fvec2 rbox = box;
if (flags & (LITextFlags_AlignMid | LITextFlags_AlignRight)) {
td = GetTextBounds(text, scale);
}
if (flags & LITextFlags_AlignMid) {
rpos = rbox * 0.5 - td * 0.5 + pos;
}
if (flags & LITextFlags_AlignRight) {
rpos.x = rpos.x - td.x;
}
std::vector<std::string> lines;
std::istringstream iss(text);
std::string tmp;
while (std::getline(iss, tmp)) {
lines.push_back(tmp);
}
for (auto &it : lines) {
/*if (flags & LITextFlags_Short) {
fvec2 tmp_dim;
it = ShortText(it, box.x() - pos.x(), tmp_dim);
}*/
auto wline = Strings::MakeWstring(it);
auto cmd = Command::New();
auto Tex = GetCodepoint(wline[0]).Tex;
cmd->Tex = Tex;
for (auto &jt : wline) {
auto cp = GetCodepoint(jt);
if (cp.pInvalid && jt != '\n' && jt != '\t') {
continue;
}
if (Tex != cp.Tex) {
cmds.Add(cmd);
cmd = Command::New();
Tex = cp.Tex;
cmd->Tex = Tex;
}
if (jt == '\t') {
off.x += 16 * cfs;
} else {
if (jt != ' ') {
if (flags & LITextFlags_Shaddow) {
// Draw
Rect rec = Renderer::PrimRect(
rpos + vec2(off.x + 1, off.x + (cp.Offset * cfs)) + 1,
cp.Size * cfs, 0.f);
Renderer::CmdQuad(cmd, rec, cp.SimpleUV, 0xff111111);
}
// Draw
Rect rec = Renderer::PrimRect(
rpos + off + fvec2(0, (cp.Offset * cfs)), cp.Size * cfs, 0.f);
Renderer::CmdQuad(cmd, rec, cp.SimpleUV, color);
} else {
off.x += 2 * cfs;
}
off.x += cp.Size.x * cfs + 2 * cfs;
}
}
cmds.Add(cmd);
off.y += lh;
off.x = 0;
}
}
} // namespace LI
} // namespace PD

View File

@ -1,34 +0,0 @@
// THIS FILE WAS GENERATED BY build_shaders.py!!!
/*
MIT License
Copyright (c) 2024 tobid7
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <pd/lithium/li7_shader.hpp>
// clang-format off
unsigned char li7_shader[] = {
0x44, 0x56, 0x4c, 0x42, 0x1, 0x0, 0x0, 0x0, 0xa4, 0x0, 0x0, 0x0, 0x44, 0x56, 0x4c, 0x50, 0x0, 0x0, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x50, 0x0, 0x0, 0x0, 0x9, 0x0, 0x0, 0x0, 0x98, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4e, 0x1, 0xf0, 0x7, 0x4e, 0x2, 0x8, 0x2, 0x8, 0x3, 0x18, 0x2, 0x8, 0x4, 0x28, 0x2, 0x8, 0x5, 0x38, 0x2, 0x8, 0x6, 0x10, 0x40, 0x4c, 0x7, 0xf1, 0x27, 0x22, 0x8, 0x10, 0x21, 0x4c, 0x0, 0x0, 0x0, 0x88, 0xac, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa1, 0xa, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x68, 0xc3, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x64, 0xc3, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x62, 0xc3, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x61, 0xc3, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0xaf, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f, 0xd5, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6f, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x44, 0x56, 0x4c, 0x45, 0x2, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x54, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x54, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x6c, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x74, 0x0, 0x0, 0x0, 0xb, 0x0, 0x0, 0x0, 0x2, 0x0, 0x5f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f, 0x0, 0x1, 0x1, 0x37, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf, 0x0, 0x0, 0x0, 0x2, 0x0, 0x1, 0x0, 0xf, 0x0, 0x0, 0x0, 0x3, 0x0, 0x2, 0x0, 0xf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x0, 0x13, 0x0, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x0, 0x0,
};
// clang-format on
size_t li7_shader_size = 0x124;

View File

@ -1,63 +0,0 @@
/*
MIT License
Copyright (c) 2024 - 2025 René Amthor (tobid7)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <pd/external/stb_truetype.h>
#include <pd/core/io.hpp>
#include <pd/core/strings.hpp>
#include <pd/core/sys.hpp>
#include <pd/lithium/font.hpp>
#include <pd/lithium/renderer.hpp>
namespace PD {
namespace LI {
void StaticText::Setup(Renderer* ren, const vec2& pos, u32 clr,
const std::string& text, LITextFlags flags,
const vec2& box) {
this->tdim = ren->GetTextDimensions(text);
this->pos = pos;
this->ren = ren;
this->text = StaticObject::New();
/// Ensure that it also renders Out of Screen i guess
ren->TextCommand(this->text->List(), pos, clr, text,
flags | LITextFlags_RenderOOS, box);
Renderer::OptiCommandList(this->text->List());
// Make sure to bring the text in edit mode
// Fixes flickering problems in ui7
this->text->ReCopy();
}
void StaticText::Draw() {
used = true;
for (auto& it : text->List()) {
ren->PushCommand(it);
}
text->ReCopy();
}
void StaticText::SetColor(u32 col) { text->ReColor(col); }
void StaticText::SetPos(const vec2& pos) { text->MoveIt(pos - this->pos); }
void StaticText::SetLayer(int layer) { text->ReLayer(layer); }
} // namespace LI
} // namespace PD

View File

@ -1,601 +1,152 @@
/*
MIT License
Copyright (c) 2024 - 2025 René Amthor (tobid7)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <3ds.h>
#include <pd/external/stb_truetype.h>
#include <pd/core/io.hpp>
#include <pd/core/strings.hpp>
#include <pd/core/sys.hpp>
#include <pd/lithium/font.hpp>
#include <pd/lithium/li7_shader.hpp>
#include <pd/lithium/renderer.hpp>
namespace PD {
namespace LI {
Renderer::Renderer(LIRenderFlags flags) {
vertex_buf.resize(4 * 4096, Vertex());
index_buf.resize(6 * 4096, 0);
/// Use 3ds u32 here
dvlb = DVLB_ParseFile((uint32_t*)li7_shader, li7_shader_size);
shaderProgramInit(&shader);
shaderProgramSetVsh(&shader, &dvlb->DVLE[0]);
uLoc_projection =
shaderInstanceGetUniformLocation(shader.vertexShader, "projection");
AttrInfo_Init(&attr);
AttrInfo_AddLoader(&attr, 0, GPU_FLOAT, 2);
AttrInfo_AddLoader(&attr, 1, GPU_FLOAT, 2);
AttrInfo_AddLoader(&attr, 2, GPU_UNSIGNED_BYTE, 4);
// Precalculate Projection (Never changes)
Mtx_OrthoTilt(&top_proj, 0.f, 400.f, 240.f, 0.f, 1.f, -1.f, false);
Mtx_OrthoTilt(&bot_proj, 0.f, 320.f, 240.f, 0.f, 1.f, -1.f, false);
std::vector<u8> pixels(16 * 16 * 4, 255);
white = Texture::New(pixels, 16, 16);
UseTex(white);
// Not Loading as Systemfont is freezing
// font = Font::New();
// font->LoadSystemFont();
}
Renderer::~Renderer() {
shaderProgramFree(&shader);
DVLB_Free(dvlb);
}
bool Renderer::InBox(const vec2& pos, const vec2& szs, const vec4& rect) {
return (pos[0] + szs[0] >= rect[0] && pos[1] + szs[1] >= rect[1] &&
pos[0] <= rect[2] && pos[1] <= rect[3]);
}
bool Renderer::InBox(const vec2& pos, const vec4& rect) {
return (pos.x() > rect.x() && pos.x() < rect.x() + rect.z() &&
pos.y() > rect.y() && pos.y() < rect.y() + rect.w());
}
bool Renderer::InBox(const vec2& alpha, const vec2& bravo, const vec2& charlie,
const vec4& rect) {
return ((alpha[0] < rect[2] && bravo[0] < rect[2] && charlie[0] < rect[2]) ||
(alpha[1] < rect[3] && bravo[1] < rect[3] && charlie[1] < rect[3]) ||
(alpha[0] > 0 && bravo[0] > 0 && charlie[0] > 0) ||
(alpha[1] > 0 && bravo[1] > 0 && charlie[1] > 0));
}
void Renderer::RotateCorner(vec2& v, float s, float c) {
float x = v[0] * c - v[1] * s;
float y = v[1] * c + v[0] * s;
v = vec2(x, y);
}
Rect Renderer::CreateRect(const vec2& pos, const vec2& size, float angle) {
vec2 c = size * 0.5f; // Center
vec2 corner[4] = {
vec2(-c[0], -c[1]),
vec2(-c[0] + size[0], -c[1]),
vec2(-c[0], -c[1] + size[1]),
vec2(-c[0] + size[0], -c[1] + size[1]),
};
// Only rotate if required
if (angle != 0.f) {
float s = std::sin(angle);
float co = std::cos(angle);
for (int i = 0; i < 4; i++) {
RotateCorner(corner[i], s, co);
}
}
// Return Result
return Rect(corner[0] + pos + c, corner[1] + pos + c, corner[2] + pos + c,
corner[3] + pos + c);
}
Rect Renderer::CreateLine(const vec2& a, const vec2& b, int t) {
// Usin g th evec maths api makes the code as short as it is
vec2 dir = a - b;
float len = dir.len();
vec2 unit_dir = dir / len;
vec2 perpendicular(-unit_dir.y(), unit_dir.x());
vec2 off = perpendicular * ((float)t * 0.5f);
return Rect(a + off, b + off, a - off, b - off);
}
void Renderer::OptiCommandList(std::vector<Command::Ref>& list) {
std::sort(list.begin(), list.end(), [](Command::Ref a, Command::Ref b) {
if (a->Layer() == b->Layer()) {
if (a->Tex() == b->Tex()) {
return a->Index() < b->Index();
}
return a->Tex() < b->Tex(); // else
}
return a->Layer() < b->Layer(); // else
});
}
void Renderer::SetupCommand(Command::Ref cmd) {
cmd->Index(cmd_idx++).Layer(current_layer).Tex(current_tex);
}
void Renderer::QuadCommand(Command::Ref cmd, const Rect& quad, const Rect& uv,
u32 col) {
cmd->PushIndex(0).PushIndex(1).PushIndex(2);
cmd->PushIndex(0).PushIndex(2).PushIndex(3);
cmd->PushVertex(Vertex(quad.BotRight(), uv.BotRight(), col));
cmd->PushVertex(Vertex(quad.TopRight(), uv.TopRight(), col));
cmd->PushVertex(Vertex(quad.TopLeft(), uv.TopLeft(), col));
cmd->PushVertex(Vertex(quad.BotLeft(), uv.BotLeft(), col));
}
void Renderer::TriangleCommand(Command::Ref cmd, const vec2& a, const vec2& b,
const vec2& c, u32 col) {
cmd->PushIndex(2).PushIndex(1).PushIndex(0);
cmd->PushVertex(Vertex(a, vec2(0.f, 1.f), col));
cmd->PushVertex(Vertex(b, vec2(1.f, 1.f), col));
cmd->PushVertex(Vertex(c, vec2(1.f, 0.f), col));
}
void Renderer::TextCommand(std::vector<Command::Ref>& cmds, const vec2& pos,
u32 color, const std::string& text,
LITextFlags flags, const vec2& box) {
if (!font) {
return;
}
vec2 off;
float cfs = (default_font_h * text_size) / (float)font->PixelHeight();
float lh = (float)font->PixelHeight() * cfs;
vec2 td;
vec2 rpos = pos;
vec2 rbox = box;
if (flags & (LITextFlags_AlignMid | LITextFlags_AlignRight)) {
td = GetTextDimensions(text);
if (rbox[0] == 0.f) {
rbox[0] = area_size.x();
}
if (rbox[1] == 0.f) {
rbox[1] = area_size.y();
}
}
if (flags & LITextFlags_AlignMid) {
rpos = rbox * 0.5 - td * 0.5 + pos;
}
if (flags & LITextFlags_AlignRight) {
rpos[0] = rpos[0] - td[0];
}
std::vector<std::string> lines;
std::istringstream iss(text);
std::string tmp;
while (std::getline(iss, tmp)) {
lines.push_back(tmp);
}
for (auto& it : lines) {
if (flags & LITextFlags_Short) {
vec2 tmp_dim;
it = ShortText(it, box.x() - pos.x(), tmp_dim);
}
/// Well support OOS Rendering here as well
/// Fixes UI7 Scroll back up bug
if (rpos[1] + off[1] + lh < 0 && !(flags & LITextFlags_RenderOOS)) {
off[1] += lh;
continue;
} else if (rpos[1] + off[1] > GetViewport().w() &&
!(flags & LITextFlags_RenderOOS)) {
// Break cause next lines would be out of screen
break;
}
auto wline = Strings::MakeWstring(it);
auto cmd = Command::New();
current_tex = font->GetCodepoint(wline[0]).tex();
SetupCommand(cmd);
cmd->Rendermode(RenderMode_Font);
for (auto& jt : wline) {
auto cp = font->GetCodepoint(jt);
if (cp.invalid() && jt != '\n' && jt != '\t') {
continue;
}
if (current_tex != cp.tex()) {
cmds.push_back(cmd);
cmd = Command::New();
current_tex = cp.tex();
SetupCommand(cmd);
cmd->Rendermode(RenderMode_Font);
}
if (jt == '\t') {
off[0] += 16 * cfs;
} else {
if (jt != ' ') {
int lr = current_layer;
if (flags & LITextFlags_Shaddow) {
// Draw
Rect rec = CreateRect(
rpos + vec2(off[0] + 1, off[1] + (cp.off() * cfs)) + 1,
cp.size() * cfs, 0.f);
QuadCommand(cmd, rec, cp.uv(), 0xff111111);
current_layer++;
}
// Draw
Rect rec = CreateRect(rpos + off + vec2(0, (cp.off() * cfs)),
cp.size() * cfs, 0.f);
QuadCommand(cmd, rec, cp.uv(), color);
current_layer = lr;
} else {
if (!font->SystemFont()) {
off[0] += 2 * cfs;
}
}
off[0] += cp.size().x() * cfs + 2 * cfs;
}
}
cmds.push_back(cmd);
off[1] += lh;
off[0] = 0;
}
}
vec4 Renderer::GetViewport() { return vec4(vec2(), screen->GetSize()); }
std::string Renderer::ShortText(const std::string& text, int maxlen,
vec2& newsize) {
vec2 cdim;
if (flags & LIRenderFlags_TMS) {
auto e = tms.find(text);
if (e != tms.end()) {
e->second.TimeCreated(Sys::GetTime());
if (e->second.Optional()) {
return e->second.Text();
}
cdim = e->second.Size();
}
}
cdim = this->GetTextDimensions(text);
if (cdim[0] < (float)maxlen) {
return text;
}
std::string ext;
/// Forgot why i called this var ending cause
/// Its more a placeholder for removed content
std::string ending = "...";
std::string cpy = text;
std::string res;
size_t extension = text.find_last_of('.');
if (extension != text.npos) {
ext = text.substr(extension);
cpy = text.substr(0, extension);
maxlen -= GetTextDimensions(ext).x();
}
maxlen -= GetTextDimensions(ending).x();
for (auto& it : cpy) {
if (GetTextDimensions(res).x() > (float)maxlen) {
res += ending;
res += ext;
newsize = GetTextDimensions(res);
if (flags & LIRenderFlags_TMS) {
auto& tmp = tms[text];
tmp.Text(res);
tmp.Size(newsize);
tmp.TimeCreated(Sys::GetTime());
tmp.Optional(true);
}
break;
}
res += it;
}
return res;
}
std::string Renderer::WrapText(const std::string& text, int maxlen,
vec2& newsize) {}
vec2 Renderer::GetTextDimensions(const std::string& text) {
if (!font) {
// No font no size (oder so)
return vec2();
}
// Handle TextMapSystem
if (flags & LIRenderFlags_TMS) {
auto ref = tms.find(text);
if (ref != tms.end()) {
ref->second.TimeCreated(Sys::GetTime());
return ref->second.Size();
}
}
// Use wstring for exemple for german äöü
auto wtext = Strings::MakeWstring(text);
// Create a temp position and offset as [0, 0]
vec2 res;
float x = 0;
// Curent Font Scale
float cfs = (default_font_h * text_size) / (float)font->PixelHeight();
float lh = (float)font->PixelHeight() * cfs;
size_t index = 0;
for (auto& it : wtext) {
if (it == '\0') {
break;
}
index++;
auto cp = font->GetCodepoint(it);
if (cp.invalid() && it != '\n' && it != '\t' && it != ' ') {
continue;
}
switch (it) {
case '\n':
res[1] += lh;
res[0] = std::max(res[0], x);
x = 0.f;
break;
case '\t':
x += 16 * cfs;
break;
case ' ':
if (!font->SystemFont()) {
x += 2 * cfs;
}
// Fall trough here to get the same result as in
// TextCommand if/else Section
default:
x += cp.size().x() * cfs;
if (index != wtext.size()) {
x += 2 * cfs;
}
break;
}
}
res[0] = std::max(res[0], x);
res[1] += lh;
if (flags & LIRenderFlags_TMS) {
tms[text] = TextBox(res, Sys::GetTime());
}
return res;
}
void Renderer::UpdateRenderMode(const RenderMode& mode) {
C3D_TexEnv* env = C3D_GetTexEnv(0);
switch (mode) {
case RenderMode_Font:
/// Sets Only Alpha Using the Color and Replase RGB with vertex color
C3D_TexEnvInit(env);
C3D_TexEnvSrc(env, C3D_RGB, GPU_PRIMARY_COLOR);
C3D_TexEnvFunc(env, C3D_RGB, GPU_REPLACE);
C3D_TexEnvSrc(env, C3D_Alpha, GPU_TEXTURE0);
C3D_TexEnvFunc(env, C3D_Alpha, GPU_MODULATE);
break;
// Fall trough instead of defining twice
case RenderMode_RGBA:
default:
/// Use Texture for RGBA and vertexcolor for visibility
C3D_TexEnvInit(env);
C3D_TexEnvSrc(env, C3D_Both, GPU_TEXTURE0);
C3D_TexEnvFunc(env, C3D_Both, GPU_MODULATE);
break;
}
}
void Renderer::PrepareRender() {
if (font_update) {
tms.clear();
font_update = false;
}
C3D_FrameBegin(C3D_FRAME_SYNCDRAW);
TT::Beg("LI_RenderAll");
vertex_idx = 0;
index_idx = 0;
vertices = 0;
indices = 0;
commands = 0;
drawcalls = 0;
C3D_BindProgram(&shader);
C3D_SetAttrInfo(&attr);
}
void Renderer::FinalizeRender() {
C3D_FrameEnd(0);
TT::End("LI_RenderAll");
current_layer = 0;
cmd_idx = 0;
rot = 0.f;
UseTex();
if (flags & LIRenderFlags_TMS) {
std::vector<std::string> rem;
for (auto& it : tms) {
if (Sys::GetTime() - it.second.TimeCreated() > 5) rem.push_back(it.first);
}
for (auto it : rem) tms.erase(it);
} else {
tms.clear();
}
if (flags & LIRenderFlags_AST) {
std::vector<u32> rem;
for (auto it : ast) {
if (!it.second->Used()) {
rem.push_back(it.first);
}
it.second->SetUnused();
}
for (auto& it : rem) {
ast.erase(it);
}
} else {
ast.clear();
}
}
void Renderer::Render(Screen::Ref s) {
Assert(s.get(), "Expected Screen Address but got nullptr!");
s->Clear();
s->Use();
bool bot = s->ScreenType() == Screen::Bottom;
C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_projection,
(bot ? &bot_proj : &top_proj));
C3D_DepthTest(false, GPU_GREATER, GPU_WRITE_ALL);
UpdateRenderMode(RenderMode_RGBA);
int total_vertices = 0;
int total_indices = 0;
auto& cmds = draw_list[Screen32(s)];
commands += cmds.size();
size_t index = 0;
if (flags & LIRenderFlags_LRS) {
OptiCommandList(cmds);
}
while (index < cmds.size()) {
C3D_Tex* tex = cmds[index]->Tex()->GetTex();
auto mode = cmds[index]->Rendermode();
auto smode = cmds[index]->GetScissorMode();
auto spos = cmds[index]->ScissorRect();
C3D_SetScissor((GPU_SCISSORMODE)smode, s->GetSize().y() - spos.w(),
s->GetSize().x() - spos.z(), s->GetSize().y() - spos.y(),
s->GetSize().x() - spos.x());
UpdateRenderMode(mode);
u32 start_vtx = vertex_idx;
u32 start_idx = index_idx;
while (index < cmds.size() && cmds[index]->Tex()->GetTex() == tex &&
cmds[index]->Rendermode() == mode &&
cmds[index]->GetScissorMode() == smode) {
auto c = cmds[index];
// Indices
for (size_t i = 0; i < c->IndexList().size(); i++) {
index_buf[index_idx++] = vertex_idx + c->IndexList().at(i);
}
// Vertices
for (size_t i = 0; i < c->VertexList().size(); i++) {
vertex_buf[vertex_idx++] = c->VertexList().at(i);
}
index++;
}
C3D_TexBind(0, tex);
auto bufInfo = C3D_GetBufInfo();
BufInfo_Init(bufInfo);
BufInfo_Add(bufInfo, vertex_buf.data(), sizeof(Vertex), 3, 0x210);
C3D_DrawElements(GPU_TRIANGLES, index_idx - start_idx, C3D_UNSIGNED_SHORT,
index_buf.data() + start_idx);
drawcalls++;
total_vertices += vertex_idx - start_vtx;
total_indices += index_idx - start_idx;
}
cmds.clear();
C3D_DepthTest(true, GPU_GREATER, GPU_WRITE_ALL);
vertices += total_vertices;
indices += total_indices;
}
void Renderer::DrawRect(const vec2& pos, const vec2& size, u32 color,
const Rect& uv) {
if (!InBox(pos, size, GetViewport())) {
// Instand abort as it is out of screen
return;
}
Rect rec = CreateRect(pos, size, rot);
auto cmd = Command::New();
SetupCommand(cmd);
QuadCommand(cmd, rec, uv, color);
draw_list[Screen32(screen)].push_back(cmd);
}
void Renderer::DrawRectSolid(const vec2& pos, const vec2& size, u32 color) {
UseTex();
DrawRect(pos, size, color, vec4(0.f, 1.f, 1.f, 0.f));
}
void Renderer::DrawTriangle(const vec2& a, const vec2& b, const vec2& c,
u32 color) {
if (!InBox(a, b, c, GetViewport())) {
return;
}
UseTex();
auto cmd = Command::New();
SetupCommand(cmd);
TriangleCommand(cmd, a, b, c, color);
draw_list[Screen32(screen)].push_back(cmd);
}
void Renderer::DrawCircle(const vec2& center_pos, float r, u32 color,
int segments) {
if (segments < 3) {
return;
}
auto cmd = Command::New();
cmd->Index(cmd_idx++).Layer(current_layer).Tex(current_tex);
for (int i = 1; i < segments - 1; i++) {
cmd->PushIndex(0);
cmd->PushIndex(i + 1).PushIndex(i);
}
float as = 2.f * M_PI / segments;
for (int i = 0; i < segments; i++) {
float a = i * as;
float x = center_pos.x() + r * std::cos(a);
float y = center_pos.y() + r * std::sin(a);
cmd->PushVertex(Vertex(
vec2(x, y), vec2((std::cos(a) + 1.f) / 2.f, (std::sin(a) + 1.f) / 2.f),
color));
}
draw_list[Screen32(screen)].push_back(cmd);
}
void Renderer::DrawLine(const vec2& a, const vec2& b, u32 color, int t) {
UseTex();
Rect line = CreateLine(a, b, t);
auto cmd = Command::New();
SetupCommand(cmd);
QuadCommand(cmd, line, vec4(0.f, 1.f, 1.f, 0.f), color);
draw_list[Screen32(screen)].push_back(cmd);
}
void Renderer::DrawImage(const vec2& pos, Texture::Ref tex, const vec2& scale) {
UseTex(tex);
DrawRect(pos, tex->GetSize() * scale, 0xffffffff, tex->GetUV());
}
void Renderer::DrawText(const vec2& pos, u32 color, const std::string& text,
u32 flags, const vec2& ap) {
if (!font) {
return;
}
if (this->flags & LIRenderFlags_AST) {
u32 id = Strings::FastHash(text);
auto e = ast.find(id);
if (e == ast.end()) {
ast[id] = StaticText::New();
e = ast.find(id);
}
if (!e->second->IsSetup() || e->second->Font() != font) {
e->second->Setup(this, pos, color, text, flags, ap);
e->second->Font(font);
}
e->second->SetPos(pos);
e->second->SetColor(color);
e->second->Draw();
return;
}
TextCommand(draw_list[Screen32(screen)], pos, color, text, flags, ap);
}
} // namespace LI
#include <pd/lithium/renderer.hpp>
namespace PD {
namespace LI {
PD_LITHIUM_API Renderer::Renderer(Backend::Ref backend) {
pBackend = backend;
std::vector<PD::u8> white(16 * 16 * 4, 0xff);
WhitePixel = pBackend->LoadTexture(white, 16, 16);
CurrentTex = WhitePixel; // Make sure to have a texture set
}
PD_LITHIUM_API void Renderer::Render() {
pBackend->NewFrame();
pBackend->RenderDrawData(DrawList);
DrawList.Clear();
for (auto it = pDrawLists.Begin(); it != pDrawLists.End(); it++) {
pBackend->RenderDrawData((*it)->pDrawList);
(*it)->Clear();
}
pDrawLists.Clear();
}
PD_LITHIUM_API bool Renderer::InBox(const fvec2& pos, const fvec2& szs,
const fvec4& rect) {
return (pos.x + szs.x >= rect.x && pos.y + szs.y >= rect.y &&
pos.x <= rect.z && pos.y <= rect.w);
}
PD_LITHIUM_API bool Renderer::InBox(const fvec2& pos, const fvec4& rect) {
return (pos.x > rect.x && pos.x < rect.x + rect.z && pos.y > rect.y &&
pos.y < rect.y + rect.w);
}
PD_LITHIUM_API bool Renderer::InBox(const fvec2& alpha, const fvec2& bravo,
const fvec2& charlie, const fvec4& rect) {
return ((alpha.x < rect.z && bravo.x < rect.z && charlie.x < rect.z) ||
(alpha.y < rect.w && bravo.y < rect.w && charlie.y < rect.w) ||
(alpha.x > 0 && bravo.x > 0 && charlie.x > 0) ||
(alpha.y > 0 && bravo.y > 0 && charlie.y > 0));
}
PD_LITHIUM_API void Renderer::RotateCorner(fvec2& pos, float sinus,
float cosinus) {
float x = pos.x * cosinus - pos.y * sinus;
float y = pos.y * cosinus - pos.x * sinus;
pos = fvec2(x, y);
}
PD_LITHIUM_API Rect Renderer::PrimRect(const fvec2& pos, const fvec2& size,
float angle) {
fvec2 c = size * 0.5f; // Center
fvec2 corner[4] = {
fvec2(-c.x, -c.y),
fvec2(-c.x + size.x, -c.y),
fvec2(-c.x, -c.y + size.y),
fvec2(-c.x + size.x, -c.y + size.y),
};
// Only rotate if required
if (angle != 0.f) {
float s = std::sin(angle);
float co = std::cos(angle);
for (int i = 0; i < 4; i++) {
RotateCorner(corner[i], s, co);
}
}
// Return Result
return Rect(corner[0] + pos + c, corner[1] + pos + c, corner[2] + pos + c,
corner[3] + pos + c);
}
PD_LITHIUM_API Rect Renderer::PrimLine(const fvec2& a, const fvec2& b,
int thickness) {
// Using the vec maths api makes the code as short as it is
vec2 dir = a - b;
float len = dir.Len();
vec2 unit_dir = dir / len;
vec2 perpendicular(-unit_dir.y, unit_dir.x);
vec2 off = perpendicular * ((float)thickness * 0.5f);
return Rect(a + off, b + off, a - off, b - off);
}
PD_LITHIUM_API void Renderer::CmdQuad(Command::Ref cmd, const Rect& quad,
const Rect& uv, u32 color) {
cmd->AppendIndex(0).AppendIndex(1).AppendIndex(2);
cmd->AppendIndex(0).AppendIndex(2).AppendIndex(3);
cmd->AppendVertex(Vertex(quad.BotRight(), uv.BotRight(), color));
cmd->AppendVertex(Vertex(quad.TopRight(), uv.TopRight(), color));
cmd->AppendVertex(Vertex(quad.TopLeft(), uv.TopLeft(), color));
cmd->AppendVertex(Vertex(quad.BotLeft(), uv.BotLeft(), color));
}
PD_LITHIUM_API void Renderer::CmdTriangle(Command::Ref cmd, const fvec2 a,
const fvec2 b, const fvec2 c,
u32 clr) {
cmd->AppendIndex(2).AppendIndex(1).AppendIndex(0);
cmd->AppendVertex(Vertex(a, vec2(0.f, 1.f), clr));
cmd->AppendVertex(Vertex(b, vec2(1.f, 1.f), clr));
cmd->AppendVertex(Vertex(c, vec2(1.f, 0.f), clr));
}
PD_LITHIUM_API Command::Ref Renderer::PreGenerateCmd() {
Command::Ref res = Command::New();
res->Index = DrawList.Size();
res->Layer = Layer;
res->Tex = CurrentTex;
return res;
}
// TODO: Don't render OOS (Probably make it with a define as it
// would probably be faster to render out of screen than checking if
// it could be skipped)
PD_LITHIUM_API void Renderer::CmdConvexPolyFilled(Command::Ref cmd,
const Vec<fvec2>& points,
u32 clr, Texture::Ref tex) {
if (points.Size() < 3 || tex == nullptr) {
return; // Need at least three points
}
// Support for Custom Textures (UV calculation)
float minX = points[0].x, minY = points[0].y;
float maxX = minX, maxY = minY;
// Check for the max and min Positions
for (auto it = points.Begin(); it != points.End(); it++) {
if ((*it).x < minX) minX = (*it).x;
if ((*it).y < minY) minY = (*it).y;
if ((*it).x > maxX) maxX = (*it).x;
if ((*it).y > maxY) maxY = (*it).y;
}
// Get Short defines for UV
// (Bottom Right is not required)
auto uv_tl = tex->UV.TopLeft();
auto uv_tr = tex->UV.TopRight();
auto uv_bl = tex->UV.BotLeft();
// Render
for (int i = 2; i < (int)points.Size(); i++) {
cmd->AppendIndex(0).AppendIndex(i).AppendIndex(i - 1);
}
for (int i = 0; i < (int)points.Size(); i++) {
// Calculate U and V coords
float u =
uv_tl.x + ((points[i].x - minX) / (maxX - minX)) * (uv_tr.x - uv_tl.x);
float v =
uv_tl.y + ((points[i].y - minY) / (maxY - minY)) * (uv_bl.y - uv_tl.y);
cmd->AppendVertex(LI::Vertex(points[i], fvec2(u, v), clr));
}
}
} // namespace LI
} // namespace PD

View File

@ -1,68 +0,0 @@
/*
MIT License
Copyright (c) 2024 - 2025 René Amthor (tobid7)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <pd/core/io.hpp>
#include <pd/app/error.hpp>
#include <pd/lithium/spritesheet.hpp>
namespace PD {
SpriteSheet::~SpriteSheet() { textures.clear(); }
void SpriteSheet::LoadFile(const std::string& path) {
auto file = IO::LoadFile2Mem(path);
if (file.size() == 0) {
Error("Unable to load file:\n" + path);
}
C3D_Tex* tex = new C3D_Tex;
auto t3x =
Tex3DS_TextureImport(file.data(), file.size(), tex, nullptr, false);
if (!t3x) {
Error("Unable to import:\n" + path);
}
tex->border = 0;
C3D_TexSetWrap(tex, GPU_CLAMP_TO_BORDER, GPU_CLAMP_TO_BORDER);
C3D_TexSetFilter(tex, GPU_LINEAR, GPU_NEAREST);
textures.reserve(Tex3DS_GetNumSubTextures(t3x) + 1);
for (int i = 0; i < (int)Tex3DS_GetNumSubTextures(t3x); i++) {
auto t = Texture::New();
auto st = Tex3DS_GetSubTexture(t3x, i);
LI::Rect uv(vec2(st->left, st->top), vec2(st->right, st->top),
vec2(st->left, st->bottom), vec2(st->right, st->bottom));
if (st->top < st->bottom) {
uv.SwapVec2XY();
}
t->LoadExternal(tex, vec2(st->width, st->height), uv);
textures.push_back(t);
}
}
Texture::Ref SpriteSheet::Get(int idx) {
if (idx >= (int)textures.size()) {
Error("Trying to Access Texture " + std::to_string(idx + 1) + " of " +
std::to_string(NumTextures()));
}
return textures[idx];
}
int SpriteSheet::NumTextures() const { return textures.size(); }
} // namespace PD

View File

@ -1,168 +0,0 @@
/*
MIT License
Copyright (c) 2024 - 2025 tobid7
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <3ds.h>
#include <pd/external/stb_image.h>
#include <tex3ds.h>
#include <pd/app/error.hpp>
#include <pd/core/bit_util.hpp>
#include <pd/core/io.hpp>
#include <pd/core/timetrace.hpp>
#include <pd/image/image.hpp>
#include <pd/image/img_convert.hpp>
#include <pd/lithium/texture.hpp>
namespace PD {
GPU_TEXCOLOR GetTexFmt(Texture::Type type) {
if (type == Texture::RGBA32)
return GPU_RGBA8;
else if (type == Texture::RGB24)
return GPU_RGB8;
else if (type == Texture::A8)
return GPU_A8;
return GPU_RGBA8; // Default
}
int GetBPP(Texture::Type type) {
if (type == Texture::RGBA32)
return 4;
else if (type == Texture::RGB24)
return 3;
else if (type == Texture::A8)
return 1;
return 0; // Error
}
void Texture::MakeTex(std::vector<u8>& buf, int w, int h, Texture::Type type,
Filter filter) {
// Don't check here as check done before
int bpp = GetBPP(type);
vec2 tex_size(w, h);
// Pow2
if (!PD::BitUtil::IsSingleBit(w)) {
tex_size.x() = PD::BitUtil::GetPow2((unsigned int)w);
}
if (!PD::BitUtil::IsSingleBit(h)) {
tex_size.y() = PD::BitUtil::GetPow2((unsigned int)h);
}
this->size.x() = (u16)w;
this->size.y() = (u16)h;
this->uv = vec4(0.f, 1.f, ((float)w / (float)tex_size.x()),
1.0 - ((float)h / (float)tex_size.y()));
// Texture Setup
auto fltr = (filter == NEAREST ? GPU_NEAREST : GPU_LINEAR);
auto tex_fmt = GetTexFmt(type);
tex = new C3D_Tex;
C3D_TexInit(tex, (u16)tex_size.x(), (u16)tex_size.y(), tex_fmt);
C3D_TexSetFilter(tex, fltr, fltr);
memset(tex->data, 0, tex->size);
/// Probably Remove this if statement in future
/// This are the things confirmed as working
if (bpp == 3 || bpp == 4 || bpp == 1) {
for (int x = 0; x < w; x++) {
for (int y = 0; y < h; y++) {
int dst_pos = ((((y >> 3) * ((int)tex_size.x() >> 3) + (x >> 3)) << 6) +
((x & 1) | ((y & 1) << 1) | ((x & 2) << 1) |
((y & 2) << 2) | ((x & 4) << 2) | ((y & 4) << 3))) *
bpp;
int src_pos = (y * w + x) * bpp;
/// Best idea i had
for (int i = 0; i < bpp; i++) {
((u8*)tex->data)[dst_pos + bpp - 1 - i] = buf[src_pos + i];
}
}
}
C3D_TexFlush(tex);
}
tex->border = 0x00000000;
C3D_TexSetWrap(tex, GPU_CLAMP_TO_BORDER, GPU_CLAMP_TO_BORDER);
}
void Texture::Delete() {
if (tex) {
C3D_TexDelete(tex);
delete tex;
tex = nullptr;
size = vec2();
uv = vec4(0.f, 1.f, 1.f, 0.f);
}
}
void Texture::LoadFile(const std::string& path) {
PD::TT::Scope st("texldr-" + path);
Delete();
PD::Image img(path);
PD::Assert(img.GetBuffer().size(), "Unable to load image: " + path);
if (img.Width() > 1024 || img.Height() > 1024) {
PD::Error("Width or heigt is > 1024");
return;
}
MakeTex(img, img.Width(), img.Height());
}
void Texture::LoadMemory(const std::vector<u8>& data) {
Delete();
PD::Image img(data);
PD::Assert(img.GetBuffer().size(), "Unable to load image from Memory!");
if (img.Width() > 1024 || img.Height() > 1024) {
PD::Error("Width or heigt is > 1024");
return;
}
MakeTex(img, img.Width(), img.Height());
}
void Texture::LoadPixels(const std::vector<u8>& pixels, int w, int h, Type type,
Filter filter) {
Delete();
int bpp = GetBPP(type);
if (w * h * bpp != (int)pixels.size()) {
return;
}
std::vector<u8> cpy(pixels);
MakeTex(cpy, w, h, type, filter);
}
void Texture::LoadT3X(const std::string& path) {
this->Delete();
auto file = IO::LoadFile2Mem(path);
if (file.size() == 0) {
Error("Unable to load file:\n" + path);
}
this->tex = new C3D_Tex;
auto t3x =
Tex3DS_TextureImport(file.data(), file.size(), this->tex, nullptr, false);
if (!t3x) {
Error("Unable to import:\n" + path);
}
auto st = Tex3DS_GetSubTexture(t3x, 0);
this->uv = vec4(st->left, st->top, st->right, st->bottom);
this->size[0] = st->width;
this->size[1] = st->height;
Tex3DS_TextureFree(t3x);
}
} // namespace PD

68
source/net/socket.cpp Normal file
View File

@ -0,0 +1,68 @@
/*
MIT License
Copyright (c) 2024 - 2025 René Amthor (tobid7)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
/** Need to outsource this into the backend */
#include <pd/net/backend.hpp>
#include <pd/net/socket.hpp>
namespace PD {
namespace Net {
PD_NET_API bool Socket::Create() {
pSocket = backend->NewSocket();
return pSocket != backend->GetInvalidRef();
}
PD_NET_API bool Socket::Bind(u16 port) { return backend->Bind(pSocket, port); }
PD_NET_API bool Socket::Listen(int backlog) {
return backend->Listen(pSocket, backlog);
}
PD_NET_API bool Socket::Accept(Socket::Ref client) {
return backend->Accept(pSocket, client);
}
PD_NET_API bool Socket::Connect(const std::string& ip, u16 port) {
return backend->Connect(pSocket, ip, port);
}
PD_NET_API int Socket::Send(const std::string& data) {
return backend->Send(pSocket, data);
}
PD_NET_API int Socket::Receive(std::string& data, int size) {
return backend->Receive(pSocket, data, size);
}
PD_NET_API void Socket::Close() {
if (IsValid()) {
backend->Close(pSocket);
pSocket = backend->GetInvalidRef();
}
}
PD_NET_API bool Socket::IsValid() const {
return pSocket != backend->GetInvalidRef();
}
} // namespace Net
} // namespace PD

View File

@ -22,13 +22,12 @@ SOFTWARE.
*/
#include <pd/external/json.hpp>
#include <pd/core/color.hpp>
#include <pd/overlays/keyboard.hpp>
#include <pd/lib3ds/gamepad_icons.hpp>
// #include <pd/lib3ds/gamepad_icons.hpp>
namespace PD {
struct Key {
Key(const std::string& key, const vec2& p, const vec2& s,
Key(const std::string& key, const fvec2& p, const fvec2& s,
Keyboard::KeyOperation o) {
k = key;
pos = p;
@ -36,8 +35,8 @@ struct Key {
op = o;
}
std::string k;
vec2 pos;
vec2 size;
fvec2 pos;
fvec2 size;
Keyboard::KeyOperation op;
};
@ -45,198 +44,198 @@ using Layout = std::vector<Key>;
Layout layouts[3] = {
{
// 1st row
Key("`", vec2(5, 0), 18, Keyboard::AppendSelf),
Key("1", vec2(25, 0), 18, Keyboard::AppendSelf),
Key("2", vec2(45, 0), 18, Keyboard::AppendSelf),
Key("3", vec2(65, 0), 18, Keyboard::AppendSelf),
Key("4", vec2(85, 0), 18, Keyboard::AppendSelf),
Key("5", vec2(105, 0), 18, Keyboard::AppendSelf),
Key("6", vec2(125, 0), 18, Keyboard::AppendSelf),
Key("7", vec2(145, 0), 18, Keyboard::AppendSelf),
Key("8", vec2(165, 0), 18, Keyboard::AppendSelf),
Key("9", vec2(185, 0), 18, Keyboard::AppendSelf),
Key("0", vec2(205, 0), 18, Keyboard::AppendSelf),
Key("-", vec2(225, 0), 18, Keyboard::AppendSelf),
Key("=", vec2(245, 0), 18, Keyboard::AppendSelf),
Key("<---", vec2(265, 0), vec2(50, 18), Keyboard::Backspace),
Key("`", fvec2(5, 0), 18, Keyboard::AppendSelf),
Key("1", fvec2(25, 0), 18, Keyboard::AppendSelf),
Key("2", fvec2(45, 0), 18, Keyboard::AppendSelf),
Key("3", fvec2(65, 0), 18, Keyboard::AppendSelf),
Key("4", fvec2(85, 0), 18, Keyboard::AppendSelf),
Key("5", fvec2(105, 0), 18, Keyboard::AppendSelf),
Key("6", fvec2(125, 0), 18, Keyboard::AppendSelf),
Key("7", fvec2(145, 0), 18, Keyboard::AppendSelf),
Key("8", fvec2(165, 0), 18, Keyboard::AppendSelf),
Key("9", fvec2(185, 0), 18, Keyboard::AppendSelf),
Key("0", fvec2(205, 0), 18, Keyboard::AppendSelf),
Key("-", fvec2(225, 0), 18, Keyboard::AppendSelf),
Key("=", fvec2(245, 0), 18, Keyboard::AppendSelf),
Key("<---", fvec2(265, 0), fvec2(50, 18), Keyboard::Backspace),
// 2nd row
Key("Tab", vec2(5, 20), vec2(40, 18), Keyboard::Tab),
Key("q", vec2(47, 20), 18, Keyboard::AppendSelf),
Key("w", vec2(67, 20), 18, Keyboard::AppendSelf),
Key("e", vec2(87, 20), 18, Keyboard::AppendSelf),
Key("r", vec2(107, 20), 18, Keyboard::AppendSelf),
Key("t", vec2(127, 20), 18, Keyboard::AppendSelf),
Key("y", vec2(147, 20), 18, Keyboard::AppendSelf),
Key("u", vec2(167, 20), 18, Keyboard::AppendSelf),
Key("i", vec2(187, 20), 18, Keyboard::AppendSelf),
Key("o", vec2(207, 20), 18, Keyboard::AppendSelf),
Key("p", vec2(227, 20), 18, Keyboard::AppendSelf),
Key("[", vec2(247, 20), 18, Keyboard::AppendSelf),
Key("]", vec2(267, 20), 18, Keyboard::AppendSelf),
Key("\\", vec2(287, 20), vec2(28, 18), Keyboard::AppendSelf),
Key("Tab", fvec2(5, 20), fvec2(40, 18), Keyboard::Tab),
Key("q", fvec2(47, 20), 18, Keyboard::AppendSelf),
Key("w", fvec2(67, 20), 18, Keyboard::AppendSelf),
Key("e", fvec2(87, 20), 18, Keyboard::AppendSelf),
Key("r", fvec2(107, 20), 18, Keyboard::AppendSelf),
Key("t", fvec2(127, 20), 18, Keyboard::AppendSelf),
Key("y", fvec2(147, 20), 18, Keyboard::AppendSelf),
Key("u", fvec2(167, 20), 18, Keyboard::AppendSelf),
Key("i", fvec2(187, 20), 18, Keyboard::AppendSelf),
Key("o", fvec2(207, 20), 18, Keyboard::AppendSelf),
Key("p", fvec2(227, 20), 18, Keyboard::AppendSelf),
Key("[", fvec2(247, 20), 18, Keyboard::AppendSelf),
Key("]", fvec2(267, 20), 18, Keyboard::AppendSelf),
Key("\\", fvec2(287, 20), fvec2(28, 18), Keyboard::AppendSelf),
// 3rd row
Key("Caps", vec2(5, 40), vec2(50, 18), Keyboard::Caps),
Key("a", vec2(57, 40), 18, Keyboard::AppendSelf),
Key("s", vec2(77, 40), 18, Keyboard::AppendSelf),
Key("d", vec2(97, 40), 18, Keyboard::AppendSelf),
Key("f", vec2(117, 40), 18, Keyboard::AppendSelf),
Key("g", vec2(137, 40), 18, Keyboard::AppendSelf),
Key("h", vec2(157, 40), 18, Keyboard::AppendSelf),
Key("j", vec2(177, 40), 18, Keyboard::AppendSelf),
Key("k", vec2(197, 40), 18, Keyboard::AppendSelf),
Key("l", vec2(217, 40), 18, Keyboard::AppendSelf),
Key(";", vec2(237, 40), 18, Keyboard::AppendSelf),
Key("'", vec2(257, 40), 18, Keyboard::AppendSelf),
Key("Enter", vec2(277, 40), vec2(38, 18), Keyboard::Enter),
Key("Caps", fvec2(5, 40), fvec2(50, 18), Keyboard::Caps),
Key("a", fvec2(57, 40), 18, Keyboard::AppendSelf),
Key("s", fvec2(77, 40), 18, Keyboard::AppendSelf),
Key("d", fvec2(97, 40), 18, Keyboard::AppendSelf),
Key("f", fvec2(117, 40), 18, Keyboard::AppendSelf),
Key("g", fvec2(137, 40), 18, Keyboard::AppendSelf),
Key("h", fvec2(157, 40), 18, Keyboard::AppendSelf),
Key("j", fvec2(177, 40), 18, Keyboard::AppendSelf),
Key("k", fvec2(197, 40), 18, Keyboard::AppendSelf),
Key("l", fvec2(217, 40), 18, Keyboard::AppendSelf),
Key(";", fvec2(237, 40), 18, Keyboard::AppendSelf),
Key("'", fvec2(257, 40), 18, Keyboard::AppendSelf),
Key("Enter", fvec2(277, 40), fvec2(38, 18), Keyboard::Enter),
// 4th row
Key("Shift", vec2(5, 60), vec2(60, 18), Keyboard::Shift),
Key("z", vec2(67, 60), 18, Keyboard::AppendSelf),
Key("x", vec2(87, 60), 18, Keyboard::AppendSelf),
Key("c", vec2(107, 60), 18, Keyboard::AppendSelf),
Key("v", vec2(127, 60), 18, Keyboard::AppendSelf),
Key("b", vec2(147, 60), 18, Keyboard::AppendSelf),
Key("n", vec2(167, 60), 18, Keyboard::AppendSelf),
Key("m", vec2(187, 60), 18, Keyboard::AppendSelf),
Key(",", vec2(207, 60), 18, Keyboard::AppendSelf),
Key(".", vec2(227, 60), 18, Keyboard::AppendSelf),
Key("/", vec2(247, 60), 18, Keyboard::AppendSelf),
Key("Shift", vec2(267, 60), vec2(48, 18), Keyboard::Shift),
Key("Shift", fvec2(5, 60), fvec2(60, 18), Keyboard::Shift),
Key("z", fvec2(67, 60), 18, Keyboard::AppendSelf),
Key("x", fvec2(87, 60), 18, Keyboard::AppendSelf),
Key("c", fvec2(107, 60), 18, Keyboard::AppendSelf),
Key("v", fvec2(127, 60), 18, Keyboard::AppendSelf),
Key("b", fvec2(147, 60), 18, Keyboard::AppendSelf),
Key("n", fvec2(167, 60), 18, Keyboard::AppendSelf),
Key("m", fvec2(187, 60), 18, Keyboard::AppendSelf),
Key(",", fvec2(207, 60), 18, Keyboard::AppendSelf),
Key(".", fvec2(227, 60), 18, Keyboard::AppendSelf),
Key("/", fvec2(247, 60), 18, Keyboard::AppendSelf),
Key("Shift", fvec2(267, 60), fvec2(48, 18), Keyboard::Shift),
// 5th row
Key("Cancel", vec2(5, 80), vec2(70, 18), Keyboard::OpCancel),
Key("(X)", vec2(77, 80), vec2(23, 18), Keyboard::Op1),
Key("Space", vec2(102, 80), vec2(108, 18), Keyboard::Space),
Key("(!)", vec2(212, 80), vec2(23, 18), Keyboard::Op2),
Key("Confirm", vec2(237, 80), vec2(78, 18), Keyboard::OpConfirm),
Key("Cancel", fvec2(5, 80), fvec2(70, 18), Keyboard::OpCancel),
Key("(X)", fvec2(77, 80), fvec2(23, 18), Keyboard::Op1),
Key("Space", fvec2(102, 80), fvec2(108, 18), Keyboard::Space),
Key("(!)", fvec2(212, 80), fvec2(23, 18), Keyboard::Op2),
Key("Confirm", fvec2(237, 80), fvec2(78, 18), Keyboard::OpConfirm),
},
{
// 1st row
Key("`", vec2(5, 0), 18, Keyboard::AppendSelf),
Key("1", vec2(25, 0), 18, Keyboard::AppendSelf),
Key("2", vec2(45, 0), 18, Keyboard::AppendSelf),
Key("3", vec2(65, 0), 18, Keyboard::AppendSelf),
Key("4", vec2(85, 0), 18, Keyboard::AppendSelf),
Key("5", vec2(105, 0), 18, Keyboard::AppendSelf),
Key("6", vec2(125, 0), 18, Keyboard::AppendSelf),
Key("7", vec2(145, 0), 18, Keyboard::AppendSelf),
Key("8", vec2(165, 0), 18, Keyboard::AppendSelf),
Key("9", vec2(185, 0), 18, Keyboard::AppendSelf),
Key("0", vec2(205, 0), 18, Keyboard::AppendSelf),
Key("-", vec2(225, 0), 18, Keyboard::AppendSelf),
Key("=", vec2(245, 0), 18, Keyboard::AppendSelf),
Key("<---", vec2(265, 0), vec2(50, 18), Keyboard::Backspace),
Key("`", fvec2(5, 0), 18, Keyboard::AppendSelf),
Key("1", fvec2(25, 0), 18, Keyboard::AppendSelf),
Key("2", fvec2(45, 0), 18, Keyboard::AppendSelf),
Key("3", fvec2(65, 0), 18, Keyboard::AppendSelf),
Key("4", fvec2(85, 0), 18, Keyboard::AppendSelf),
Key("5", fvec2(105, 0), 18, Keyboard::AppendSelf),
Key("6", fvec2(125, 0), 18, Keyboard::AppendSelf),
Key("7", fvec2(145, 0), 18, Keyboard::AppendSelf),
Key("8", fvec2(165, 0), 18, Keyboard::AppendSelf),
Key("9", fvec2(185, 0), 18, Keyboard::AppendSelf),
Key("0", fvec2(205, 0), 18, Keyboard::AppendSelf),
Key("-", fvec2(225, 0), 18, Keyboard::AppendSelf),
Key("=", fvec2(245, 0), 18, Keyboard::AppendSelf),
Key("<---", fvec2(265, 0), fvec2(50, 18), Keyboard::Backspace),
// 2nd row
Key("Tab", vec2(5, 20), vec2(40, 18), Keyboard::Tab),
Key("Q", vec2(47, 20), 18, Keyboard::AppendSelf),
Key("W", vec2(67, 20), 18, Keyboard::AppendSelf),
Key("E", vec2(87, 20), 18, Keyboard::AppendSelf),
Key("R", vec2(107, 20), 18, Keyboard::AppendSelf),
Key("T", vec2(127, 20), 18, Keyboard::AppendSelf),
Key("Y", vec2(147, 20), 18, Keyboard::AppendSelf),
Key("U", vec2(167, 20), 18, Keyboard::AppendSelf),
Key("I", vec2(187, 20), 18, Keyboard::AppendSelf),
Key("O", vec2(207, 20), 18, Keyboard::AppendSelf),
Key("P", vec2(227, 20), 18, Keyboard::AppendSelf),
Key("[", vec2(247, 20), 18, Keyboard::AppendSelf),
Key("]", vec2(267, 20), 18, Keyboard::AppendSelf),
Key("\\", vec2(287, 20), vec2(28, 18), Keyboard::AppendSelf),
Key("Tab", fvec2(5, 20), fvec2(40, 18), Keyboard::Tab),
Key("Q", fvec2(47, 20), 18, Keyboard::AppendSelf),
Key("W", fvec2(67, 20), 18, Keyboard::AppendSelf),
Key("E", fvec2(87, 20), 18, Keyboard::AppendSelf),
Key("R", fvec2(107, 20), 18, Keyboard::AppendSelf),
Key("T", fvec2(127, 20), 18, Keyboard::AppendSelf),
Key("Y", fvec2(147, 20), 18, Keyboard::AppendSelf),
Key("U", fvec2(167, 20), 18, Keyboard::AppendSelf),
Key("I", fvec2(187, 20), 18, Keyboard::AppendSelf),
Key("O", fvec2(207, 20), 18, Keyboard::AppendSelf),
Key("P", fvec2(227, 20), 18, Keyboard::AppendSelf),
Key("[", fvec2(247, 20), 18, Keyboard::AppendSelf),
Key("]", fvec2(267, 20), 18, Keyboard::AppendSelf),
Key("\\", fvec2(287, 20), fvec2(28, 18), Keyboard::AppendSelf),
// 3rd row
Key("Caps", vec2(5, 40), vec2(50, 18), Keyboard::Caps),
Key("A", vec2(57, 40), 18, Keyboard::AppendSelf),
Key("S", vec2(77, 40), 18, Keyboard::AppendSelf),
Key("D", vec2(97, 40), 18, Keyboard::AppendSelf),
Key("F", vec2(117, 40), 18, Keyboard::AppendSelf),
Key("G", vec2(137, 40), 18, Keyboard::AppendSelf),
Key("H", vec2(157, 40), 18, Keyboard::AppendSelf),
Key("J", vec2(177, 40), 18, Keyboard::AppendSelf),
Key("K", vec2(197, 40), 18, Keyboard::AppendSelf),
Key("L", vec2(217, 40), 18, Keyboard::AppendSelf),
Key(";", vec2(237, 40), 18, Keyboard::AppendSelf),
Key("'", vec2(257, 40), 18, Keyboard::AppendSelf),
Key("Enter", vec2(277, 40), vec2(38, 18), Keyboard::Enter),
Key("Caps", fvec2(5, 40), fvec2(50, 18), Keyboard::Caps),
Key("A", fvec2(57, 40), 18, Keyboard::AppendSelf),
Key("S", fvec2(77, 40), 18, Keyboard::AppendSelf),
Key("D", fvec2(97, 40), 18, Keyboard::AppendSelf),
Key("F", fvec2(117, 40), 18, Keyboard::AppendSelf),
Key("G", fvec2(137, 40), 18, Keyboard::AppendSelf),
Key("H", fvec2(157, 40), 18, Keyboard::AppendSelf),
Key("J", fvec2(177, 40), 18, Keyboard::AppendSelf),
Key("K", fvec2(197, 40), 18, Keyboard::AppendSelf),
Key("L", fvec2(217, 40), 18, Keyboard::AppendSelf),
Key(";", fvec2(237, 40), 18, Keyboard::AppendSelf),
Key("'", fvec2(257, 40), 18, Keyboard::AppendSelf),
Key("Enter", fvec2(277, 40), fvec2(38, 18), Keyboard::Enter),
// 4th row
Key("Shift", vec2(5, 60), vec2(60, 18), Keyboard::Shift),
Key("Z", vec2(67, 60), 18, Keyboard::AppendSelf),
Key("X", vec2(87, 60), 18, Keyboard::AppendSelf),
Key("C", vec2(107, 60), 18, Keyboard::AppendSelf),
Key("V", vec2(127, 60), 18, Keyboard::AppendSelf),
Key("B", vec2(147, 60), 18, Keyboard::AppendSelf),
Key("N", vec2(167, 60), 18, Keyboard::AppendSelf),
Key("M", vec2(187, 60), 18, Keyboard::AppendSelf),
Key(",", vec2(207, 60), 18, Keyboard::AppendSelf),
Key(".", vec2(227, 60), 18, Keyboard::AppendSelf),
Key("/", vec2(247, 60), 18, Keyboard::AppendSelf),
Key("Shift", vec2(267, 60), vec2(48, 18), Keyboard::Shift),
Key("Shift", fvec2(5, 60), fvec2(60, 18), Keyboard::Shift),
Key("Z", fvec2(67, 60), 18, Keyboard::AppendSelf),
Key("X", fvec2(87, 60), 18, Keyboard::AppendSelf),
Key("C", fvec2(107, 60), 18, Keyboard::AppendSelf),
Key("V", fvec2(127, 60), 18, Keyboard::AppendSelf),
Key("B", fvec2(147, 60), 18, Keyboard::AppendSelf),
Key("N", fvec2(167, 60), 18, Keyboard::AppendSelf),
Key("M", fvec2(187, 60), 18, Keyboard::AppendSelf),
Key(",", fvec2(207, 60), 18, Keyboard::AppendSelf),
Key(".", fvec2(227, 60), 18, Keyboard::AppendSelf),
Key("/", fvec2(247, 60), 18, Keyboard::AppendSelf),
Key("Shift", fvec2(267, 60), fvec2(48, 18), Keyboard::Shift),
// 5th row
Key("Cancel", vec2(5, 80), vec2(70, 18), Keyboard::OpCancel),
Key("(X)", vec2(77, 80), vec2(23, 18), Keyboard::Op1),
Key("Space", vec2(102, 80), vec2(108, 18), Keyboard::Space),
Key("(!)", vec2(212, 80), vec2(23, 18), Keyboard::Op2),
Key("Confirm", vec2(237, 80), vec2(78, 18), Keyboard::OpConfirm),
Key("Cancel", fvec2(5, 80), fvec2(70, 18), Keyboard::OpCancel),
Key("(X)", fvec2(77, 80), fvec2(23, 18), Keyboard::Op1),
Key("Space", fvec2(102, 80), fvec2(108, 18), Keyboard::Space),
Key("(!)", fvec2(212, 80), fvec2(23, 18), Keyboard::Op2),
Key("Confirm", fvec2(237, 80), fvec2(78, 18), Keyboard::OpConfirm),
},
{
// 1st row
Key("~", vec2(5, 0), 18, Keyboard::AppendSelf),
Key("!", vec2(25, 0), 18, Keyboard::AppendSelf),
Key("@", vec2(45, 0), 18, Keyboard::AppendSelf),
Key("#", vec2(65, 0), 18, Keyboard::AppendSelf),
Key("$", vec2(85, 0), 18, Keyboard::AppendSelf),
Key("%", vec2(105, 0), 18, Keyboard::AppendSelf),
Key("^", vec2(125, 0), 18, Keyboard::AppendSelf),
Key("&", vec2(145, 0), 18, Keyboard::AppendSelf),
Key("*", vec2(165, 0), 18, Keyboard::AppendSelf),
Key("(", vec2(185, 0), 18, Keyboard::AppendSelf),
Key(")", vec2(205, 0), 18, Keyboard::AppendSelf),
Key("_", vec2(225, 0), 18, Keyboard::AppendSelf),
Key("+", vec2(245, 0), 18, Keyboard::AppendSelf),
Key("<---", vec2(265, 0), vec2(50, 18), Keyboard::Backspace),
Key("~", fvec2(5, 0), 18, Keyboard::AppendSelf),
Key("!", fvec2(25, 0), 18, Keyboard::AppendSelf),
Key("@", fvec2(45, 0), 18, Keyboard::AppendSelf),
Key("#", fvec2(65, 0), 18, Keyboard::AppendSelf),
Key("$", fvec2(85, 0), 18, Keyboard::AppendSelf),
Key("%", fvec2(105, 0), 18, Keyboard::AppendSelf),
Key("^", fvec2(125, 0), 18, Keyboard::AppendSelf),
Key("&", fvec2(145, 0), 18, Keyboard::AppendSelf),
Key("*", fvec2(165, 0), 18, Keyboard::AppendSelf),
Key("(", fvec2(185, 0), 18, Keyboard::AppendSelf),
Key(")", fvec2(205, 0), 18, Keyboard::AppendSelf),
Key("_", fvec2(225, 0), 18, Keyboard::AppendSelf),
Key("+", fvec2(245, 0), 18, Keyboard::AppendSelf),
Key("<---", fvec2(265, 0), fvec2(50, 18), Keyboard::Backspace),
// 2nd row
Key("Tab", vec2(5, 20), vec2(40, 18), Keyboard::Tab),
Key("Q", vec2(47, 20), 18, Keyboard::AppendSelf),
Key("W", vec2(67, 20), 18, Keyboard::AppendSelf),
Key("E", vec2(87, 20), 18, Keyboard::AppendSelf),
Key("R", vec2(107, 20), 18, Keyboard::AppendSelf),
Key("T", vec2(127, 20), 18, Keyboard::AppendSelf),
Key("Y", vec2(147, 20), 18, Keyboard::AppendSelf),
Key("U", vec2(167, 20), 18, Keyboard::AppendSelf),
Key("I", vec2(187, 20), 18, Keyboard::AppendSelf),
Key("O", vec2(207, 20), 18, Keyboard::AppendSelf),
Key("P", vec2(227, 20), 18, Keyboard::AppendSelf),
Key("{", vec2(247, 20), 18, Keyboard::AppendSelf),
Key("}", vec2(267, 20), 18, Keyboard::AppendSelf),
Key("|", vec2(287, 20), vec2(28, 18), Keyboard::AppendSelf),
Key("Tab", fvec2(5, 20), fvec2(40, 18), Keyboard::Tab),
Key("Q", fvec2(47, 20), 18, Keyboard::AppendSelf),
Key("W", fvec2(67, 20), 18, Keyboard::AppendSelf),
Key("E", fvec2(87, 20), 18, Keyboard::AppendSelf),
Key("R", fvec2(107, 20), 18, Keyboard::AppendSelf),
Key("T", fvec2(127, 20), 18, Keyboard::AppendSelf),
Key("Y", fvec2(147, 20), 18, Keyboard::AppendSelf),
Key("U", fvec2(167, 20), 18, Keyboard::AppendSelf),
Key("I", fvec2(187, 20), 18, Keyboard::AppendSelf),
Key("O", fvec2(207, 20), 18, Keyboard::AppendSelf),
Key("P", fvec2(227, 20), 18, Keyboard::AppendSelf),
Key("{", fvec2(247, 20), 18, Keyboard::AppendSelf),
Key("}", fvec2(267, 20), 18, Keyboard::AppendSelf),
Key("|", fvec2(287, 20), fvec2(28, 18), Keyboard::AppendSelf),
// 3rd row
Key("Caps", vec2(5, 40), vec2(50, 18), Keyboard::Caps),
Key("A", vec2(57, 40), 18, Keyboard::AppendSelf),
Key("S", vec2(77, 40), 18, Keyboard::AppendSelf),
Key("D", vec2(97, 40), 18, Keyboard::AppendSelf),
Key("F", vec2(117, 40), 18, Keyboard::AppendSelf),
Key("G", vec2(137, 40), 18, Keyboard::AppendSelf),
Key("H", vec2(157, 40), 18, Keyboard::AppendSelf),
Key("J", vec2(177, 40), 18, Keyboard::AppendSelf),
Key("K", vec2(197, 40), 18, Keyboard::AppendSelf),
Key("L", vec2(217, 40), 18, Keyboard::AppendSelf),
Key(":", vec2(237, 40), 18, Keyboard::AppendSelf),
Key("\"", vec2(257, 40), 18, Keyboard::AppendSelf),
Key("Enter", vec2(277, 40), vec2(38, 18), Keyboard::Enter),
Key("Caps", fvec2(5, 40), fvec2(50, 18), Keyboard::Caps),
Key("A", fvec2(57, 40), 18, Keyboard::AppendSelf),
Key("S", fvec2(77, 40), 18, Keyboard::AppendSelf),
Key("D", fvec2(97, 40), 18, Keyboard::AppendSelf),
Key("F", fvec2(117, 40), 18, Keyboard::AppendSelf),
Key("G", fvec2(137, 40), 18, Keyboard::AppendSelf),
Key("H", fvec2(157, 40), 18, Keyboard::AppendSelf),
Key("J", fvec2(177, 40), 18, Keyboard::AppendSelf),
Key("K", fvec2(197, 40), 18, Keyboard::AppendSelf),
Key("L", fvec2(217, 40), 18, Keyboard::AppendSelf),
Key(":", fvec2(237, 40), 18, Keyboard::AppendSelf),
Key("\"", fvec2(257, 40), 18, Keyboard::AppendSelf),
Key("Enter", fvec2(277, 40), fvec2(38, 18), Keyboard::Enter),
// 4th row
Key("Shift", vec2(5, 60), vec2(60, 18), Keyboard::Shift),
Key("Z", vec2(67, 60), 18, Keyboard::AppendSelf),
Key("X", vec2(87, 60), 18, Keyboard::AppendSelf),
Key("C", vec2(107, 60), 18, Keyboard::AppendSelf),
Key("V", vec2(127, 60), 18, Keyboard::AppendSelf),
Key("B", vec2(147, 60), 18, Keyboard::AppendSelf),
Key("N", vec2(167, 60), 18, Keyboard::AppendSelf),
Key("M", vec2(187, 60), 18, Keyboard::AppendSelf),
Key("<", vec2(207, 60), 18, Keyboard::AppendSelf),
Key(">", vec2(227, 60), 18, Keyboard::AppendSelf),
Key("?", vec2(247, 60), 18, Keyboard::AppendSelf),
Key("Shift", vec2(267, 60), vec2(48, 18), Keyboard::Shift),
Key("Shift", fvec2(5, 60), fvec2(60, 18), Keyboard::Shift),
Key("Z", fvec2(67, 60), 18, Keyboard::AppendSelf),
Key("X", fvec2(87, 60), 18, Keyboard::AppendSelf),
Key("C", fvec2(107, 60), 18, Keyboard::AppendSelf),
Key("V", fvec2(127, 60), 18, Keyboard::AppendSelf),
Key("B", fvec2(147, 60), 18, Keyboard::AppendSelf),
Key("N", fvec2(167, 60), 18, Keyboard::AppendSelf),
Key("M", fvec2(187, 60), 18, Keyboard::AppendSelf),
Key("<", fvec2(207, 60), 18, Keyboard::AppendSelf),
Key(">", fvec2(227, 60), 18, Keyboard::AppendSelf),
Key("?", fvec2(247, 60), 18, Keyboard::AppendSelf),
Key("Shift", fvec2(267, 60), fvec2(48, 18), Keyboard::Shift),
// 5th row
Key("Cancel", vec2(5, 80), vec2(70, 18), Keyboard::OpCancel),
Key("(X)", vec2(77, 80), vec2(23, 18), Keyboard::Op1),
Key("Space", vec2(102, 80), vec2(108, 18), Keyboard::Space),
Key("(!)", vec2(212, 80), vec2(23, 18), Keyboard::Op2),
Key("Confirm", vec2(237, 80), vec2(78, 18), Keyboard::OpConfirm),
Key("Cancel", fvec2(5, 80), fvec2(70, 18), Keyboard::OpCancel),
Key("(X)", fvec2(77, 80), fvec2(23, 18), Keyboard::Op1),
Key("Space", fvec2(102, 80), fvec2(108, 18), Keyboard::Space),
Key("(!)", fvec2(212, 80), fvec2(23, 18), Keyboard::Op2),
Key("Confirm", fvec2(237, 80), fvec2(78, 18), Keyboard::OpConfirm),
},
};
@ -248,10 +247,10 @@ void DumpLayout(const std::string& path) {
for (size_t j = 0; j < layouts[0].size(); j++) {
nlohmann::json key;
key["display_char"] = layouts[i][j].k;
key["pos_x"] = layouts[i][j].pos[0];
key["pos_y"] = layouts[i][j].pos[1];
key["size_x"] = layouts[i][j].size[0];
key["size_y"] = layouts[i][j].size[1];
key["pos_x"] = layouts[i][j].pos.x;
key["pos_y"] = layouts[i][j].pos.y;
key["size_x"] = layouts[i][j].size.x;
key["size_y"] = layouts[i][j].size.y;
key["op"] = layouts[i][j].op;
l1.push_back(key);
}
@ -521,20 +520,20 @@ void Keyboard::Update(float delta, LI::Renderer::Ref ren, Hid::Ref inp) {
}
if (flags & Flags_BlendTop) {
ren->OnScreen(ren->GetScreen(false));
ren->DrawRectSolid(0, vec2(400, 240), fade);
ren->DrawRectSolid(0, fvec2(400, 240), fade);
}
if (flags & Flags_BlendBottom) {
ren->OnScreen(ren->GetScreen(true));
ren->DrawRectSolid(0, vec2(320, 240), fade);
ren->DrawRectSolid(0, fvec2(320, 240), fade);
}
}
/// Get the current start possition
vec2 start = flymgr;
// Draw head and Keyboard background
ren->DrawRectSolid(
vec2(0, start.y()), vec2(320, 125),
fvec2(0, start.y()), fvec2(320, 125),
PD::Color("#222222ff").a((flags & Flags_Transparency) ? 0xaa : 0xff));
ren->DrawRectSolid(vec2(0, start.y()), vec2(320, 17), 0xaa000000);
ren->DrawRectSolid(fvec2(0, start.y()), fvec2(320, 17), 0xaa000000);
/// Grab the base layer and go one up for texts
int l = ren->Layer();
ren->Layer(l + 2);
@ -548,11 +547,11 @@ void Keyboard::Update(float delta, LI::Renderer::Ref ren, Hid::Ref inp) {
// s << GamePadIcons::GetIcon(GamePadIcons::R) << " CAPS\n";
// s << GamePadIcons::GetIcon(GamePadIcons::Dpad) << " Move ";
// s << GamePadIcons::GetIcon(GamePadIcons::A) << " Select\n";
// ren->DrawText(vec2(5, start.y() -
// ren->DrawText(fvec2(5, start.y() -
// ren->GetTextDimensions(s.str()).y()+16),
// 0xffffffff, s.str());
// }
ren->DrawText(vec2(5, start.y()), 0xffffffff, "> " + *text);
ren->DrawText(fvec2(5, start.y()), 0xffffffff, "> " + *text);
ren->Layer(l + 1);
/// Offset Keys start height by 22
start[1] += 22;
@ -592,7 +591,7 @@ void Keyboard::Update(float delta, LI::Renderer::Ref ren, Hid::Ref inp) {
if (raw_sel != -1) {
ren->Layer(l);
ren->DrawRectSolid(start + selector - vec2(1), vec2(sel_szs) + vec2(2),
ren->DrawRectSolid(start + selector - fvec2(1), fvec2(sel_szs) + fvec2(2),
0xaaffffff);
ren->Layer(l);
}

View File

@ -28,7 +28,7 @@ MessageMgr::Container::Container(const std::string& title,
const std::string& msg) {
this->title = title;
this->msg = msg;
size = vec2(150, 50);
size = fvec2(150, 50);
// Precalculate colors
col_bg = PD::Color("#111111aa");
col_text = PD::Color("#ffffff");
@ -42,11 +42,11 @@ void MessageMgr::Container::Render(PD::LI::Renderer::Ref ren) {
// result in the same we would waste a lot
// of cpu performance which is a big issue
// espeacilly on the Old3ds...
vec2 tpos = pos;
fvec2 tpos = pos;
// Check if it goes out of screen
// Instant kills cause it will never be on
// Screen agains
if (tpos[1] + size[1] < 0) {
if (tpos.y + size.y < 0) {
kill = true;
}
// If should be removed modify the color by fade
@ -58,7 +58,7 @@ void MessageMgr::Container::Render(PD::LI::Renderer::Ref ren) {
}
// Create a backup Layer to Render
// Text onto the next layer
int l = ren->Layer();
//int l = ren->Layer();
ren->DrawRectSolid(tpos, size, col_bg);
ren->Layer(l + 1);
ren->DrawText(tpos + vec2(4, 2), col_text, title);

View File

@ -1,72 +1,72 @@
/*
MIT License
Copyright (c) 2024 - 2025 René Amthor (tobid7)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <pd/sound/mp3.hpp>
namespace PD {
namespace Music {
int Mp3Decoder::Init(const std::string& path) {
int ret = 0;
int encoding = 0;
if ((ret = mpg123_init() != MPG123_OK)) {
return ret;
}
if ((handle = mpg123_new(nullptr, &ret)) == nullptr) {
return ret;
}
int cnls = 0;
long _rate = 0;
if (mpg123_open(handle, path.c_str()) != MPG123_OK ||
mpg123_getformat(handle, &_rate, &cnls, &encoding)) {
return ret;
}
rate = _rate;
channels = cnls;
mpg123_format_none(handle);
mpg123_format(handle, rate, channels, encoding);
buf_size = mpg123_outblock(handle) * 16;
return ret;
}
void Mp3Decoder::Deinit() {
mpg123_close(handle);
mpg123_delete(handle);
mpg123_exit();
}
u32 Mp3Decoder::GetSampleRate() { return rate; }
u8 Mp3Decoder::GetChannels() { return channels; }
u64 Mp3Decoder::Decode(u16* buf_address) {
size_t done = 0;
mpg123_read(handle, buf_address, buf_size, &done);
return done / sizeof(u16);
}
size_t Mp3Decoder::GetFileSamples() {
off_t len = mpg123_length(handle);
if (len != MPG123_ERR) {
return len * size_t(channels);
}
return -1; // NotExist
}
} // namespace Music
/*
MIT License
Copyright (c) 2024 - 2025 René Amthor (tobid7)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <pd/sound/mp3.hpp>
namespace PD {
namespace Music {
int Mp3Decoder::Init(const std::string& path) {
int ret = 0;
int encoding = 0;
if ((ret = mpg123_init() != MPG123_OK)) {
return ret;
}
if ((handle = mpg123_new(nullptr, &ret)) == nullptr) {
return ret;
}
int cnls = 0;
long _rate = 0;
if (mpg123_open(handle, path.c_str()) != MPG123_OK ||
mpg123_getformat(handle, &_rate, &cnls, &encoding)) {
return ret;
}
rate = _rate;
channels = cnls;
mpg123_format_none(handle);
mpg123_format(handle, rate, channels, encoding);
buf_size = mpg123_outblock(handle) * 16;
return ret;
}
void Mp3Decoder::Deinit() {
mpg123_close(handle);
mpg123_delete(handle);
mpg123_exit();
}
u32 Mp3Decoder::GetSampleRate() { return rate; }
u8 Mp3Decoder::GetChannels() { return channels; }
u64 Mp3Decoder::Decode(u16* buf_address) {
size_t done = 0;
mpg123_read(handle, buf_address, buf_size, &done);
return done / sizeof(u16);
}
size_t Mp3Decoder::GetFileSamples() {
off_t len = mpg123_length(handle);
if (len != MPG123_ERR) {
return len * size_t(channels);
}
return -1; // NotExist
}
} // namespace Music
} // namespace PD

View File

@ -1,64 +1,64 @@
/*
MIT License
Copyright (c) 2024 - 2025 René Amthor (tobid7)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <pd/ui7/container/button.hpp>
namespace PD {
namespace UI7 {
void Button::HandleInput() {
/// Ensure to only check input once
if (inp_done) {
return;
}
/// Ensure it gets sed to false and stays if not pressed
pressed = false;
color = UI7Color_Button;
Assert(screen.get(), "Screen is not set up!");
if (screen->ScreenType() == Screen::Bottom) {
if (io->DragObject(this->GetID(), vec4(FinalPos(), size))) {
if (io->DragReleased) {
color = UI7Color_ButtonActive;
pressed = true;
} else {
color = UI7Color_ButtonHovered;
}
}
}
inp_done = true;
}
void Button::Draw() {
Assert(io.get() && list.get(), "Did you run Container::Init correctly?");
io->Ren->OnScreen(screen);
list->AddRectangle(FinalPos(), size, io->Theme->Get(color));
list->Layer(list->Layer() + 1);
list->AddText(FinalPos() + size * 0.5 - tdim * 0.5, label,
io->Theme->Get(UI7Color_Text));
list->Layer(list->Layer() - 1);
}
void Button::Update() {
Assert(io.get(), "Did you run Container::Init correctly?");
this->SetSize(tdim + io->FramePadding);
}
} // namespace UI7
/*
MIT License
Copyright (c) 2024 - 2025 René Amthor (tobid7)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <pd/ui7/container/button.hpp>
namespace PD {
namespace UI7 {
PD_UI7_API void Button::HandleInput() {
/// Ensure to only check input once
if (inp_done) {
return;
}
/// Ensure it gets sed to false and stays if not pressed
pressed = false;
color = UI7Color_Button;
// Assert(screen.get(), "Screen is not set up!");
// if (screen->ScreenType() == Screen::Bottom) {
if (io->InputHandler->DragObject(this->GetID(), vec4(FinalPos(), size))) {
if (io->InputHandler->DragReleased) {
color = UI7Color_ButtonActive;
pressed = true;
} else {
color = UI7Color_ButtonHovered;
}
}
//}
inp_done = true;
}
PD_UI7_API void Button::Draw() {
// Assert(io.get() && list.get(), "Did you run Container::Init correctly?");
// io->Ren->OnScreen(screen);
list->AddRectangle(FinalPos(), size, io->Theme->Get(color));
list->Layer++;
list->AddText(FinalPos() + size * 0.5 - tdim * 0.5, label,
io->Theme->Get(UI7Color_Text));
list->Layer--;
}
PD_UI7_API void Button::Update() {
// Assert(io.get(), "Did you run Container::Init correctly?");
this->SetSize(tdim + io->FramePadding);
}
} // namespace UI7
} // namespace PD

View File

@ -1,66 +1,66 @@
/*
MIT License
Copyright (c) 2024 - 2025 René Amthor (tobid7)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <pd/ui7/container/checkbox.hpp>
namespace PD {
namespace UI7 {
void Checkbox::HandleInput() {
/// Ensure to only check input once
if (inp_done) {
return;
}
color = UI7Color_FrameBackground;
/// Ensure it gets sed to false and stays if not pressed
Assert(screen.get(), "Screen is not set up!");
if (screen->ScreenType() == Screen::Bottom) {
if (io->DragObject(this->GetID(), vec4(FinalPos(), size))) {
if (io->DragReleased) {
color = UI7Color_FrameBackgroundHovered;
usr_ref = !usr_ref;
} else {
color = UI7Color_FrameBackgroundHovered;
}
}
}
inp_done = true;
}
void Checkbox::Draw() {
Assert(list.get() && io.get(), "Did you run Container::Init correctly?");
io->Ren->OnScreen(screen);
list->AddRectangle(FinalPos(), cbs, io->Theme->Get(color));
if (usr_ref) {
list->AddRectangle(FinalPos() + 2, cbs - 4,
io->Theme->Get(UI7Color_Checkmark));
}
list->AddText(FinalPos() + vec2(cbs.x() + io->ItemSpace.x(),
cbs.y() * 0.5 - tdim.y() * 0.5),
label, io->Theme->Get(UI7Color_Text));
}
void Checkbox::Update() {
Assert(io.get(), "Did you run Container::Init correctly?");
this->SetSize(cbs + vec2(tdim.x() + io->ItemSpace.x(), 0));
}
} // namespace UI7
/*
MIT License
Copyright (c) 2024 - 2025 René Amthor (tobid7)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <pd/ui7/container/checkbox.hpp>
namespace PD {
namespace UI7 {
PD_UI7_API void Checkbox::HandleInput() {
/// Ensure to only check input once
if (inp_done) {
return;
}
color = UI7Color_FrameBackground;
/// Ensure it gets sed to false and stays if not pressed
// Assert(screen.get(), "Screen is not set up!");
// if (screen->ScreenType() == Screen::Bottom) {
if (io->InputHandler->DragObject(this->GetID(), vec4(FinalPos(), size))) {
if (io->InputHandler->DragReleased) {
color = UI7Color_FrameBackgroundHovered;
usr_ref = !usr_ref;
} else {
color = UI7Color_FrameBackgroundHovered;
}
}
//}
inp_done = true;
}
PD_UI7_API void Checkbox::Draw() {
// Assert(list.get() && io.get(), "Did you run Container::Init correctly?");
// io->Ren->OnScreen(screen);
list->AddRectangle(FinalPos(), cbs, io->Theme->Get(color));
if (usr_ref) {
list->AddRectangle(FinalPos() + 2, cbs - 4,
io->Theme->Get(UI7Color_Checkmark));
}
list->AddText(
FinalPos() + fvec2(cbs.x + io->ItemSpace.x, cbs.y * 0.5 - tdim.y * 0.5),
label, io->Theme->Get(UI7Color_Text));
}
PD_UI7_API void Checkbox::Update() {
// Assert(io.get(), "Did you run Container::Init correctly?");
this->SetSize(cbs + fvec2(tdim.x + io->ItemSpace.x, 0));
}
} // namespace UI7
} // namespace PD

View File

@ -1,65 +1,65 @@
/*
MIT License
Copyright (c) 2024 - 2025 René Amthor (tobid7)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <pd/ui7/container/coloredit.hpp>
#include <pd/ui7/container/label.hpp>
namespace PD {
namespace UI7 {
void ColorEdit::HandleInput() {
/// Ensure to only check input once
if (inp_done) {
return;
}
Assert(screen.get(), "Screen is not set up!");
if (screen->ScreenType() == Screen::Bottom) {
if (io->DragObject(this->GetID(), vec4(FinalPos(), size))) {
if (io->DragReleased) {
is_shown = !is_shown;
}
}
}
inp_done = true;
}
void ColorEdit::Draw() {
Assert(io.get() && list.get(), "Did you run Container::Init correctly?");
io->Ren->OnScreen(screen);
list->AddRectangle(FinalPos(), vec2(20, 20), *color_ref);
list->AddText(FinalPos() + vec2(io->ItemSpace.x() + 20, 0), label,
io->Theme->Get(UI7Color_Text));
if (is_shown) {
if (!layout) {
layout = Layout::New(GetID(), io);
}
layout->AddObject(PD::New<Label>("Hello World!", io->Ren));
layout->Update();
io->RegisterDrawList(GetID(), layout->GetDrawList());
}
}
void ColorEdit::Update() {
Assert(io.get(), "Did you run Container::Init correctly?");
this->SetSize(vec2(tdim.x() + io->ItemSpace.x() + 20, 20));
}
} // namespace UI7
/*
MIT License
Copyright (c) 2024 - 2025 René Amthor (tobid7)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <pd/ui7/container/coloredit.hpp>
#include <pd/ui7/container/label.hpp>
namespace PD {
namespace UI7 {
PD_UI7_API void ColorEdit::HandleInput() {
/// Ensure to only check input once
if (inp_done) {
return;
}
// Assert(screen.get(), "Screen is not set up!");
// if (screen->ScreenType() == Screen::Bottom) {
if (io->InputHandler->DragObject(this->GetID(), vec4(FinalPos(), size))) {
if (io->InputHandler->DragReleased) {
is_shown = !is_shown;
}
}
//}
inp_done = true;
}
PD_UI7_API void ColorEdit::Draw() {
// Assert(io.get() && list.get(), "Did you run Container::Init correctly?");
// io->Ren->OnScreen(screen);
list->AddRectangle(FinalPos(), fvec2(20, 20), *color_ref);
list->AddText(FinalPos() + fvec2(io->ItemSpace.x + 20, 0), label,
io->Theme->Get(UI7Color_Text));
if (is_shown) {
if (!layout) {
layout = Layout::New(GetID(), io);
}
layout->AddObject(PD::New<Label>("Hello World!", io));
layout->Update();
io->RegisterDrawList(GetID(), layout->GetDrawList());
}
}
PD_UI7_API void ColorEdit::Update() {
// Assert(io.get(), "Did you run Container::Init correctly?");
this->SetSize(fvec2(tdim.x + io->ItemSpace.x + 20, 20));
}
} // namespace UI7
} // namespace PD

View File

@ -21,19 +21,24 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <pd/core/sys.hpp>
#include <pd/ui7/container/container.hpp>
namespace PD {
namespace UI7 {
void Container::HandleScrolling(vec2 scrolling, vec4 viewport) {
PD_UI7_API void Container::HandleScrolling(fvec2 scrolling, fvec4 viewport) {
if (last_use != 0 && Sys::GetTime() - last_use > 5000) {
rem = true;
}
last_use = Sys::GetTime();
pos -= vec2(0, scrolling.y());
pos -= fvec2(0, scrolling.y);
skippable = !LI::Renderer::InBox(
pos, size, vec4(viewport.xy(), viewport.xy() + viewport.zw()));
pos, size,
fvec4(viewport.x, viewport.y, viewport.x + viewport.z,
viewport.y + viewport.w));
}
PD_UI7_API void Container::HandleInternalInput() {
/** Requires Handle Scrolling First */
}
} // namespace UI7
} // namespace PD

View File

@ -1,113 +1,115 @@
/*
MIT License
Copyright (c) 2024 - 2025 René Amthor (tobid7)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <pd/ui7/container/dragdata.hpp>
#include <pd/ui7/container/label.hpp>
#include <type_traits>
namespace PD {
namespace UI7 {
// Setup Supported Datatypes (Probably making this Object
// header only to not care about datatype support)
template class DragData<float>;
template class DragData<int>;
template class DragData<double>;
template class DragData<u8>;
template class DragData<u16>;
template class DragData<u32>;
template class DragData<u64>;
template <typename T>
void DragData<T>::HandleInput() {
/// Ensure to only check input once
if (inp_done) {
return;
}
Assert(screen.get(), "Screen is not set up!");
if (screen->ScreenType() == Screen::Bottom) {
float off_x = 0;
for (size_t i = 0; i < elm_count; i++) {
std::string p;
if constexpr (std::is_floating_point_v<T>) {
p = std::format("{:.{}f}", data[i], precision);
} else {
p = std::format("{}", data[i]);
}
vec2 tdim = io->Ren->GetTextDimensions(p);
// Unsafe but is the fastest solution
if (io->DragObject(
this->GetID() + i + 1,
vec4(FinalPos() + vec2(off_x, 0), tdim + io->FramePadding))) {
data[i] = std::clamp(T(data[i] + (step * (io->DragPosition[0] -
io->DragLastPosition[0]))),
this->min, this->max);
}
off_x += tdim.x() + io->ItemSpace.x() + io->FramePadding.x();
}
}
inp_done = true;
}
template <typename T>
void DragData<T>::Draw() {
Assert(io.get() && list.get(), "Did you run Container::Init correctly?");
io->Ren->OnScreen(screen);
float off_x = 0.f;
for (size_t i = 0; i < elm_count; i++) {
std::string p;
if constexpr (std::is_floating_point_v<T>) {
p = std::format("{:.{}f}", data[i], precision);
} else {
p = std::format("{}", data[i]);
}
vec2 td = io->Ren->GetTextDimensions(p);
list->AddRectangle(FinalPos() + vec2(off_x, 0), td + io->FramePadding,
io->Theme->Get(UI7Color_Button));
list->Layer(list->Layer() + 1);
list->AddText(FinalPos() + vec2(off_x, 0), p, io->Theme->Get(UI7Color_Text),
LITextFlags_AlignMid, td + io->FramePadding);
list->Layer(list->Layer() - 1);
off_x += td.x() + io->ItemSpace.x() + io->FramePadding.x();
}
list->AddText(FinalPos() + vec2(off_x, io->FramePadding.y() * 0.5), label,
io->Theme->Get(UI7Color_Text));
}
template <typename T>
void DragData<T>::Update() {
Assert(io.get(), "Did you run Container::Init correctly?");
// Probably need to find a faster solution (caching sizes calculated here)
float off_x = 0;
for (size_t i = 0; i < elm_count; i++) {
std::string p;
if constexpr (std::is_floating_point_v<T>) {
p = std::format("{:.{}f}", data[i], precision);
} else {
p = std::format("{}", data[i]);
}
vec2 tdim = io->Ren->GetTextDimensions(p);
off_x += tdim.x() + io->ItemSpace.x() + io->FramePadding.x();
}
this->SetSize(vec2(tdim.x() + off_x, tdim.y() + io->FramePadding.y()));
}
} // namespace UI7
/*
MIT License
Copyright (c) 2024 - 2025 René Amthor (tobid7)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <pd/ui7/container/dragdata.hpp>
#include <pd/ui7/container/label.hpp>
#include <type_traits>
namespace PD {
namespace UI7 {
// Setup Supported Datatypes (Probably making this Object
// header only to not care about datatype support)
template class PD_UI7_API DragData<float>;
template class PD_UI7_API DragData<int>;
template class PD_UI7_API DragData<double>;
template class PD_UI7_API DragData<u8>;
template class PD_UI7_API DragData<u16>;
template class PD_UI7_API DragData<u32>;
template class PD_UI7_API DragData<u64>;
template <typename T>
PD_UI7_API void DragData<T>::HandleInput() {
/// Ensure to only check input once
if (inp_done) {
return;
}
// Assert(screen.get(), "Screen is not set up!");
// if (screen->ScreenType() == Screen::Bottom) {
float off_x = 0;
for (size_t i = 0; i < elm_count; i++) {
std::string p;
if constexpr (std::is_floating_point_v<T>) {
p = std::format("{:.{}f}", data[i], precision);
} else {
p = std::format("{}", data[i]);
}
vec2 tdim = io->Font->GetTextBounds(p, io->FontScale);
// Unsafe but is the fastest solution
if (io->InputHandler->DragObject(
this->GetID() + i + 1,
fvec4(FinalPos() + fvec2(off_x, 0), tdim + io->FramePadding))) {
data[i] = std::clamp(
T(data[i] + (step * (io->InputHandler->DragPosition.x -
io->InputHandler->DragLastPosition.x))),
this->min, this->max);
}
off_x += tdim.x + io->ItemSpace.x + io->FramePadding.x;
}
//}
inp_done = true;
}
template <typename T>
PD_UI7_API void DragData<T>::Draw() {
// Assert(io.get() && list.get(), "Did you run Container::Init correctly?");
// io->Ren->OnScreen(screen);
float off_x = 0.f;
for (size_t i = 0; i < elm_count; i++) {
std::string p;
if constexpr (std::is_floating_point_v<T>) {
p = std::format("{:.{}f}", data[i], precision);
} else {
p = std::format("{}", data[i]);
}
vec2 td = io->Font->GetTextBounds(p, io->FontScale);
list->AddRectangle(FinalPos() + fvec2(off_x, 0), td + io->FramePadding,
io->Theme->Get(UI7Color_Button));
list->Layer++;
list->AddText(FinalPos() + fvec2(off_x, 0), p,
io->Theme->Get(UI7Color_Text), LITextFlags_AlignMid,
td + io->FramePadding);
list->Layer--;
off_x += td.x + io->ItemSpace.x + io->FramePadding.x;
}
list->AddText(FinalPos() + fvec2(off_x, io->FramePadding.y * 0.5), label,
io->Theme->Get(UI7Color_Text));
}
template <typename T>
PD_UI7_API void DragData<T>::Update() {
// Assert(io.get(), "Did you run Container::Init correctly?");
// Probably need to find a faster solution (caching sizes calculated here)
float off_x = 0;
for (size_t i = 0; i < elm_count; i++) {
std::string p;
if constexpr (std::is_floating_point_v<T>) {
p = std::format("{:.{}f}", data[i], precision);
} else {
p = std::format("{}", data[i]);
}
vec2 tdim = io->Font->GetTextBounds(p, io->FontScale);
off_x += tdim.x + io->ItemSpace.x + io->FramePadding.x;
}
this->SetSize(vec2(tdim.x + off_x, tdim.y + io->FramePadding.y));
}
} // namespace UI7
} // namespace PD

View File

@ -1,50 +1,33 @@
/*
MIT License
Copyright (c) 2024 - 2025 René Amthor (tobid7)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <3ds.h>
#include <pd/lib3ds/hwinfo.hpp>
namespace PD {
namespace HwInfo {
void Init() {
mcuHwcInit();
ptmuInit();
}
void Deinit() {
mcuHwcExit();
ptmuExit();
}
bool IsCharging() {
u8 v = 0;
PTMU_GetBatteryChargeState(&v);
return v == 1;
}
int GetBatteryPercentage() {
u8 lvl = 0;
MCUHWC_GetBatteryLevel(&lvl);
return lvl;
}
int GetWifiLevel() { return osGetWifiStrength(); }
} // namespace HwInfo
/*
MIT License
Copyright (c) 2024 - 2025 tobid7
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <pd/ui7/container/dynobj.hpp>
namespace PD {
namespace UI7 {
PD_UI7_API void DynObj::Draw() { pRenFun(io, list, this); }
PD_UI7_API void DynObj::HandleInput() {}
PD_UI7_API void DynObj::Update() {}
} // namespace UI7
} // namespace PD

View File

@ -25,11 +25,13 @@ SOFTWARE.
namespace PD {
namespace UI7 {
void Image::Draw() {
Assert(io.get() && list.get(), "Did you run Container::Init correctly?");
Assert(img.get(), "Image is nullptr!");
io->Ren->OnScreen(screen);
PD_UI7_API void Image::Draw() {
// Assert(io.get() && list.get(), "Did you run Container::Init correctly?");
// Assert(img.get(), "Image is nullptr!");
// io->Ren->OnScreen(screen);
list->Layer++;
list->AddImage(FinalPos(), img, newsize, this->cuv);
list->Layer--;
}
} // namespace UI7
} // namespace PD

View File

@ -25,9 +25,9 @@ SOFTWARE.
namespace PD {
namespace UI7 {
void Label::Draw() {
Assert(io.get() && list.get(), "Did you run Container::Init correctly?");
io->Ren->OnScreen(screen);
PD_UI7_API void Label::Draw() {
// Assert(io.get() && list.get(), "Did you run Container::Init correctly?");
// io->Ren->OnScreen(screen);
list->AddText(FinalPos(), label, io->Theme->Get(UI7Color_Text));
}
} // namespace UI7

View File

@ -1,263 +1,288 @@
/*
MIT License
Copyright (c) 2024 - 2025 René Amthor (tobid7)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <pd/core/strings.hpp>
#include <pd/ui7/drawlist.hpp>
namespace PD {
namespace UI7 {
void DrawList::PathArcToN(const vec2& c, float radius, float a_min, float a_max,
int segments) {
// Path.push_back(c);
PathReserve(segments + 1);
for (int i = 0; i < segments; i++) {
float a = a_min + ((float)i / (float)segments) * (a_max - a_min);
PathNext(vec2(c.x() + std::cos(a) * radius, c.y() + std::sin(a) * radius));
}
}
void DrawList::PathRect(vec2 a, vec2 b, float rounding, UI7DrawFlags flags) {
if (rounding == 0.f) {
PathNext(a);
PathNext(vec2(b.x(), a.y()));
PathNext(b);
PathNext(vec2(a.x(), b.y()));
} else {
PathArcToN(vec2(a.x() + rounding, a.y() + rounding), rounding, 4*6, 4*9, 21);
PathArcToN(vec2(b.x() - rounding, a.y() + rounding), rounding, 4*9, 4*12, 21);
PathArcToN(vec2(b.x() - rounding, b.y() - rounding), rounding, 4*0, 4*3, 21);
PathArcToN(vec2(a.x() + rounding, b.y() - rounding), rounding, 4*3, 4*6, 21);
}
}
void DrawList::AddRect(const vec2& pos, const vec2& size, const UI7Color& clr,
int thickness) {
if (!ren->InBox(pos, size, ren->GetViewport())) {
return;
}
ren->UseTex();
PathRect(pos, pos + size);
PathStroke(clr, thickness, UI7DrawFlags_Close);
}
void DrawList::AddRectangle(vec2 pos, vec2 szs, const UI7Color& clr) {
if (!ren->InBox(pos, szs, ren->GetViewport())) {
return;
}
ren->UseTex();
PathRect(pos, pos + szs);
PathFill(clr);
}
void DrawList::AddTriangle(const vec2& a, const vec2& b, const vec2& c,
const UI7Color& clr, int thickness) {
ren->UseTex();
PathNext(a);
PathNext(b);
PathNext(c);
PathStroke(clr, thickness, UI7DrawFlags_Close);
}
void DrawList::AddTriangleFilled(const vec2& a, const vec2& b, const vec2& c,
const UI7Color& clr) {
ren->UseTex();
PathNext(a);
PathNext(b);
PathNext(c);
PathFill(clr);
}
void DrawList::AddCircle(const vec2& pos, float rad, UI7Color col,
int num_segments, int thickness) {
if (num_segments <= 0) {
// Auto Segment
} else {
float am = (M_PI * 2.0f) * ((float)num_segments) / (float)num_segments;
PathArcToN(pos, rad, 0.f, am, num_segments);
}
ren->UseTex();
PathStroke(col, thickness, UI7DrawFlags_Close);
}
void DrawList::AddCircleFilled(const vec2& pos, float rad, UI7Color col,
int num_segments) {
if (num_segments <= 0) {
// Auto Segment
} else {
float am = (M_PI * 2.0f) * ((float)num_segments) / (float)num_segments;
PathArcToN(pos, rad, 0.f, am, num_segments);
}
ren->UseTex();
PathFill(col);
}
void DrawList::AddText(vec2 pos, const std::string& text, const UI7Color& clr,
LITextFlags flags, vec2 box) {
if (!ren->Font()) {
return;
}
u32 id = Strings::FastHash(text);
LI::StaticText::Ref e;
auto f = static_text.find(id);
if (static_text.find(id) == static_text.end()) {
e = LI::StaticText::New();
static_text[id] = e;
} else {
e = f->second;
}
if (!e->IsSetup() || e->Font() != ren->Font()) {
int l = ren->Layer();
ren->Layer(layer);
e->Setup(ren.get(), pos, clr, text, flags, box);
e->Font(ren->Font());
ren->Layer(l);
}
e->SetPos(pos);
e->SetColor(clr);
e->SetLayer(layer);
if (!clip_rects.empty()) {
e->SetScissorMode(LI::ScissorMode_Normal);
e->ScissorRect(clip_rects.top());
}
for (auto it : e->GetRawObject()->List()) {
this->commands.push_back(std::make_pair(
ren->CurrentScreen()->ScreenType() == Screen::Bottom, it));
}
e->GetRawObject()->ReCopy();
}
void DrawList::AddImage(vec2 pos, Texture::Ref img, vec2 size, LI::Rect uv) {
size = size == 0.f ? img->GetSize() : size;
uv = (uv.Top() == 0.0f && uv.Bot() == 0.0f) ? img->GetUV() : uv;
if (!ren->InBox(pos, size, ren->GetViewport())) {
return;
}
auto rect = ren->CreateRect(pos, size, 0.f);
auto cmd = LI::Command::New();
ren->UseTex(img);
ren->SetupCommand(cmd);
cmd->Layer(layer);
if (!clip_rects.empty()) {
cmd->SetScissorMode(LI::ScissorMode_Normal);
cmd->ScissorRect(clip_rects.top());
}
ren->QuadCommand(cmd, rect, uv, 0xffffffff);
commands.push_back(std::make_pair(
ren->CurrentScreen()->ScreenType() == Screen::Bottom, cmd));
}
void DrawList::AddLine(const vec2& a, const vec2& b, const UI7Color& clr,
int t) {
if (!ren->InBox(a, ren->GetViewport()) &&
!ren->InBox(b, ren->GetViewport())) {
return;
}
ren->UseTex();
PathNext(a);
PathNext(b);
PathStroke(clr, t);
}
// TODO: Don't render OOS
void DrawList::AddPolyLine(const std::vector<vec2>& points, const UI7Color& clr,
UI7DrawFlags flags, int thickness) {
if (points.size() < 2) {
return;
}
auto cmd = LI::Command::New();
ren->SetupCommand(cmd);
cmd->Layer(layer);
if (!clip_rects.empty()) {
cmd->SetScissorMode(LI::ScissorMode_Normal);
cmd->ScissorRect(clip_rects.top());
}
bool close = (flags & UI7DrawFlags_Close);
int num_points = close ? (int)points.size() : (int)points.size() - 1;
if (flags & UI7DrawFlags_AALines) {
// TODO: Find a way to draw less garbage looking lines
} else {
// Non antialiased lines look awful when rendering with thickness != 1
for (int i = 0; i < num_points; i++) {
int j = (i + 1) == (int)points.size() ? 0 : (i + 1);
auto line = ren->CreateLine(points[i], points[j], thickness);
ren->QuadCommand(cmd, line, vec4(0.f, 1.f, 1.f, 0.f), clr);
}
}
commands.push_back(std::make_pair(
ren->CurrentScreen()->ScreenType() == Screen::Bottom, cmd));
}
// TODO: Don't render OOS (Probably make it with a define as it
// would probably be faster to render out of screen than checking if
// it could be skipped)
void DrawList::AddConvexPolyFilled(const std::vector<vec2>& points,
const UI7Color& clr) {
if (points.size() < 3) {
return; // Need at least three points
}
auto cmd = LI::Command::New();
ren->SetupCommand(cmd);
cmd->Layer(layer);
if (!clip_rects.empty()) {
cmd->SetScissorMode(LI::ScissorMode_Normal);
cmd->ScissorRect(clip_rects.top());
}
for (int i = 2; i < (int)points.size(); i++) {
cmd->PushIndex(0).PushIndex(i).PushIndex(i - 1);
}
for (int i = 0; i < (int)points.size(); i++) {
cmd->PushVertex(LI::Vertex(points[i], vec2(0, 0), clr));
}
commands.push_back(std::make_pair(
ren->CurrentScreen()->ScreenType() == Screen::Bottom, cmd));
}
void DrawList::Clear() { commands.clear(); }
void DrawList::Process() {
num_vertices = 0;
num_indices = 0;
for (auto command : commands) {
ren->OnScreen(ren->GetScreen(command.first));
command.second->Layer(command.second->Layer() + base);
ren->PushCommand(command.second);
num_vertices += command.second->VertexList().size();
num_indices += command.second->IndexList().size();
}
commands.clear();
layer = 0;
std::vector<u32> rem;
for (auto it : static_text) {
if (!it.second->Used()) {
rem.push_back(it.first);
}
it.second->SetUnused();
}
for (auto& it : rem) {
static_text.erase(it);
}
while (!clip_rects.empty()) {
clip_rects.pop();
}
}
} // namespace UI7
/*
MIT License
Copyright (c) 2024 - 2025 René Amthor (tobid7)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <pd/core/core.hpp>
#include <pd/ui7/drawlist.hpp>
#include <pd/ui7/io.hpp>
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
namespace PD {
namespace UI7 {
PD_UI7_API void DrawList::PathArcToN(const fvec2& c, float radius, float a_min,
float a_max, int segments) {
// Path.push_back(c);
PathReserve(segments + 1);
for (int i = 0; i < segments; i++) {
float a = a_min + ((float)i / (float)segments) * (a_max - a_min);
PathNext(vec2(c.x + std::cos(a) * radius, c.y + std::sin(a) * radius));
}
}
PD_UI7_API void DrawList::PathRect(fvec2 a, fvec2 b, float rounding,
UI7DrawFlags flags) {
if (rounding == 0.f) {
PathNext(a);
PathNext(vec2(b.x, a.y));
PathNext(b);
PathNext(vec2(a.x, b.y));
} else {
PathArcToN(vec2(a.x + rounding, a.y + rounding), rounding, 4 * 6, 4 * 9,
21);
PathArcToN(vec2(b.x - rounding, a.y + rounding), rounding, 4 * 9, 4 * 12,
21);
PathArcToN(vec2(b.x - rounding, b.y - rounding), rounding, 4 * 0, 4 * 3,
21);
PathArcToN(vec2(a.x + rounding, b.y - rounding), rounding, 4 * 3, 4 * 6,
21);
}
}
PD_UI7_API void DrawList::AddRect(const fvec2& pos, const fvec2& size,
const UI7Color& clr, int thickness) {
PathRect(pos, pos + size);
PathStroke(clr, thickness, UI7DrawFlags_Close);
}
PD_UI7_API void DrawList::AddRectangle(fvec2 pos, fvec2 szs,
const UI7Color& clr) {
PathRect(pos, pos + szs);
PathFill(clr);
}
PD_UI7_API void DrawList::AddTriangle(const fvec2& a, const fvec2& b,
const fvec2& c, const UI7Color& clr,
int thickness) {
PathNext(a);
PathNext(b);
PathNext(c);
PathStroke(clr, thickness, UI7DrawFlags_Close);
}
PD_UI7_API void DrawList::AddTriangleFilled(const fvec2& a, const fvec2& b,
const fvec2& c,
const UI7Color& clr) {
PathNext(a);
PathNext(b);
PathNext(c);
PathFill(clr);
}
PD_UI7_API void DrawList::AddCircle(const fvec2& pos, float rad, UI7Color col,
int num_segments, int thickness) {
if (num_segments <= 0) {
// Auto Segment
} else {
float am = (M_PI * 2.0f) * ((float)num_segments) / (float)num_segments;
PathArcToN(pos, rad, 0.f, am, num_segments);
}
PathStroke(col, thickness, UI7DrawFlags_Close);
}
PD_UI7_API void DrawList::AddCircleFilled(const fvec2& pos, float rad,
UI7Color col, int num_segments) {
if (num_segments <= 0) {
// Auto Segment
} else {
float am = (M_PI * 2.0f) * ((float)num_segments) / (float)num_segments;
PathArcToN(pos, rad, 0.f, am, num_segments);
}
PathFill(col);
}
PD_UI7_API void DrawList::AddText(fvec2 pos, const std::string& text,
const UI7Color& clr, u32 flags, fvec2 box) {
Vec<LI::Command::Ref> cmds;
pIO->Font->CmdTextEx(cmds, pos, clr, pIO->FontScale, text, flags, box);
for (size_t i = 0; i < cmds.Size(); i++) {
ClipCmd(cmds[i]);
cmds[i]->Layer = Layer;
cmds[i]->Index = Commands.Size();
Commands.Add(cmds[i]);
}
// if (!IO->Ren->Font()) {
// return;
// }
// u32 id = Strings::FastHash(text);
// LI::StaticText::Ref e;
// auto f = static_text.find(id);
// if (static_text.find(id) == static_text.end()) {
// e = LI::StaticText::New();
// static_text[id] = e;
// } else {
// e = f->second;
// }
// if (!e->IsSetup() || e->Font() != IO->Ren->Font()) {
// int l = IO->Ren->Layer();
// IO->Ren->Layer(layer);
// e->Setup(ren.get(), pos, clr, text, flags, box);
// e->Font(IO->Ren->Font());
// IO->Ren->Layer(l);
// }
// e->SetPos(pos);
// e->SetColor(clr);
// e->SetLayer(layer);
// if (!clip_rects.empty()) {
// e->SetScissorMode(LI::ScissorMode_Normal);
// e->ScissorRect(clip_rects.top());
// }
// for (auto it : e->GetRawObject()->List()) {
// this->commands.push_back(std::make_pair(
// IO->Ren->CurrentScreen()->ScreenType() == Screen::Bottom, it));
// }
// e->GetRawObject()->ReCopy();
}
PD_UI7_API void DrawList::AddImage(fvec2 pos, LI::Texture::Ref img, fvec2 size,
LI::Rect uv) {
size = size == 0.f ? fvec2(img->GetSize().x, img->GetSize().y) : size;
uv = (uv.Top() == 0.0f && uv.Bot() == 0.0f) ? img->GetUV() : uv;
LI::Command::Ref cmd = LI::Command::New();
cmd->Layer = Layer;
cmd->Index = Commands.Size();
cmd->Tex = img;
auto r = LI::Renderer::PrimRect(pos, size);
LI::Renderer::CmdQuad(cmd, r, uv, 0xffffffff);
// auto rect = IO->Ren->CreateRect(pos, size, 0.f);
// auto cmd = LI::Command::New();
// IO->Ren->UseTex(img);
// IO->Ren->SetupCommand(cmd);
// cmd->Layer(layer);
// if (!clip_rects.empty()) {
// cmd->SetScissorMode(LI::ScissorMode_Normal);
// cmd->ScissorRect(clip_rects.top());
// }
// IO->Ren->QuadCommand(cmd, rect, uv, 0xffffffff);
// commands.push_back(std::make_pair(
// IO->Ren->CurrentScreen()->ScreenType() == Screen::Bottom, cmd));
}
PD_UI7_API void DrawList::AddLine(const fvec2& a, const fvec2& b,
const UI7Color& clr, int t) {
PathNext(a);
PathNext(b);
PathStroke(clr, t);
}
// TODO: Don't render OOS
PD_UI7_API void DrawList::AddPolyLine(const Vec<fvec2>& points,
const UI7Color& clr, UI7DrawFlags flags,
int thickness) {
if (points.Size() < 2) {
return;
}
auto cmd = LI::Command::New();
cmd->Index = Commands.Size();
cmd->Layer = Layer;
cmd->Tex = pIO->Ren->WhitePixel;
ClipCmd(cmd);
bool close = (flags & UI7DrawFlags_Close);
int num_points = close ? (int)points.Size() : (int)points.Size() - 1;
if (flags & UI7DrawFlags_AALines) {
// TODO: Find a way to draw less garbage looking lines
} else {
// Non antialiased lines look awful when rendering with thickness != 1
for (int i = 0; i < num_points; i++) {
int j = (i + 1) == (int)points.Size() ? 0 : (i + 1);
auto line = LI::Renderer::PrimLine(points[i], points[j], thickness);
LI::Renderer::CmdQuad(cmd, line, vec4(0.f, 1.f, 1.f, 0.f), clr);
}
}
Commands.Add(cmd);
}
PD_UI7_API void DrawList::ClipCmd(LI::Command::Ref cmd) {
if (!pClipRects.IsEmpty()) {
cmd->ScissorEnabled = true;
fvec4 sr = pClipRects.Top();
cmd->ScissorRect = ivec4(sr.x, sr.y, sr.z, sr.w);
}
}
// TODO: Don't render OOS (Probably make it with a define as it
// would probably be faster to render out of screen than checking if
// it could be skipped)
PD_UI7_API void DrawList::AddConvexPolyFilled(const Vec<fvec2>& points,
const UI7Color& clr) {
if (points.Size() < 3) {
return; // Need at least three points
}
auto cmd = LI::Command::New();
cmd->Index = Commands.Size();
cmd->Layer = Layer;
auto tex = CurrentTex;
if (!tex) {
tex = pIO->Ren->WhitePixel;
}
cmd->Tex = tex;
ClipCmd(cmd);
for (int i = 2; i < (int)points.Size(); i++) {
cmd->AppendIndex(0).AppendIndex(i).AppendIndex(i - 1);
}
for (int i = 0; i < (int)points.Size(); i++) {
cmd->AppendVertex(LI::Vertex(points[i], fvec2(0, 0), clr));
}
Commands.Add(cmd);
}
PD_UI7_API void DrawList::Clear() { Commands.Clear(); }
/** Process [Render] the Drawlist */
PD_UI7_API void DrawList::Process(LI::DrawList::Ref d) {
std::sort(Commands.Begin(), Commands.End(),
[](LI::Command::Ref a, LI::Command::Ref b) {
/** Advanced (for saving Drawcalls)
* - Probably could handle this by creating diffrent layers
* for texts and solid objectives
* if(a->Tex == b->Tex) { return a->Layer < b->Layer; }
* return a->Tex < b->Tex;
*/
/** Simple */
return a->Layer < b->Layer;
});
NumVertices = 0;
NumIndices = 0;
for (auto command = Commands.Begin(); command != Commands.End(); command++) {
// IO->Ren->OnScreen(IO->Ren->GetScreen(command.first));
(*command)->Layer = (*command)->Layer + Base;
d->AddCommand(*command);
NumVertices += (*command)->VertexBuffer.Size();
NumIndices += (*command)->IndexBuffer.Size();
}
Commands.Clear();
Layer = 0;
std::vector<u32> rem;
// for (auto it : static_text) {
// if (!it.second->Used()) {
// rem.push_back(it.first);
// }
// it.second->SetUnused();
// }
// for (auto& it : rem) {
// static_text.erase(it);
// }
pClipRects.Clear();
}
} // namespace UI7
} // namespace PD

View File

@ -1,42 +1,40 @@
/*
MIT License
Copyright (c) 2024 - 2025 René Amthor (tobid7)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <pd/core/timetrace.hpp>
#include <pd/ui7/io.hpp>
namespace PD {
void UI7::IO::Update() {
u64 current = Sys::GetNanoTime();
Delta = static_cast<float>(current - LastTime) / 1000000.f;
LastTime = current;
DeltaStats->Add(Delta * 1000);
Time->Update();
DragTime->Update();
DragReleased = false;
DragReleasedAW = false;
DragDoubleRelease = false;
Framerate = 1000.f / Delta;
DrawListRegestry.clear();
RegisterDrawList("CtxBackList", Back);
}
/*
MIT License
Copyright (c) 2024 - 2025 René Amthor (tobid7)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <pd/core/core.hpp>
#include <pd/ui7/io.hpp>
namespace PD {
PD_UI7_API void UI7::IO::Update() {
u64 current = Sys::GetNanoTime();
Delta = static_cast<float>(current - LastTime) / 1000000.f;
LastTime = current;
DeltaStats->Add(Delta * 1000);
Time->Update();
InputHandler->Update();
Framerate = 1000.f / Delta;
DrawListRegestry.Clear();
DrawListRegestry.PushFront(Pair<UI7::ID, DrawList::Ref>("CtxBackList", Back));
// RegisterDrawList("CtxBackList", Back);
}
} // namespace PD

View File

@ -1,118 +1,140 @@
/*
MIT License
Copyright (c) 2024 - 2025 René Amthor (tobid7)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <pd/ui7/layout.hpp>
namespace PD {
namespace UI7 {
void Layout::CursorInit() { Cursor = WorkRect.xy(); }
void Layout::SameLine() {
BackupCursor = LastObjSize;
Cursor = SamelineCursor;
}
void Layout::CursorMove(const vec2& size) {
LastObjSize = size;
SamelineCursor = Cursor + vec2(size[0] + IO->ItemSpace[0], 0);
if (BeforeSameLine[1]) {
Cursor = vec2(IO->MenuPadding[0],
Cursor[1] + BeforeSameLine[1] + IO->ItemSpace[1]);
BeforeSameLine = 0.f;
} else {
Cursor = vec2(IO->MenuPadding[0] + InitialCursorOffset[0],
Cursor[1] + size[1] + IO->ItemSpace[1]);
}
// Logical Issue here as x should use a max check
MaxPosition = vec2(std::max(MaxPosition[0], SamelineCursor[0]), Cursor[1]);
}
bool Layout::ObjectWorkPos(vec2& movpos) {
if (Scrolling[1]) {
movpos[1] -= ScrollOffset[1];
if (!IO->Ren->InBox(movpos, LastObjSize,
vec4(WorkRect.xy(), WorkRect.xy() + WorkRect.zw()))) {
return true;
}
}
return false;
}
void Layout::AddObject(Container::Ref obj) {
obj->Init(IO, DrawList);
obj->SetPos(AlignPosition(Cursor, obj->GetSize(), WorkRect, GetAlignment()));
obj->Update();
CursorMove(obj->GetSize());
obj->HandleScrolling(ScrollOffset, WorkRect);
Objects.push_back(obj);
}
Container::Ref Layout::FindObject(u32 id) {
for (auto& it : IDObjects) {
if (it->GetID() == id) {
return it;
}
}
return nullptr;
}
vec2 Layout::AlignPosition(vec2 pos, vec2 size, vec4 area, UI7Align alignment) {
vec2 p = pos;
if (alignment & UI7Align_Center) {
p[0] = (area[0] + area[2]) * 0.5 - (pos[0] - area[0] + size[0] * 0.5);
} else if (alignment & UI7Align_Right) {
}
if (alignment & UI7Align_Mid) {
p[1] = (area[1] + area[3]) * 0.5 - (pos[1] - area[1] + size[1] * 0.5);
} else if (alignment & UI7Align_Bottom) {
}
return p;
}
void Layout::Update() {
for (auto& it : Objects) {
if (it->GetID() != 0 && !FindObject(it->GetID())) {
IDObjects.push_back(it);
}
if (!it->Skippable()) {
it->SetPos(it->GetPos() + Pos);
it->HandleInput();
it->UnlockInput();
it->Draw();
}
}
std::vector<size_t> tbr;
for (size_t i = 0; i < IDObjects.size(); i++) {
if (IDObjects[i]->Removable()) {
tbr.push_back(i);
}
}
for (auto& it : tbr) {
IDObjects.erase(IDObjects.begin() + it);
}
Objects.clear();
WorkRect = vec4(WorkRect.xy(), Size - IO->MenuPadding);
CursorInit();
}
} // namespace UI7
/*
MIT License
Copyright (c) 2024 - 2025 René Amthor (tobid7)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <pd/ui7/layout.hpp>
namespace PD {
namespace UI7 {
PD_UI7_API void Layout::CursorInit() { Cursor = fvec2(WorkRect.x, WorkRect.y); }
PD_UI7_API void Layout::SameLine() {
BackupCursor = LastObjSize;
Cursor = SamelineCursor;
}
PD_UI7_API void Layout::CursorMove(const fvec2& size) {
LastObjSize = size;
SamelineCursor = Cursor + fvec2(size.x + IO->ItemSpace.x, 0);
if (BeforeSameLine.y) {
Cursor =
fvec2(IO->MenuPadding.x, Cursor.y + BeforeSameLine.y + IO->ItemSpace.y);
BeforeSameLine = 0.f;
} else {
Cursor = fvec2(IO->MenuPadding.x + InitialCursorOffset.x,
Cursor.y + size.y + IO->ItemSpace.y);
}
// Logical Issue here as x should use a max check
MaxPosition = fvec2(std::max(MaxPosition.x, SamelineCursor.x), Cursor.y);
}
PD_UI7_API bool Layout::ObjectWorkPos(fvec2& movpos) {
if (Scrolling[1]) {
movpos.y -= ScrollOffset.y;
if (!IO->Ren->InBox(movpos, LastObjSize,
fvec4(WorkRect.x, WorkRect.y, WorkRect.x + WorkRect.z,
WorkRect.y + WorkRect.w))) {
return true;
}
}
return false;
}
PD_UI7_API void Layout::AddObject(Container::Ref obj) {
obj->Init(IO, DrawList);
obj->SetPos(AlignPosition(Cursor, obj->GetSize(), WorkRect, GetAlignment()));
obj->Update();
CursorMove(obj->GetSize());
obj->HandleScrolling(ScrollOffset, WorkRect);
Objects.PushBack(obj);
}
PD_UI7_API void Layout::AddObjectEx(Container::Ref obj, u32 flags) {
obj->Init(IO, DrawList);
if (!(flags & 1)) {
obj->SetPos(
AlignPosition(Cursor, obj->GetSize(), WorkRect, GetAlignment()));
}
obj->Update();
if (!(flags & 1)) {
CursorMove(obj->GetSize());
}
if (!(flags & 2)) {
obj->HandleScrolling(ScrollOffset, WorkRect);
}
if (!(flags & 4)) {
Objects.PushFront(obj);
} else {
Objects.PushBack(obj);
}
}
PD_UI7_API Container::Ref Layout::FindObject(u32 id) {
for (auto& it : IDObjects) {
if (it->GetID() == id) {
return it;
}
}
return nullptr;
}
PD_UI7_API fvec2 Layout::AlignPosition(fvec2 pos, fvec2 size, fvec4 area,
UI7Align alignment) {
vec2 p = pos;
if (alignment & UI7Align_Center) {
p.x = (area.x + area.z) * 0.5 - (pos.x - area.x + size.x * 0.5);
} else if (alignment & UI7Align_Right) {
}
if (alignment & UI7Align_Mid) {
p.y = (area.y + area.w) * 0.5 - (pos.y - area.y + size.y * 0.5);
} else if (alignment & UI7Align_Bottom) {
}
return p;
}
PD_UI7_API void Layout::Update() {
for (auto& it : Objects) {
if (it->GetID() != 0 && !FindObject(it->GetID())) {
IDObjects.push_back(it);
}
if (!it->Skippable()) {
it->SetPos(it->GetPos() + Pos);
it->HandleInput();
it->UnlockInput();
it->Draw();
}
}
std::vector<size_t> tbr;
for (size_t i = 0; i < IDObjects.size(); i++) {
if (IDObjects[i]->Removable()) {
tbr.push_back(i);
}
}
for (auto& it : tbr) {
IDObjects.erase(IDObjects.begin() + it);
}
Objects.Clear();
WorkRect = fvec4(fvec2(WorkRect.x, WorkRect.y), Size - IO->MenuPadding);
CursorInit();
}
} // namespace UI7
} // namespace PD

File diff suppressed because it is too large Load Diff

269
source/ui7/remenu.cpp Normal file
View File

@ -0,0 +1,269 @@
/*
MIT License
Copyright (c) 2024 - 2025 tobid7
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <pd/ui7/containers.hpp>
#include <pd/ui7/remenu.hpp>
namespace PD {
namespace UI7 {
PD_UI7_API void UI7::ReMenu::Label(const std::string& label) {
// Layout API
auto r = PD::New<UI7::Label>(label, pIO);
pLayout->AddObject(r);
}
PD_UI7_API bool UI7::ReMenu::Button(const std::string& label) {
bool ret = false;
u32 id = Strings::FastHash("btn" + label +
std::to_string(pLayout->Objects.Size()));
Container::Ref r = pLayout->FindObject(id);
if (!r) {
r = PD::New<UI7::Button>(label, pIO);
r->SetID(id);
}
pLayout->AddObject(r);
if (!r->Skippable()) {
ret = std::static_pointer_cast<UI7::Button>(r)->IsPressed();
}
return ret;
}
/*PD_UI7_API void UI7::ReMenu::DragFloat(const std::string& label, float* data,
size_t num_elms) {
u32 id = Strings::FastHash("dfl" + label + std::to_string(count_btn++));
Container::Ref r = Layout->FindObject(id);
if (!r) {
r = PD::New<UI7::DragData<float>>(label, data, num_elms, io);
r->SetID(id);
}
Layout->AddObject(r);
}*/
PD_UI7_API void UI7::ReMenu::Checkbox(const std::string& label, bool& v) {
u32 id = Strings::FastHash("cbx" + label +
std::to_string(pLayout->Objects.Size()));
Container::Ref r = pLayout->FindObject(id);
if (!r) {
r = PD::New<UI7::Checkbox>(label, v, pIO);
r->SetID(id);
}
pLayout->AddObject(r);
}
PD_UI7_API void UI7::ReMenu::Image(LI::Texture::Ref img, fvec2 size,
LI::Rect uv) {
Container::Ref r = PD::New<UI7::Image>(img, size, uv);
pLayout->AddObject(r);
}
PD_UI7_API void ReMenu::Separator() {
// Dynamic Objects are very simple...
Container::Ref r = PD::New<UI7::DynObj>(
[=, this](UI7::IO::Ref io, UI7::DrawList::Ref l, UI7::Container* self) {
l->AddRect(self->FinalPos(), self->GetSize(),
pIO->Theme->Get(UI7Color_TextDead));
});
// Set size before pushing (cause Cursor Move will require it)
r->SetSize(fvec2(pLayout->Size.x - 10, 1));
pLayout->AddObject(r);
}
PD_UI7_API void ReMenu::SeparatorText(const std::string& label) {
// Also note to use [=] instead of [&] to not undefined access label
Container::Ref r = PD::New<UI7::DynObj>(
[=, this](UI7::IO::Ref io, UI7::DrawList::Ref l, UI7::Container* self) {
fvec2 size = self->GetSize();
fvec2 tdim = io->Font->GetTextBounds(label, io->FontScale);
fvec2 pos = self->FinalPos();
auto align = pLayout->GetAlignment();
vec2 rpos = pLayout->AlignPosition(
pos, tdim, fvec4(pLayout->Pos, pLayout->Size), align);
if (!(align & UI7Align_Left)) {
l->AddRectangle(fvec2(rpos.x + io->FramePadding.x, tdim.y * 0.5),
fvec2(pos.x - rpos.x - io->MenuPadding.x, 1),
io->Theme->Get(UI7Color_TextDead));
}
if (!(align & UI7Align_Right)) {
l->AddRectangle(
pos + fvec2(tdim.x + io->FramePadding.x, tdim.y * 0.5),
fvec2(size.x - tdim.x - io->MenuPadding.x, 1),
io->Theme->Get(UI7Color_TextDead));
}
l->AddText(rpos, label, io->Theme->Get(UI7Color_Text), 0,
fvec2(pLayout->Size.x, self->GetSize().y));
});
// Set size before pushing (cause Cursor Move will require it)
r->SetSize(
fvec2(pLayout->Size.x - 10, pIO->Font->PixelHeight * pIO->FontScale));
pLayout->AddObject(r);
}
PD_UI7_API void ReMenu::HandleFocus() {
// Check if menu can be focused for Selective Menu Input API
vec4 newarea = fvec4(pLayout->Pos, pLayout->Size);
if (!pIsOpen) {
newarea = fvec4(pLayout->Pos, fvec2(pLayout->Size.x, TitleBarHeight));
}
if (pIO->Inp->IsDown(pIO->Inp->Touch) &&
pIO->Ren->InBox(pIO->Inp->TouchPos(), newarea) &&
!pIO->Ren->InBox(pIO->Inp->TouchPos(),
pIO->InputHandler->FocusedMenuRect)) {
pIO->InputHandler->FocusedMenu = pID;
}
if (pIO->InputHandler->FocusedMenu == pID) {
pIO->InputHandler->FocusedMenuRect = newarea;
}
}
PD_UI7_API void ReMenu::HandleScrolling() {}
PD_UI7_API void ReMenu::HandleTitlebarActions() {
// Collapse
if (!(Flags & UI7MenuFlags_NoCollapse)) {
vec2 cpos = pLayout->Pos + pIO->FramePadding;
// clr_collapse_tri = UI7Color_FrameBackground;
if (pIO->InputHandler->DragObject(UI7::ID(pID.GetName() + "clbse"),
fvec4(cpos, fvec2(18, TitleBarHeight)))) {
if (pIO->InputHandler->DragReleased) {
pIsOpen = !pIsOpen;
}
// clr_collapse_tri = UI7Color_FrameBackgroundHovered;
}
}
// Close Logic
if (!(Flags & UI7MenuFlags_NoClose) && pIsShown != nullptr) {
vec2 cpos =
fvec2(pLayout->Pos.x + pLayout->Size.x - 12 - pIO->FramePadding.x,
pLayout->Pos.y + pIO->FramePadding.y);
// clr_close_btn = UI7Color_FrameBackground;
if (pIO->InputHandler->DragObject(UI7::ID(pID.GetName() + "clse"),
fvec4(cpos, fvec2(12)))) {
if (pIO->InputHandler->DragReleased) {
*pIsShown = !(*pIsShown);
}
// clr_close_btn = UI7Color_FrameBackgroundHovered;
}
}
// Menu Movement
if (!(Flags & UI7MenuFlags_NoMove)) {
if (pIO->InputHandler->DragObject(
pID.GetName() + "tmv",
fvec4(pLayout->Pos, fvec2(pLayout->Size.x, TitleBarHeight)))) {
if (pIO->InputHandler->DragDoubleRelease) {
pIsOpen = !pIsOpen;
}
pLayout->Pos = pLayout->Pos + (pIO->InputHandler->DragPosition -
pIO->InputHandler->DragLastPosition);
// Have no ViewPort Yet :(
// pLayout->Pos = std::clamp(pLayout->Pos, fvec2(10), fvec2(1270, 710));
}
}
}
PD_UI7_API void ReMenu::DrawBaseLayout() {
if (pIsOpen) {
Container::Ref r = PD::New<UI7::DynObj>(
[](UI7::IO::Ref io, UI7::DrawList::Ref l, UI7::Container* self) {
l->Layer = 0;
l->AddRectangle(self->FinalPos(), self->GetSize(),
io->Theme->Get(UI7Color_Background));
});
// Set size before pushing (cause Cursor Move will require it)
r->SetSize(
fvec2(pLayout->GetSize().x, pLayout->GetSize().y - TitleBarHeight));
r->SetPos(fvec2(0, TitleBarHeight));
pLayout->AddObjectEx(r, UI7LytAdd_NoCursorUpdate |
UI7LytAdd_NoScrollHandle | UI7LytAdd_Front);
}
if (!(Flags & UI7MenuFlags_NoTitlebar)) {
Container::Ref r = PD::New<UI7::DynObj>(
[=, this](UI7::IO::Ref io, UI7::DrawList::Ref l, UI7::Container* self) {
l->Layer = 20;
/** Header Bar */
l->AddRectangle(self->FinalPos(), self->GetSize(),
io->Theme->Get(UI7Color_Header));
l->Layer = 21;
/** Inline if statement to shift the Text if collapse sym is shown */
/** What the hell is this code btw (didn't found a better way) */
l->AddText(self->FinalPos() +
fvec2(Flags & UI7MenuFlags_NoClose
? 0
: (TitleBarHeight - pIO->FramePadding.y * 2 +
(io->FramePadding.x * 2)),
0),
pID.GetName(), io->Theme->Get(UI7Color_Text));
});
r->SetSize(fvec2(pLayout->GetSize().x, TitleBarHeight));
r->SetPos(0);
pLayout->AddObjectEx(r,
UI7LytAdd_NoCursorUpdate | UI7LytAdd_NoScrollHandle);
/** Collapse Sym */
if (!(Flags & UI7MenuFlags_NoCollapse)) {
r = PD::New<UI7::DynObj>([=, this](UI7::IO::Ref io, UI7::DrawList::Ref l,
UI7::Container* self) {
/** This sym actually requires layer 21 (i dont know why) */
l->Layer = 21;
/**
* Symbol (Position Swapping set by pIsOpen ? openpos : closepos;)
*/
l->AddTriangleFilled(
self->FinalPos(),
self->FinalPos() +
fvec2(self->GetSize().x, pIsOpen ? 0 : self->GetSize().y * 0.5),
self->FinalPos() +
fvec2(pIsOpen ? self->GetSize().x * 0.5 : 0, self->GetSize().y),
io->Theme->Get(UI7Color_FrameBackground));
});
r->SetSize(TitleBarHeight - pIO->FramePadding.y * 2);
r->SetPos(pIO->FramePadding);
pLayout->AddObjectEx(r,
UI7LytAdd_NoCursorUpdate | UI7LytAdd_NoScrollHandle);
}
/** Close Sym (only shown if pIsShown is not nullptr) */
if (!(Flags & UI7MenuFlags_NoClose) && pIsShown) {
fvec2 size = TitleBarHeight - pIO->FramePadding.y * 2; // Fixed quad size
// Need to clamp this way as the math lib lacks a less and greater
// operator in vec2 (don't checked if it would make sense yet)
size.x = std::clamp(size.x, 5.f, std::numeric_limits<float>::max());
size.y = std::clamp(size.y, 5.f, std::numeric_limits<float>::max());
// Probably should fix the minsize to be locked on y
fvec2 cpos =
fvec2(pLayout->Pos.x + pLayout->Size.x - size.x - pIO->FramePadding.x,
pLayout->Pos.y + pIO->FramePadding.y);
pLayout->DrawList->AddLine(cpos, cpos + size,
pIO->Theme->Get(UI7Color_FrameBackground), 2);
pLayout->DrawList->AddLine(cpos + fvec2(0, size.y),
cpos + fvec2(size.x, 0),
pIO->Theme->Get(UI7Color_FrameBackground), 2);
}
}
}
PD_UI7_API void ReMenu::Update() {
HandleFocus();
if (!(Flags & UI7MenuFlags_NoTitlebar)) {
HandleTitlebarActions();
}
DrawBaseLayout();
pLayout->Update();
}
} // namespace UI7
} // namespace PD

View File

@ -1,69 +1,69 @@
/*
MIT License
Copyright (c) 2024 - 2025 René Amthor (tobid7)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <pd/core/color.hpp>
#include <pd/ui7/theme.hpp>
namespace PD {
namespace UI7 {
void Theme::Default(Theme& theme) {
theme.Set(UI7Color_Text, Color("#FFFFFFFF"));
theme.Set(UI7Color_TextDead, Color("#AAAAAAFF"));
theme.Set(UI7Color_Background, Color("#222222ff"));
theme.Set(UI7Color_Border, Color("#999999ff"));
theme.Set(UI7Color_Button, Color("#111111FF"));
theme.Set(UI7Color_ButtonDead, Color("#080808FF"));
theme.Set(UI7Color_ButtonActive, Color("#2A2A2AFF"));
theme.Set(UI7Color_ButtonHovered, Color("#222222FF"));
theme.Set(UI7Color_Header, Color("#111111FF"));
theme.Set(UI7Color_HeaderDead, Color("#080808FF"));
theme.Set(UI7Color_Selector, Color("#222222FF"));
theme.Set(UI7Color_Checkmark, Color("#2A2A2AFF"));
theme.Set(UI7Color_FrameBackground, Color("#555555FF"));
theme.Set(UI7Color_FrameBackgroundHovered, Color("#777777FF"));
theme.Set(UI7Color_Progressbar, Color("#00FF00FF"));
theme.Set(UI7Color_ListEven, Color("#CCCCCCFF"));
theme.Set(UI7Color_ListOdd, Color("#BBBBBBFF"));
}
void Theme::Flashbang(Theme& theme) {
theme.Set(UI7Color_Text, Color("#000000FF"));
theme.Set(UI7Color_TextDead, Color("#333333FF"));
theme.Set(UI7Color_Background, Color("#eeeeeeFF"));
theme.Set(UI7Color_Border, Color("#777777ff"));
theme.Set(UI7Color_Button, Color("#ccccccFF"));
theme.Set(UI7Color_ButtonDead, Color("#bbbbbbFF"));
theme.Set(UI7Color_ButtonActive, Color("#ccccccFF"));
theme.Set(UI7Color_ButtonHovered, Color("#acacacFF"));
theme.Set(UI7Color_Header, Color("#ddddddFF"));
theme.Set(UI7Color_HeaderDead, Color("#cdcdcdFF"));
theme.Set(UI7Color_Selector, Color("#222222FF"));
theme.Set(UI7Color_Checkmark, Color("#ccccccFF"));
theme.Set(UI7Color_FrameBackground, Color("#aaaaaaFF"));
theme.Set(UI7Color_FrameBackgroundHovered, Color("#909090FF"));
theme.Set(UI7Color_Progressbar, Color("#00FF00FF"));
theme.Set(UI7Color_ListEven, Color("#CCCCCCFF"));
theme.Set(UI7Color_ListOdd, Color("#BBBBBBFF"));
}
} // namespace UI7
/*
MIT License
Copyright (c) 2024 - 2025 René Amthor (tobid7)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <pd/core/core.hpp>
#include <pd/ui7/theme.hpp>
namespace PD {
namespace UI7 {
PD_UI7_API void Theme::Default(Theme& theme) {
theme.Set(UI7Color_Text, Color("#FFFFFFFF"));
theme.Set(UI7Color_TextDead, Color("#AAAAAAFF"));
theme.Set(UI7Color_Background, Color("#222222ff"));
theme.Set(UI7Color_Border, Color("#999999ff"));
theme.Set(UI7Color_Button, Color("#111111FF"));
theme.Set(UI7Color_ButtonDead, Color("#080808FF"));
theme.Set(UI7Color_ButtonActive, Color("#2A2A2AFF"));
theme.Set(UI7Color_ButtonHovered, Color("#222222FF"));
theme.Set(UI7Color_Header, Color("#111111FF"));
theme.Set(UI7Color_HeaderDead, Color("#080808FF"));
theme.Set(UI7Color_Selector, Color("#222222FF"));
theme.Set(UI7Color_Checkmark, Color("#2A2A2AFF"));
theme.Set(UI7Color_FrameBackground, Color("#555555FF"));
theme.Set(UI7Color_FrameBackgroundHovered, Color("#777777FF"));
theme.Set(UI7Color_Progressbar, Color("#00FF00FF"));
theme.Set(UI7Color_ListEven, Color("#CCCCCCFF"));
theme.Set(UI7Color_ListOdd, Color("#BBBBBBFF"));
}
PD_UI7_API void Theme::Flashbang(Theme& theme) {
theme.Set(UI7Color_Text, Color("#000000FF"));
theme.Set(UI7Color_TextDead, Color("#333333FF"));
theme.Set(UI7Color_Background, Color("#eeeeeeFF"));
theme.Set(UI7Color_Border, Color("#777777ff"));
theme.Set(UI7Color_Button, Color("#ccccccFF"));
theme.Set(UI7Color_ButtonDead, Color("#bbbbbbFF"));
theme.Set(UI7Color_ButtonActive, Color("#ccccccFF"));
theme.Set(UI7Color_ButtonHovered, Color("#acacacFF"));
theme.Set(UI7Color_Header, Color("#ddddddFF"));
theme.Set(UI7Color_HeaderDead, Color("#cdcdcdFF"));
theme.Set(UI7Color_Selector, Color("#222222FF"));
theme.Set(UI7Color_Checkmark, Color("#ccccccFF"));
theme.Set(UI7Color_FrameBackground, Color("#aaaaaaFF"));
theme.Set(UI7Color_FrameBackgroundHovered, Color("#909090FF"));
theme.Set(UI7Color_Progressbar, Color("#00FF00FF"));
theme.Set(UI7Color_ListEven, Color("#CCCCCCFF"));
theme.Set(UI7Color_ListOdd, Color("#BBBBBBFF"));
}
} // namespace UI7
} // namespace PD

View File

@ -1,300 +1,367 @@
/*
MIT License
Copyright (c) 2024 - 2025 René Amthor (tobid7)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <pd/core/timetrace.hpp>
#include <pd/ui7/ui7.hpp>
// Helpers
#define UI7DV4(x) \
std::format("{}: [{:.2f}, {:.2f}, {:.2f}, {:.2f}]", #x, x[0], x[1], x[2], \
x[3])
#define UI7DV4N(x) \
std::format("[{:.2f}, {:.2f}, {:.2f}, {:.2f}]", x[0], x[1], x[2], x[3])
#define UI7DV2(x) std::format("{}: [{:.2f}, {:.2f}]", #x, x[0], x[1])
#define UI7DV2N(x) std::format("[{:.2f}, {:.2f}]", x[0], x[1])
#define UI7DHX32(x) std::format("{}: {:#08x}", #x, x)
#define UI7DTF(x) PD::Strings::FormatNanos(x)
namespace PD {
std::string UI7::GetVersion(bool show_build) {
std::stringstream s;
s << ((UI7_VERSION >> 24) & 0xFF) << ".";
s << ((UI7_VERSION >> 16) & 0xFF) << ".";
s << ((UI7_VERSION >> 8) & 0xFF);
if (show_build) s << "-" << ((UI7_VERSION) & 0xFF);
return s.str();
}
bool UI7::Context::BeginMenu(const ID& id, UI7MenuFlags flags, bool* show) {
Assert(!this->current, "You are already in another Menu!");
Assert(std::find(amenus.begin(), amenus.end(), (u32)id) == amenus.end(),
"Menu Name Already used or\nContext::Update not called!");
if (show != nullptr) {
if (!(*show)) {
if (io->FocusedMenu == id) {
io->FocusedMenu = 0;
io->FocusedMenuRect = 0;
}
return false;
}
}
auto menu = this->menus.find(id);
if (menu == this->menus.end()) {
this->menus[id] = Menu::New(id, io);
this->menus[id]->Layout->SetSize(io->Ren->GetViewport().zw());
menu = this->menus.find(id);
}
this->current = menu->second;
this->current->is_shown = show;
this->io->CurrentMenu = this->current->id;
io->RegisterDrawList(id, this->current->Layout->GetDrawList());
this->current->PreHandler(flags);
amenus.push_back(this->current->GetID());
if (!this->current->is_open) {
this->current = nullptr;
}
return this->current != nullptr;
}
UI7::Menu::Ref UI7::Context::GetCurrentMenu() {
Assert(current != nullptr, "Not in a Menu!");
return current;
}
UI7::Menu::Ref UI7::Context::FindMenu(const ID& id) {
auto e = this->menus.find(id);
if (e != this->menus.end()) {
return e->second;
}
return nullptr;
}
void UI7::Context::EndMenu() {
this->current->PostHandler();
this->current = nullptr;
this->io->CurrentMenu = 0;
}
void UI7::Context::Update(float) {
TT::Scope st("UI7_Update");
Assert(current == nullptr, "Still in a Menu!");
bool focused_exist = false;
for (auto it : amenus) {
auto m = menus[it];
io->CurrentMenu = m->id;
m->Update(io->Delta);
io->CurrentMenu = 0;
if (it == io->FocusedMenu) {
focused_exist = true;
}
}
if (!focused_exist) {
io->FocusedMenu = 0;
io->FocusedMenuRect = 0;
}
int list = 0;
u32 vtx_counter = 0;
u32 idx_counter = 0;
// Render the Focused Menu Last
std::sort(io->DrawListRegestry.begin(), io->DrawListRegestry.end(),
[&](const auto& a, const auto& b) {
return (a.first == io->FocusedMenu) <
(b.first == io->FocusedMenu);
});
// Register Front List as last element
io->RegisterDrawList("CtxFrontList", io->Front);
for (auto it : io->DrawListRegestry) {
it.second->BaseLayer(list * 30);
it.second->Process();
vtx_counter += it.second->num_vertices;
idx_counter += it.second->num_indices;
list++;
}
io->NumIndices = idx_counter;
io->NumVertices = vtx_counter;
this->amenus.clear();
this->io->Update();
}
void UI7::Context::AboutMenu(bool* show) {
if (this->BeginMenu("About UI7", UI7MenuFlags_Scrolling, show)) {
auto m = this->GetCurrentMenu();
m->Label("Palladium - UI7 " + GetVersion());
m->Separator();
m->Label("(c) 2023-2025 René Amthor");
m->Label("UI7 is licensed under the MIT License.");
m->Label("See LICENSE for more information.");
static bool show_build;
m->Checkbox("Show Build Info", show_build);
if (show_build) {
m->SeparatorText("Build Info");
m->Label("Full Version -> " + GetVersion(true));
m->Label("sizeof(size_t) -> " + std::to_string(sizeof(size_t)));
m->Label("sizeof(LI::Vertex) -> " + std::to_string(sizeof(LI::Vertex)));
m->Label("__cplusplus -> " + std::to_string(__cplusplus));
m->Label("Compiler -> " + LibInfo::CompiledWith());
}
this->EndMenu();
}
}
void UI7::Context::MetricsMenu(bool* show) {
if (this->BeginMenu("UI7 Metrics", UI7MenuFlags_Scrolling, show)) {
auto m = this->GetCurrentMenu();
m->Label("Palladium - UI7 " + GetVersion());
m->Separator();
m->Label(
std::format("Average {:.3f} ms/f ({:.1f} FPS)",
((float)io->DeltaStats->GetAverage() / 1000.f),
1000.f / ((float)io->DeltaStats->GetAverage() / 1000.f)));
m->Label(std::format("NumVertices: {}", io->NumVertices));
m->Label(std::format("NumIndices: {} -> {} Tris", io->NumIndices,
io->NumIndices / 3));
m->Label("Menus: " + std::to_string(menus.size()));
if (m->BeginTreeNode("Font")) {
for (u32 i = 0; i <= 0x00ff; i++) {
auto& c = io->Ren->Font()->GetCodepoint(i);
if (!c.invalid()) {
m->Image(c.tex(), c.size(), c.uv());
if ((i % 15) != 0 || i == 0) {
m->SameLine();
}
}
}
m->EndTreeNode();
}
m->SeparatorText("TimeTrace");
if (m->BeginTreeNode("Traces (" +
std::to_string(Sys::GetTraceMap().size()) + ")")) {
for (auto& it : Sys::GetTraceMap()) {
if (m->BeginTreeNode(it.second->GetID())) {
m->Label("Diff: " + UI7DTF(it.second->GetLastDiff()));
m->Label("Protocol Len: " +
std::to_string(it.second->GetProtocol()->GetLen()));
m->Label("Average: " +
UI7DTF(it.second->GetProtocol()->GetAverage()));
m->Label("Min: " + UI7DTF(it.second->GetProtocol()->GetMin()));
m->Label("Max: " + UI7DTF(it.second->GetProtocol()->GetMax()));
m->EndTreeNode();
}
}
m->EndTreeNode();
}
m->SeparatorText("IO");
if (m->BeginTreeNode("Menus (" + std::to_string(menus.size()) + ")")) {
for (auto& it : menus) {
if (m->BeginTreeNode(it.second->name)) {
m->Label("Name: " + it.second->name);
m->Label("Pos: " + UI7DV2N(it.second->Layout->GetPosition()));
m->Label("Size: " + UI7DV2N(it.second->Layout->GetSize()));
m->Label("Work Rect: " + UI7DV4N(it.second->Layout->WorkRect));
m->Label("Cursor: " + UI7DV2N(it.second->Layout->Cursor));
if (m->BeginTreeNode(
"ID Objects (" +
std::to_string(it.second->Layout->IDObjects.size()) + ")")) {
for (auto& jt : it.second->Layout->IDObjects) {
m->Label(UI7DHX32(jt->GetID()));
}
m->EndTreeNode();
}
m->EndTreeNode();
}
}
m->EndTreeNode();
}
if (m->BeginTreeNode("DrawLists (" +
std::to_string(io->DrawListRegestry.size()) + ")")) {
for (auto& it : io->DrawListRegestry) {
if (m->BeginTreeNode(it.first.GetName())) {
m->Label("Vertices: " + std::to_string(it.second->num_vertices));
m->Label("Indices: " + std::to_string(it.second->num_indices));
m->Label("Base Layer: " + std::to_string(it.second->base));
m->EndTreeNode();
}
}
m->EndTreeNode();
}
m->Label("io->Time: " + Strings::FormatMillis(io->Time->Get()));
m->Label(std::format("io->Delta: {:.3f}", io->Delta));
m->Label(std::format("io->Framerate: {:.2f}", io->Framerate));
m->Label(UI7DHX32(io->FocusedMenu));
m->Label(UI7DHX32(io->DraggedObject));
m->Label(std::format("io->DragTime: {:.2f}s", io->DragTime->GetSeconds()));
m->Label(UI7DV4(io->DragDestination));
m->Label(UI7DV2(io->DragSourcePos));
m->Label(UI7DV2(io->DragPosition));
m->Label(UI7DV2(io->DragLastPosition));
this->EndMenu();
}
}
void UI7::Context::StyleEditor(bool* show) {
if (this->BeginMenu("UI7 Style Editor", UI7MenuFlags_Scrolling, show)) {
auto m = this->GetCurrentMenu();
m->Label("Palladium - UI7 " + GetVersion() + " Style Editor");
m->Separator();
m->DragData("MenuPadding", (float*)&io->MenuPadding, 2, 0.f, 100.f);
m->DragData("FramePadding", (float*)&io->FramePadding, 2, 0.f, 100.f);
m->DragData("ItemSpace", (float*)&io->ItemSpace, 2, 0.f, 100.f);
m->DragData("MinSliderSize", (float*)&io->MinSliderDragSize, 2, 1.f, 100.f);
m->DragData("OverScroll Modifier", &io->OverScrollMod, 1, 0.01f,
std::numeric_limits<float>::max(), 0.01f, 2);
m->Checkbox("Menu Border", io->ShowMenuBorder);
m->Checkbox("Frame Border", io->ShowFrameBorder);
m->SeparatorText("Theme");
if (m->Button("Dark")) {
UI7::Theme::Default(*io->Theme.get());
}
m->SameLine();
if (m->Button("Flashbang")) {
UI7::Theme::Flashbang(*io->Theme.get());
}
/// Small trick to print without prefix
#define ts(x) m->ColorEdit(std::string(#x).substr(9), &io->Theme->GetRef(x));
#define ts2(x) \
m->DragData(std::string(#x).substr(9), (u8*)&io->Theme->GetRef(x), 4, (u8)0, \
(u8)255);
ts2(UI7Color_Background);
ts2(UI7Color_Border);
ts2(UI7Color_Button);
ts2(UI7Color_ButtonDead);
ts2(UI7Color_ButtonActive);
ts2(UI7Color_ButtonHovered);
ts2(UI7Color_Text);
ts2(UI7Color_TextDead);
ts2(UI7Color_Header);
ts2(UI7Color_HeaderDead);
ts2(UI7Color_Selector);
ts2(UI7Color_Checkmark);
ts2(UI7Color_FrameBackground);
ts2(UI7Color_FrameBackgroundHovered);
ts2(UI7Color_Progressbar);
ts2(UI7Color_ListEven);
ts2(UI7Color_ListOdd);
this->EndMenu();
}
}
/*
MIT License
Copyright (c) 2024 - 2025 René Amthor (tobid7)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <pd/core/core.hpp>
#include <pd/ui7/ui7.hpp>
// Helpers
std::string _UI7DV4(PD::vec4<float> v) {
return std::format("[{:.2f}, {:.2f}, {:.2f}, {:.2f}]", v.x, v.y, v.z, v.w);
}
std::string _UI7DV2(PD::vec2<float> v) {
return std::format("[{:.2f}, {:.2f}]", v.x, v.y);
}
#define UI7DV4(x) #x ": " + _UI7DV4(x)
#define UI7DV4N(x) _UI7DV4(x)
#define UI7DV2(x) #x ": " + _UI7DV2(x)
#define UI7DV2N(x) _UI7DV2(x)
#define UI7DHX32(x) std::format("{}: {:#08x}", #x, x)
#define UI7DTF(x) PD::Strings::FormatNanos(x)
namespace PD {
PD_UI7_API std::string UI7::GetVersion(bool show_build) {
std::stringstream s;
s << ((UI7_VERSION >> 24) & 0xFF) << ".";
s << ((UI7_VERSION >> 16) & 0xFF) << ".";
s << ((UI7_VERSION >> 8) & 0xFF);
if (show_build) s << "-" << ((UI7_VERSION) & 0xFF);
return s.str();
}
PD_UI7_API bool UI7::Context::BeginMenu(const ID& id, UI7MenuFlags flags,
bool* show) {
// Assert(!this->current, "You are already in another Menu!");
// Assert(std::find(amenus.begin(), amenus.end(), (u32)id) == amenus.end(),
// "Menu Name Already used or\nContext::Update not called!");
if (show != nullptr) {
if (!(*show)) {
if (io->InputHandler->FocusedMenu == id) {
io->InputHandler->FocusedMenu = 0;
io->InputHandler->FocusedMenuRect = 0;
}
return false;
}
}
auto menu = this->menus.find(id);
if (menu == this->menus.end()) {
this->menus[id] = Menu::New(id, io);
// this->menus[id]->Layout->SetSize(io->Ren->GetViewport().zw());
menu = this->menus.find(id);
}
this->current = menu->second;
this->current->is_shown = show;
this->io->InputHandler->CurrentMenu = this->current->id;
io->RegisterDrawList(id, this->current->Layout->GetDrawList());
this->current->PreHandler(flags);
amenus.push_back(this->current->GetID());
if (!this->current->is_open) {
this->current = nullptr;
}
return this->current != nullptr;
}
PD_UI7_API UI7::Menu::Ref UI7::Context::GetCurrentMenu() {
// Assert(current != nullptr, "Not in a Menu!");
return current;
}
PD_UI7_API UI7::Menu::Ref UI7::Context::FindMenu(const ID& id) {
auto e = this->menus.find(id);
if (e != this->menus.end()) {
return e->second;
}
return nullptr;
}
PD_UI7_API void UI7::Context::EndMenu() {
this->current->PostHandler();
this->current = nullptr;
this->io->InputHandler->CurrentMenu = 0;
}
PD_UI7_API bool UI7::Context::DoMenuEx(
const UI7::ID& id, UI7MenuFlags flags,
std::function<void(UI7::ReMenu::Ref m)> f) {
if (!Current) {
Current = ReMenu::New(id, io);
}
// Current->pIsShown = show;
io->InputHandler->CurrentMenu = Current->pID;
io->RegisterDrawList(id, Current->pLayout->GetDrawList());
if (Current->pIsOpen) {
f(Current);
}
return Current != nullptr;
}
PD_UI7_API void UI7::Context::Update(float) {
TT::Scope st("UI7_Update");
// Assert(current == nullptr, "Still in a Menu!");
if (!io->InputHandler->FocusedMenu && amenus.size() > 0) {
io->InputHandler->FocusedMenu = amenus[amenus.size() - 1];
}
bool focused_exist = false;
if (aml.size() == 0) {
aml = amenus;
} else {
std::vector<size_t> tbr;
for (size_t i = 0; i < aml.size(); i++) {
if (std::find(amenus.begin(), amenus.end(), aml[i]) == amenus.end()) {
tbr.push_back(i);
}
}
for (auto& it : tbr) {
aml.erase(aml.begin() + it);
}
}
for (auto& it : amenus) {
if (std::find(aml.begin(), aml.end(), it) == aml.end()) {
aml.push_back(it);
}
}
auto ptf = std::find(aml.begin(), aml.end(), io->InputHandler->FocusedMenu);
if (ptf != aml.end() && ptf != aml.begin()) {
std::rotate(aml.begin(), ptf, ptf + 1);
}
for (auto it : aml) {
auto m = menus[it];
io->InputHandler->CurrentMenu = m->id;
m->Update(io->Delta);
io->InputHandler->CurrentMenu = 0;
if (it == io->InputHandler->FocusedMenu) {
focused_exist = true;
}
}
io->InputHandler->CurrentMenu = Current->pID;
Current->Update();
/*if (!focused_exist && io->CurrentMenu != Current->pID) {
io->FocusedMenu = 0;
io->FocusedMenuRect = 0;
}*/
int list = 0;
u32 vtx_counter = 0;
u32 idx_counter = 0;
// Register Front List as last element
io->RegisterDrawList("CtxFrontList", io->Front);
// io->DrawListRegestry.Reverse();
for (auto it : io->DrawListRegestry) {
it.Second->Base = list * 30;
it.Second->Process(io->pRDL);
vtx_counter += it.Second->NumVertices;
idx_counter += it.Second->NumIndices;
list++;
}
io->Ren->RegisterDrawList(io->pRDL);
io->NumIndices = idx_counter;
io->NumVertices = vtx_counter;
this->amenus.clear();
this->io->Update();
}
PD_UI7_API void UI7::Context::AboutMenu(bool* show) {
if (this->BeginMenu("About UI7", UI7MenuFlags_Scrolling, show)) {
auto m = this->GetCurrentMenu();
m->Label("Palladium - UI7 " + GetVersion());
m->Separator();
m->Label("(c) 2023-2025 René Amthor");
m->Label("UI7 is licensed under the MIT License.");
m->Label("See LICENSE for more information.");
static bool show_build;
m->Checkbox("Show Build Info", show_build);
if (show_build) {
m->SeparatorText("Build Info");
m->Label("Full Version -> " + GetVersion(true));
m->Label("sizeof(size_t) -> " + std::to_string(sizeof(size_t)));
m->Label("sizeof(LI::Vertex) -> " + std::to_string(sizeof(LI::Vertex)));
m->Label("__cplusplus -> " + std::to_string(__cplusplus));
m->Label("Compiler -> " + LibInfo::CompiledWith());
}
this->EndMenu();
}
}
PD_UI7_API void UI7::Context::MetricsMenu(bool* show) {
if (this->BeginMenu("UI7 Metrics", UI7MenuFlags_Scrolling, show)) {
auto m = this->GetCurrentMenu();
m->Label("Palladium - UI7 " + GetVersion());
m->Separator();
m->Label(
std::format("Average {:.3f} ms/f ({:.1f} FPS)",
((float)io->DeltaStats->GetAverage() / 1000.f),
1000.f / ((float)io->DeltaStats->GetAverage() / 1000.f)));
m->Label(std::format("NumVertices: {}", io->NumVertices));
m->Label(std::format("NumIndices: {} -> {} Tris", io->NumIndices,
io->NumIndices / 3));
m->Label("Menus: " + std::to_string(menus.size()));
/*if (m->BeginTreeNode("Font")) {
for (u32 i = 0; i <= 0x00ff; i++) {
auto& c = io->Ren->Font()->GetCodepoint(i);
if (!c.invalid()) {
m->Image(c.tex(), c.size(), c.uv());
if ((i % 15) != 0 || i == 0) {
m->SameLine();
}
}
}
m->EndTreeNode();
}*/
m->SeparatorText("TimeTrace");
if (m->BeginTreeNode("Traces (" +
std::to_string(Sys::GetTraceMap().size()) + ")")) {
for (auto& it : Sys::GetTraceMap()) {
if (m->BeginTreeNode(it.second->GetID())) {
m->Label("Diff: " + UI7DTF(it.second->GetLastDiff()));
m->Label("Protocol Len: " +
std::to_string(it.second->GetProtocol()->GetLen()));
m->Label("Average: " +
UI7DTF(it.second->GetProtocol()->GetAverage()));
m->Label("Min: " + UI7DTF(it.second->GetProtocol()->GetMin()));
m->Label("Max: " + UI7DTF(it.second->GetProtocol()->GetMax()));
m->EndTreeNode();
}
}
m->EndTreeNode();
}
m->SeparatorText("IO");
if (m->BeginTreeNode("Menus (" + std::to_string(menus.size()) + ")")) {
for (auto& it : menus) {
if (m->BeginTreeNode(it.second->name)) {
m->Label("Name: " + it.second->name);
/*m->Label("Pos: " + UI7DV2N(it.second->Layout->GetPosition()));
m->Label("Size: " + UI7DV2N(it.second->Layout->GetSize()));
m->Label("Work Rect: " + UI7DV4N(it.second->Layout->WorkRect));
m->Label("Cursor: " + UI7DV2N(it.second->Layout->Cursor));*/
if (m->BeginTreeNode(
"ID Objects (" +
std::to_string(it.second->Layout->IDObjects.size()) + ")")) {
for (auto& jt : it.second->Layout->IDObjects) {
m->Label(UI7DHX32(jt->GetID()));
}
m->EndTreeNode();
}
m->EndTreeNode();
}
}
m->EndTreeNode();
}
if (m->BeginTreeNode("Active Menus (" + std::to_string(aml.size()) + ")")) {
for (auto& it : aml) {
if (m->BeginTreeNode(menus[it]->name)) {
m->Label("Name: " + menus[it]->name);
/*m->Label("Pos: " + UI7DV2N(it.second->Layout->Pos));
m->Label("Size: " + UI7DV2N(it.second->Layout->GetSize()));
m->Label("Work Rect: " + UI7DV4N(it.second->Layout->WorkRect));
m->Label("Cursor: " + UI7DV2N(it.second->Layout->Cursor));*/
if (m->BeginTreeNode(
"ID Objects (" +
std::to_string(menus[it]->Layout->IDObjects.size()) + ")")) {
for (auto& jt : menus[it]->Layout->IDObjects) {
m->Label(UI7DHX32(jt->GetID()));
}
m->EndTreeNode();
}
m->EndTreeNode();
}
}
m->EndTreeNode();
}
if (m->BeginTreeNode("DrawLists (" +
std::to_string(io->DrawListRegestry.Size()) + ")")) {
for (auto& it : io->DrawListRegestry) {
if (m->BeginTreeNode(it.First.GetName())) {
m->Label("Vertices: " + std::to_string(it.Second->NumVertices));
m->Label("Indices: " + std::to_string(it.Second->NumIndices));
m->Label("Base Layer: " + std::to_string(it.Second->Base));
m->EndTreeNode();
}
}
m->EndTreeNode();
}
m->Label("io->Time: " + Strings::FormatMillis(io->Time->Get()));
m->Label(std::format("io->Delta: {:.3f}", io->Delta));
m->Label(std::format("io->Framerate: {:.2f}", io->Framerate));
m->Label(UI7DHX32(io->InputHandler->FocusedMenu));
m->Label(UI7DHX32(io->InputHandler->DraggedObject));
m->Label(std::format("io->DragTime: {:.2f}s",
io->InputHandler->DragTime->GetSeconds()));
m->Label(UI7DV4(io->InputHandler->DragDestination));
m->Label(UI7DV2(io->InputHandler->DragSourcePos));
m->Label(UI7DV2(io->InputHandler->DragPosition));
m->Label(UI7DV2(io->InputHandler->DragLastPosition));
this->EndMenu();
}
}
PD_UI7_API void UI7::Context::StyleEditor(bool* show) {
if (this->BeginMenu("UI7 Style Editor", UI7MenuFlags_Scrolling, show)) {
auto m = this->GetCurrentMenu();
m->Label("Palladium - UI7 " + GetVersion() + " Style Editor");
m->Separator();
m->DragData("MenuPadding", (float*)&io->MenuPadding, 2, 0.f, 100.f);
m->DragData("FramePadding", (float*)&io->FramePadding, 2, 0.f, 100.f);
m->DragData("ItemSpace", (float*)&io->ItemSpace, 2, 0.f, 100.f);
m->DragData("MinSliderSize", (float*)&io->MinSliderDragSize, 2, 1.f, 100.f);
m->DragData("OverScroll Modifier", &io->OverScrollMod, 1, 0.01f,
std::numeric_limits<float>::max(), 0.01f, 2);
m->Checkbox("Menu Border", io->ShowMenuBorder);
m->Checkbox("Frame Border", io->ShowFrameBorder);
m->SeparatorText("Theme");
if (m->Button("Dark")) {
UI7::Theme::Default(*io->Theme.get());
}
m->SameLine();
if (m->Button("Flashbang")) {
UI7::Theme::Flashbang(*io->Theme.get());
}
/// Small trick to print without prefix
#define ts(x) m->ColorEdit(std::string(#x).substr(9), &io->Theme->GetRef(x));
#define ts2(x) \
m->DragData(std::string(#x).substr(9), (u8*)&io->Theme->GetRef(x), 4, (u8)0, \
(u8)255);
ts2(UI7Color_Background);
ts2(UI7Color_Border);
ts2(UI7Color_Button);
ts2(UI7Color_ButtonDead);
ts2(UI7Color_ButtonActive);
ts2(UI7Color_ButtonHovered);
ts2(UI7Color_Text);
ts2(UI7Color_TextDead);
ts2(UI7Color_Header);
ts2(UI7Color_HeaderDead);
ts2(UI7Color_Selector);
ts2(UI7Color_Checkmark);
ts2(UI7Color_FrameBackground);
ts2(UI7Color_FrameBackgroundHovered);
ts2(UI7Color_Progressbar);
ts2(UI7Color_ListEven);
ts2(UI7Color_ListOdd);
this->EndMenu();
}
}
} // namespace PD