# Rewrite Stage 1.5

- Added Overlays (Performance / Keyboaed)
- Keyboard has Gamepad Movement WIP (kinda)
- Work on UI7 Started
- Added Input Manager
- Added Message Boxes (Animated)
- Added Signle Header Tween func for animated stuff (Keyboard Messages, etc)
- Add FastHash (Maybe useful later)
- Using const & for vec in lithium
- Add ability to copy a command by a Ref
- Make Lists in Commands OpenAccess for Modification (StaticObject)
- Add Static Object (System to PreRender Suff that never changes) but can still be recolored or moved
- Add Layer and Font change functions
- Make Renderer Tools (RotateCorner, CreateRect, CreateLine, InBox, OptiCommandList) static (OpenAccess)
- Add ReIndexing to PushCommand
- Add Ability to Init vec3 and vec4 with vec2 and add .xy and .zw to vec4
- Fully Animated Keyboard that currently has problem of Top Down GamePad movement
- Add Func to Get GamePad Icon Codepoints for TextRenderer
- Made deltatime a float
- Using filesystem::path().wstring for convertation (works)
- Add a New InBox to Renderer that only checks if a point is inside a boundingbox
- Disable Font loading on Renderer Init due to 3ds Freezes when using SystemFont
- Make SystemFont lineheight 10% larger than it is to be nearly the same size as the ttf fonts
- Fix Some SpaceOffsets between TTF and SystemFont Rendering
- Cleanup the Update Rendermode Func
- Use LayerRenderSystem by default now as it now runs faster even with ttf fonts
This commit is contained in:
tobid7 2025-01-19 20:16:43 +01:00
parent d815bb5674
commit b4a4b6a426
35 changed files with 1919 additions and 131 deletions

View File

@ -32,6 +32,8 @@ set(SRC_FILES
source/common/sys.cpp
source/common/lang.cpp
source/common/error.cpp
# Controls
source/controls/hid.cpp
# Maths
source/maths/color.cpp
source/maths/bit_util.cpp
@ -40,6 +42,15 @@ set(SRC_FILES
source/graphics/texture.cpp
source/graphics/li7_shader.cpp
source/graphics/lithium.cpp
# Overlays
source/overlays/message_mgr.cpp
source/overlays/overlay_mgr.cpp
source/overlays/keyboard.cpp
source/overlays/performance.cpp
# Tools
source/tools/gamepad_icons.cpp
# UI7
source/ui7/drawlist.cpp
# External
source/external/stb.cpp
)
@ -61,7 +72,7 @@ target_compile_definitions(${TARGET_NAME} PUBLIC
)
add_executable(test test/main.cpp)
target_include_directories(test PUBLIC include)
target_include_directories(test PUBLIC include test)
target_link_directories(test PUBLIC ${CMAKE_BINARY_DIR})
target_link_libraries(test PUBLIC palladium citro3d ctru m)
# Generate 3DSX

View File

@ -1,5 +1,7 @@
# Palladium
**Framework / Engine to create Homebrew Apps / Games**
## Build types
```bash

View File

@ -38,5 +38,12 @@ SOFTWARE.
#include <pd/maths/color.hpp>
#include <pd/maths/img_convert.hpp>
#include <pd/maths/vec.hpp>
// Overlays
#include <pd/overlays/keyboard.hpp>
#include <pd/overlays/message_mgr.hpp>
#include <pd/overlays/overlay_mgr.hpp>
#include <pd/overlays/performance.hpp>
// UI7
#include <pd/ui7/ui7.hpp>
// namespace Palladium = PD;

View File

@ -25,7 +25,10 @@ SOFTWARE.
#include <pd/common/common.hpp>
#include <pd/common/timetrace.hpp>
#include <pd/controls/hid.hpp>
#include <pd/graphics/lithium.hpp>
#include <pd/overlays/message_mgr.hpp>
#include <pd/overlays/overlay_mgr.hpp>
namespace PD {
/// @brief Template Class for User Application
@ -43,7 +46,7 @@ class App : public SmartCtor<App> {
/// @param delta Deltatime
/// @param time App RunTime
/// @return false to exit the app
virtual bool MainLoop(u64 delta, float time) { return false; }
virtual bool MainLoop(float delta, float time) { return false; }
/// @brief Function to run the App
/// (int main() {
@ -52,15 +55,19 @@ class App : public SmartCtor<App> {
/// return 0;
/// })
void Run();
LI::Renderer::Ref Renderer() { return renderer; }
MessageMgr::Ref Messages() { return msg_mgr; }
OverlayMgr::Ref Overlays() { return overlay_mgr; }
Hid::Ref Input() { return input_mgr; }
float GetFps() const { return fps; }
private:
void PreInit();
void PostDeinit();
LI::Renderer::Ref renderer;
MessageMgr::Ref msg_mgr;
OverlayMgr::Ref overlay_mgr;
Hid::Ref input_mgr;
u64 last_time;
float app_time;
float fps;

View File

@ -29,6 +29,7 @@ SOFTWARE.
#include <filesystem> // Requires C++ 17 or later
#include <format> // Requires C++ 20 or later
#include <fstream>
#include <functional>
#include <iostream>
#include <map>
#include <memory>

View File

@ -38,5 +38,6 @@ const std::string GetFileName(const std::string& path,
const std::string PathRemoveExtension(const std::string& path);
template <typename T>
const std::string ToHex(const T& v);
u32 FastHash(const std::string& s);
} // namespace Strings
} // namespace PD

View File

@ -0,0 +1,95 @@
#pragma once
#include <pd/common/common.hpp>
#include <pd/maths/vec.hpp>
namespace PD {
class Hid : public SmartCtor<Hid> {
public:
enum Key : u32 {
No = 0,
A = 1 << 0,
B = 1 << 1,
X = 1 << 2,
Y = 1 << 3,
Start = 1 << 4,
Select = 1 << 5,
L = 1 << 6,
R = 1 << 7,
DUp = 1 << 8,
DDown = 1 << 9,
DLeft = 1 << 10,
DRight = 1 << 11,
CPUp = 1 << 12,
CPDown = 1 << 13,
CPLeft = 1 << 14,
CPRight = 1 << 15,
CSUp = 1 << 16,
CSDown = 1 << 17,
CSLeft = 1 << 18,
CSRight = 1 << 19,
ZL = 1 << 20,
ZR = 1 << 21,
Touch = 1 << 22,
Up = DUp | CPUp,
Down = DDown | CPDown,
Left = DLeft | CPLeft,
Right = DRight | CPRight,
};
enum Event {
Event_Down,
Event_Held,
Event_Up,
};
Hid();
~Hid() {}
vec2 TouchPos() const { return touch[0]; }
vec2 TouchPosLast() const { return touch[1]; }
bool IsEvent(Event e, Key keys);
bool IsDown(Key keys) const { return key_events[0].at(Event_Down) & keys; }
bool IsHeld(Key keys) const { return key_events[0].at(Event_Held) & keys; }
bool IsUp(Key keys) const { return key_events[0].at(Event_Up) & keys; }
void Clear() {
for (int i = 0; i < 2; i++) {
key_events[i][Event_Down] = 0;
key_events[i][Event_Up] = 0;
key_events[i][Event_Held] = 0;
}
}
void Lock(bool v) {
if (v != locked) {
SwappyTable();
}
locked = v;
}
bool Locked() const { return locked; }
void Lock() {
if (!locked) {
SwappyTable();
}
locked = true;
}
void Unlock() {
if (locked) {
SwappyTable();
}
locked = false;
}
/// @brief Get the New Keystates etc
/// @note WOW not using the deltatime
void Update();
private:
void SwappyTable();
/// Using 2 Touch positions for current and last frame
vec2 touch[2];
bool locked = false;
std::unordered_map<Event, u32> key_events[2];
std::unordered_map<u32, u32> binds;
};
} // namespace PD

View File

@ -49,11 +49,11 @@ namespace LI {
class Rect {
public:
Rect() = default;
Rect(vec4 t, vec4 b) {
Rect(const vec4& t, const vec4& b) {
top = t;
bot = b;
}
Rect(vec2 tl, vec2 tr, vec2 bl, vec2 br) {
Rect(const vec2& tl, const vec2& tr, const vec2& bl, const vec2& br) {
top = vec4(tl, tr);
bot = vec4(bl, br);
}
@ -84,7 +84,7 @@ class Font : public SmartCtor<Font> {
return *this;
}
vec4 uv() const { return m_uv; }
Codepoint& uv(vec4 v) {
Codepoint& uv(const vec4& v) {
m_uv = v;
return *this;
}
@ -94,7 +94,7 @@ class Font : public SmartCtor<Font> {
return *this;
}
vec2 size() const { return m_size; }
Codepoint& size(vec2 v) {
Codepoint& size(const vec2& v) {
m_size = v;
return *this;
}
@ -134,7 +134,7 @@ class Font : public SmartCtor<Font> {
class Vertex {
public:
Vertex() {}
Vertex(vec2 p, vec2 u, u32 c) {
Vertex(const vec2& p, const vec2& u, u32 c) {
pos[0] = p[0];
pos[1] = p[1];
pos[2] = 0.f;
@ -143,18 +143,18 @@ class Vertex {
}
~Vertex() {}
Vertex& Pos(vec3 v) {
Vertex& Pos(const vec3& v) {
pos = v;
return *this;
}
// Lets support that as well
Vertex& Pos(vec2 v) {
Vertex& Pos(const vec2& v) {
pos[0] = v[0];
pos[1] = v[1];
pos[2] = 0.f;
return *this;
}
Vertex& Uv(vec2 v) {
Vertex& Uv(const vec2& v) {
uv = v;
return *this;
}
@ -179,6 +179,15 @@ class Command : public SmartCtor<Command> {
Command() {}
~Command() {}
Command(Command::Ref v) {
this->index = v->index;
this->index_buf = v->index_buf;
this->layer = v->layer;
this->mode = v->mode;
this->tex = v->tex;
this->vertex_buf = v->vertex_buf;
}
Command& Layer(int v) {
layer = v;
return *this;
@ -200,7 +209,7 @@ class Command : public SmartCtor<Command> {
Texture::Ref Tex() const { return tex; }
Command& PushVertex(Vertex v) {
Command& PushVertex(const Vertex& v) {
vertex_buf.push_back(v);
return *this;
}
@ -208,6 +217,10 @@ class Command : public SmartCtor<Command> {
const std::vector<u16>& IndexList() const { return index_buf; }
const std::vector<Vertex>& VertexList() const { return vertex_buf; }
/// ADVANCED ///
std::vector<u16>& IndexList() { return index_buf; }
std::vector<Vertex>& VertexList() { return vertex_buf; }
Command& PushIndex(u16 v) {
index_buf.push_back(vertex_buf.size() + v);
return *this;
@ -255,11 +268,74 @@ class TextBox {
bool optional;
std::string text; // TextWrap
};
class StaticObject : public SmartCtor<StaticObject> {
public:
StaticObject() {}
~StaticObject() {}
void PushCommand(Command::Ref v) { cmds.push_back(v); }
void ReCopy() {
cpy.clear();
for (auto it : cmds) {
cpy.push_back(Command::New(it));
}
}
void ReColorQuad(int idx, u32 col) {
if (idx > (int)cpy.size()) {
return;
}
for (auto& it : cpy[idx]->VertexList()) {
it.Color(col);
}
}
void MoveIt(vec2 off) {
for (auto& it : cpy) {
for (auto& jt : it->VertexList()) {
jt.pos += vec3(off, 0);
}
}
}
void ReColor(u32 col) {
for (auto& it : cpy) {
for (auto& jt : it->VertexList()) {
jt.Color(col);
}
}
}
void ReLayer(int base_layer) {
for (auto& it : cpy) {
it->Layer(it->Layer() + base_layer);
}
}
void ReIndex(int start) {
for (int i = 0; i < (int)cpy.size(); i++) {
cpy[i]->Index(start + i);
}
}
std::vector<Command::Ref>& List() {
if (cpy.size() != 0) {
return cpy;
}
return cmds;
}
private:
std::vector<Command::Ref> cpy;
std::vector<Command::Ref> cmds;
};
using RenderFlags = u32;
enum RenderFlags_ {
RenderFlags_None = 0,
RenderFlags_TMS = 1 << 0, ///< Text Map System
RenderFlags_LRS = 1 << 1, ///< Layer Render System
RenderFlags_AST = 1 << 2, ///< Auto Static Text
RenderFlags_Default = RenderFlags_TMS,
};
class Renderer : public SmartCtor<Renderer> {
@ -291,6 +367,13 @@ class Renderer : public SmartCtor<Renderer> {
void TextScale(float v) { text_size = v; }
void DefaultTextScale() { text_size = default_text_size; }
float TextScale() const { return text_size; }
void Layer(int v) { current_layer = v; }
int Layer() const { return current_layer; }
void Font(Font::Ref v) {
font = v;
font_update = true;
}
Font::Ref Font() const { return font; }
void UseTex(Texture::Ref v = nullptr) {
if (v == nullptr) {
@ -305,42 +388,43 @@ class Renderer : public SmartCtor<Renderer> {
/// @param size Size
/// @param color Color
/// @param uv UV Map
void DrawRect(vec2 pos, vec2 size, u32 color,
vec4 uv = vec4(0.f, 1.f, 1.f, 0.f));
void DrawRect(const vec2& pos, const vec2& size, u32 color,
const vec4& uv = vec4(0.f, 1.f, 1.f, 0.f));
/// @brief Draw a Solid Rect (uses white tex)
/// @note acts as a simplified Draw rect Wrapper
/// @param pos Position
/// @param size Size
/// @param color Color
void DrawRectSolid(vec2 pos, vec2 size, u32 color);
void DrawRectSolid(const vec2& pos, const vec2& size, u32 color);
/// @brief Render a Triangle
/// @param a Position Alpha
/// @param b Position Bravo
/// @param c Position Delta
/// @param color Color
/// @note Defaults to Solif Color
void DrawTriangle(vec2 a, vec2 b, vec2 c, u32 color);
void DrawTriangle(const vec2& a, const vec2& b, const vec2& c, u32 color);
/// @brief Draw a Circle (Supports Textures)
/// @param center_pos Center Position
/// @param r Radius
/// @param color Color
/// @param segments Segments to use
/// @note Textures could look a bit janky due to uv mapping
void DrawCircle(vec2 center_pos, float r, u32 color, int segments);
void DrawCircle(const vec2& center_pos, float r, u32 color, int segments);
/// @brief Draw a Line between to Positions
/// @param a Position Alpha
/// @param b Position Bravo
/// @param color Color
/// @param t Thickness
void DrawLine(vec2 a, vec2 b, u32 color, int t);
void DrawText(vec2 pos, u32 color, const std::string& text, u32 flags = 0,
vec2 ap = vec2());
void DrawLine(const vec2& a, const vec2& b, u32 color, int t);
void DrawText(const vec2& pos, u32 color, const std::string& text,
u32 flags = 0, const vec2& ap = vec2());
/// @brief Draw a Texture as 2D Image
/// @param pos Position
/// @param tex Texture reference
/// @param scale Scale (cause maybe wants to be resized)
/// @note Acts as a Simplified wrapper to DrawRect
void DrawImage(vec2 pos, Texture::Ref tex, vec2 scale = vec2(1.f));
void DrawImage(const vec2& pos, Texture::Ref tex,
const vec2& scale = vec2(1.f));
/// Debug STUFF
u32 Vertices() const { return vertices; }
@ -349,22 +433,28 @@ class Renderer : public SmartCtor<Renderer> {
u32 DrawCalls() const { return drawcalls; }
/// TOOLS ///
void RotateCorner(vec2& v, float s, float c);
Rect CreateRect(vec2 pos, vec2 size, float angle);
Rect CreateLine(vec2 a, vec2 b, int t);
bool InBox(vec2 pos, vec2 size, vec4 rect);
bool InBox(vec2 alpha, vec2 bravo, vec2 charlie, vec4 rect);
void OptiCommandList(std::vector<Command::Ref>& list);
static void RotateCorner(vec2& v, float s, float c);
static Rect CreateRect(const vec2& pos, const vec2& size, float angle);
static Rect CreateLine(const vec2& a, const vec2& b, int t);
static bool InBox(const vec2& pos, const vec2& size, const vec4& rect);
static bool InBox(const vec2& pos, const vec4& rect);
static bool InBox(const vec2& alpha, const vec2& bravo, const vec2& charlie,
const vec4& rect);
static void OptiCommandList(std::vector<Command::Ref>& list);
/// @brief Returns Viewport with xy
vec4 GetViewport();
/// @brief Push a Self Created command
void PushCommand(Command::Ref cmd) { draw_list[bottom].push_back(cmd); }
void PushCommand(Command::Ref cmd) {
cmd->Index(cmd_idx++); // Indexing
draw_list[bottom].push_back(cmd);
}
/// @brief Automatically sets up a command
void SetupCommand(Command::Ref cmd);
/// @brief Creates a default Quad Render Command
void QuadCommand(Command::Ref cmd, const Rect& quad, vec4 uv, u32 col);
void QuadCommand(Command::Ref cmd, const Rect& quad, const vec4& uv, u32 col);
/// @brief Create a Default Triangle
void TriangleCommand(Command::Ref cmd, vec2 a, vec2 b, vec2 c, u32 col);
void TriangleCommand(Command::Ref cmd, const vec2& a, const vec2& b,
const vec2& c, u32 col);
/// @brief Create List of a Text Commands
/// @param cmds Link to a command List
/// @param pos Position
@ -373,8 +463,8 @@ class Renderer : public SmartCtor<Renderer> {
/// @param flags Flags
/// @param box (Size for wrapping / Offset for Centered Text)
/// @note Funktion macht noch faxxen (Text nicht sichtbar)
void TextCommand(std::vector<Command::Ref>& cmds, vec2 pos, u32 color,
const std::string& text, LITextFlags flags, vec2 box);
void TextCommand(std::vector<Command::Ref>& cmds, const vec2& pos, u32 color,
const std::string& text, LITextFlags flags, const vec2& box);
vec2 GetTextDimensions(const std::string& text);
private:

138
include/pd/maths/tween.hpp Normal file
View File

@ -0,0 +1,138 @@
#pragma once
#include <pd/maths/vec.hpp>
namespace PD {
template <typename T>
class Tween {
public:
enum Effect {
Linear,
EaseInQuad,
EaseOutQuad,
EaseInOutQuad,
EaseInCubic,
EaseOutCubic,
EaseInOutCubic,
EaseInSine,
EaseOutSine,
EaseInOutSine,
};
Tween() {}
~Tween() {}
void Update(float delta) {
time += delta / 1000.f;
if (time > tend) {
finished = true;
time = tend;
}
}
bool IsFinished() const { return finished; }
Tween& Finish() {
time = tend;
finished = true;
return *this;
}
Tween& From(const T& start) {
Reset();
this->start = start;
return *this;
}
Tween& To(const T& end) {
Reset();
this->end = end;
return *this;
}
Tween& In(float seconds) {
Reset();
tend = seconds;
return *this;
}
Tween& As(const Effect& e) {
effect = e;
return *this;
}
Tween& Reset() {
finished = false;
time = 0.f;
return *this;
}
/// @brief Probably usefull for fading colors by animation
/// Used to create this caus dont wanted to create a
/// fade effect fpr keyboard without having to Tween functions
/// @return
float Progress() const { return time / tend; }
Tween& Swap() {
T temp = start;
start = end;
end = temp;
return *this;
}
operator T() {
float t = 0.f;
switch (effect) {
case EaseInQuad:
t = time / tend;
return (end - start) * t * t + start;
break;
case EaseOutQuad:
t = time / tend;
return -(end - start) * t * (t - 2) + start;
break;
case EaseInOutQuad:
t = time / (tend / 2);
if (t < 1) return (end - start) / 2 * t * t + start;
t--;
return -(end - start) / 2 * (t * (t - 2) - 1) + start;
break;
case EaseInCubic:
t = time / tend;
return (end - start) * t * t * t + start;
break;
case EaseOutCubic:
t = time / tend;
t--;
return (end - start) * (t * t * t + 1) + start;
break;
// case EaseInOutCubic:
// t = time / (tend / 2);
// if (t < 1) return (end - start) / 2 * t * t * t + start;
// t--;
// return (end - start) / 2 * (t * t * t * 2) + start;
// break;
case EaseInSine:
return -(end - start) * cos(time / tend * (M_PI / 2)) + (end - start) +
start;
break;
case EaseOutSine:
return (end - start) * sin(time / tend * (M_PI / 2)) + start;
break;
case EaseInOutSine:
return -(end - start) / 2 * (cos(M_PI * time / tend) - 1) + start;
break;
default: // Linear
return (end - start) * (time / tend) + start;
break;
}
}
private:
Effect effect;
float time = 0.f;
// Defaulting to one to prevent div zero
// without a safetey check
// not implementing one cause if the user is
// Writing a In(0.f) its their fault
float tend = 1.f;
T start;
T end;
bool finished = false;
};
} // namespace PD

View File

@ -332,6 +332,20 @@ struct vec4 {
v[3] = i;
}
vec4(const vec3 &xyz, float w) {
v[0] = xyz[0];
v[1] = xyz[1];
v[2] = xyz[2];
v[3] = w;
}
vec4(float x, const vec3 &yzw) {
v[0] = x;
v[1] = yzw[1];
v[2] = yzw[2];
v[3] = yzw[3];
}
// Operators
// Add
vec4 &operator+=(const vec4 &i) {
@ -371,7 +385,7 @@ struct vec4 {
}
// Base
vec3 operator-() const { return vec3(-v[0], -v[1], -v[2]); }
vec4 operator-() const { return vec4(-v[0], -v[1], -v[2], -v[3]); }
float operator[](int i) const { return v[i]; }
float &operator[](int i) { return v[i]; }
@ -380,7 +394,7 @@ struct vec4 {
return v[0] * v[0] + v[1] * v[1] + v[2] * v[2] + v[3] * v[3];
}
// Vec2 Acess
// Vec Acess
float x() const { return v[0]; }
float &x() { return v[0]; }
float y() const { return v[1]; }
@ -389,6 +403,10 @@ struct vec4 {
float &z() { return v[2]; }
float w() const { return v[3]; }
float &w() { return v[3]; }
vec2 xy() const { return vec2(v[0], v[1]); }
vec2 zw() const { return vec2(v[2], v[3]); }
vec3 xyz() const { return vec3(v[0], v[1], v[2]); }
vec3 yzw() const { return vec3(v[1], v[2], v[3]); }
// Quaternion Acess
float r() const { return v[0]; }
float &r() { return v[0]; }
@ -398,6 +416,10 @@ struct vec4 {
float &j() { return v[2]; }
float i() const { return v[3]; }
float &i() { return v[3]; }
vec2 rk() const { return vec2(v[0], v[1]); }
vec2 ji() const { return vec2(v[2], v[3]); }
vec3 rkj() const { return vec3(v[0], v[1], v[2]); }
vec3 kji() const { return vec3(v[1], v[2], v[3]); }
// Internal Values
float v[4];
};

View File

@ -0,0 +1,105 @@
#pragma once
#include <pd/controls/hid.hpp>
#include <pd/maths/tween.hpp>
#include <pd/overlays/overlay.hpp>
namespace PD {
void DumpLayout(const std::string& path);
/// Keyboard class
/// @brief needs to be pushed with text and State reference as Overlay
/// to communicate with it
/// @note Hardcoded Rendering to get maximum Rendering Performance
class Keyboard : public Overlay {
public:
enum KeyOperation {
AppendSelf = 0,
Shift = 1,
Backspace = 2,
Enter = 3,
OpCancel = 4,
OpConfirm = 5,
Tab = 6,
Caps = 7,
Space = 8,
Op1 = 9,
Op2 = 10,
};
enum Type {
Default,
Numpad,
Password,
};
enum State {
None,
Cancel,
Confirm,
};
using Flags = u32;
enum Flags_ {
Flags_None = 0,
Flags_BlendTop = 1 << 0,
Flags_BlendBottom = 1 << 1,
Flags_LockControls = 1 << 2,
Flags_Default = Flags_BlendBottom | Flags_BlendTop | Flags_LockControls,
};
Keyboard(std::string& text, State& state, const std::string& hint = "",
Type type = Default, Flags flags = Flags_Default) {
too++;
if (too > 1) {
Kill();
return;
}
this->text = &text;
this->copy = text;
this->state = &state;
this->hint = hint;
this->type = type;
this->flags = flags;
this->raw_sel = -1;
flymgr.From(vec2(0, 240)).To(vec2(0, 115)).In(0.3f).As(flymgr.EaseInQuad);
chflymgr.From(vec2(-320, 0)).To(vec2(-320, 0)).In(0.1f).As(chflymgr.Linear);
}
~Keyboard() { too--; }
void Update(float delta, LI::Renderer::Ref ren, Hid::Ref inp) override;
void Rem() {
rem = true;
flymgr.From(vec2(0, 115)).To(vec2(0, 240)).In(0.2f).As(flymgr.EaseOutQuad);
}
private:
void LoadTheKeys(LI::Renderer::Ref ren);
void Movement(Hid::Ref inp);
void MoveSelector();
void DoOperation(KeyOperation op, const std::string& kname);
void RecolorBy(KeyOperation op, u32 color, int cm);
void InputOpBind(Hid::Key k, KeyOperation op, Hid::Ref inp, int cm);
std::string* text;
std::string copy;
std::string hint;
State* state;
Type type;
Flags flags;
int mode = 0; // def, caps, shift
// Stands for The Only One
static int too;
Tween<vec2> selector;
Tween<vec2> sel_szs;
vec2 cselszs;
int raw_sel;
// Performance Optimisation
LI::StaticObject::Ref keys[3];
bool keys_loadet = false;
// Some Animation
bool rem = false;
/// Probably a float would've done the job as well ;)
Tween<vec2> flymgr;
Tween<vec2> chflymgr;
bool show_help = true;
};
} // namespace PD

View File

@ -0,0 +1,45 @@
#pragma once
#include <pd/graphics/lithium.hpp>
#include <pd/maths/color.hpp>
#include <pd/maths/tween.hpp>
namespace PD {
class MessageMgr : public PD::SmartCtor<MessageMgr> {
public:
class Container : public PD::SmartCtor<Container> {
public:
Container(const std::string& title, const std::string& msg);
~Container() {}
void Render(PD::LI::Renderer::Ref ren);
void Update(int slot, float delta);
void FlyIn();
void ToBeMoved(int slot);
void ToBeRemoved();
bool ShouldBeRemoved() const { return (tbr && pos.IsFinished()) || kill; }
private:
PD::Color col_bg; // Background Color
PD::Color col_text; // Text Color
float lifetime = 0.f; // LifeTime
PD::Tween<vec2> pos; // Position effect
std::string title; // Title
std::string msg; // Message
vec2 size; // Size of the Background
bool tbr = false; // To be Removed ?
bool kill = false; // Instant Kill
int s = 0; // Slot
};
MessageMgr(PD::LI::Renderer::Ref r) { ren = r; }
~MessageMgr() {}
void Push(const std::string& title, const std::string& text);
void Update(float delta);
private:
std::vector<Container::Ref> msgs;
PD::LI::Renderer::Ref ren;
};
} // namespace PD

View File

@ -0,0 +1,23 @@
#pragma once
#include <pd/common/common.hpp>
#include <pd/controls/hid.hpp>
#include <pd/graphics/lithium.hpp>
namespace PD {
class Overlay : public SmartCtor<Overlay> {
public:
Overlay() {}
virtual ~Overlay() {}
virtual void Update(float delta, LI::Renderer::Ref ren, Hid::Ref inp) = 0;
bool IsKilled() const { return kill; }
protected:
void Kill() { kill = true; }
private:
bool kill = false;
};
} // namespace PD

View File

@ -0,0 +1,24 @@
#pragma once
#include <pd/controls/hid.hpp>
#include <pd/graphics/lithium.hpp>
#include <pd/overlays/overlay.hpp>
namespace PD {
class OverlayMgr : public SmartCtor<OverlayMgr> {
public:
OverlayMgr(LI::Renderer::Ref ren, Hid::Ref inp) {
this->ren = ren;
this->inp = inp;
}
~OverlayMgr() { overlays.clear(); }
void Push(Overlay::Ref overlay);
void Update(float delta);
private:
std::vector<Overlay::Ref> overlays;
LI::Renderer::Ref ren;
Hid::Ref inp;
};
} // namespace PD

View File

@ -0,0 +1,31 @@
#pragma once
#include <pd/overlays/overlay.hpp>
#include <pd/controls/hid.hpp>
namespace PD {
class Performance : public Overlay {
public:
Performance(bool& skill, bool& screen) {
too++;
if (too > 1) {
Kill();
return;
}
this->skill = &skill;
*this->skill = false; // Make sure its false
this->screen = &screen;
}
~Performance() { too--; }
void Update(float delta, LI::Renderer::Ref ren, Hid::Ref inp) override;
private:
void Line(vec2& pos, const std::string& text, LI::Renderer::Ref ren);
// Trace String Average
std::string TSA(const std::string& id);
// Described in Keyboard
static int too;
bool *skill, *screen;
};
} // namespace PD

View File

@ -0,0 +1,38 @@
#pragma once
#include <pd/maths/tween.hpp>
#include <pd/overlays/overlay.hpp>
#include <pd/controls/hid.hpp>
namespace PD {
class SettingsMenu : public Overlay {
public:
SettingsMenu() {
too++;
if (too > 1) {
Kill();
return;
}
flymgr.From(vec2(0, 240)).To(vec2(0, 115)).In(0.3f).As(flymgr.EaseInQuad);
}
~SettingsMenu() { too--; }
void Update(float delta, LI::Renderer::Ref ren, Hid::Ref inp) override;
void Rem() {
rem = true;
flymgr.From(vec2(0, 115)).To(vec2(0, 240)).In(0.2f).As(flymgr.EaseOutQuad);
}
private:
/// Section is used to determinate what
/// should be displayed on the top screen
int section = 0;
// Stands for The Only One
static int too;
// Some Animation
bool rem = false;
Tween<vec2> flymgr;
};
} // namespace PD

View File

@ -0,0 +1,33 @@
#pragma once
#include <pd/common/common.hpp>
#include <pd/controls/hid.hpp>
namespace PD {
namespace GamePadIcons {
enum ID {
A,
B,
X,
Y,
L,
R,
Dpad,
Start,
Select,
Home,
Steps,
PlayCoin,
AnalogStick,
Power3DS,
DpadUp,
DpadDown,
DpadLeft,
DpadRight,
DpadHorizontal,
DpadVertical,
};
std::string GetIcon(ID id);
std::string GetIcon(Hid::Key key);
} // namespace GamePadIcons
} // namespace PD

View File

@ -28,10 +28,11 @@ SOFTWARE.
#include <pd/ui7/theme.hpp>
namespace PD {
class UI7DrawList : SmartCtor<UI7DrawList> {
namespace UI7 {
class DrawList : public SmartCtor<DrawList> {
public:
UI7DrawList(LI::Renderer::Ref r) { ren = r; }
~UI7DrawList() = default;
DrawList(LI::Renderer::Ref r) { ren = r; }
~DrawList() = default;
void AddRectangle(vec2 pos, vec2 szs, const UI7Color& clr);
void AddTriangle(vec2 pos0, vec2 pos1, vec2 pos2, const UI7Color& clr);
@ -50,4 +51,5 @@ class UI7DrawList : SmartCtor<UI7DrawList> {
LI::Renderer::Ref ren;
std::vector<LI::Command::Ref> commands;
};
} // namespace UI7
} // namespace PD

View File

@ -31,5 +31,6 @@ enum UI7MenuFlags_ {
UI7MenuFlags_CenterTitle = 1 << 1,
UI7MenuFlags_HzScrolling = 1 << 2,
UI7MenuFlags_VtScrolling = 1 << 3,
UI7MenuFlags_NoBackground = 1 << 4,
UI7MenuFlags_Scrolling = UI7MenuFlags_HzScrolling | UI7MenuFlags_VtScrolling,
};

19
include/pd/ui7/id.hpp Normal file
View File

@ -0,0 +1,19 @@
#pragma once
#include <pd/common/common.hpp>
#include <pd/common/strings.hpp>
namespace PD {
namespace UI7 {
class ID {
public:
ID(const std::string& text) { id = PD::Strings::FastHash(text); }
~ID() {}
operator u32() const { return id; }
private:
u32 id;
};
} // namespace UI7
} // namespace PD

70
include/pd/ui7/menu.hpp Normal file
View File

@ -0,0 +1,70 @@
#pragma once
/*
MIT License
Copyright (c) 2024 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/drawlist.hpp>
namespace PD {
namespace UI7 {
class Menu : public SmartCtor<Menu> {
public:
Menu(u32 id) {
this->id = id;
scrolling[0] = false;
scrolling[1] = false;
scrollbar[0] = false;
scrollbar[1] = false;
scroll_allowed[0] = false;
scroll_allowed[1] = false;
};
~Menu() {};
private:
u32 id;
vec2 cursor;
vec2 bcursor;
vec2 slcursor;
vec4 view_area;
vec2 scrolling_off;
bool scrolling[2];
vec2 scroll_mod;
float tbh;
bool scrollbar[2];
bool scroll_allowed[2];
bool has_touch;
Menu::Ref submenu;
// DrawLists
DrawList::Ref back;
DrawList::Ref main;
DrawList::Ref front;
vec2 max;
vec2 mouse;
vec2 bslpos;
vec2 last_size;
};
} // namespace UI7
} // namespace PD

View File

@ -25,14 +25,96 @@ SOFTWARE.
#include <pd/common/common.hpp>
using UI7Color = unsigned int;
using UI7Color = u32;
enum UI7Color_ {
UI7Color_Background,
UI7Color_Button,
UI7Color_ButtonDead,
UI7Color_ButtonActive,
UI7Color_ButtonDisabled,
UI7Color_Text,
UI7Color_TextDead,
UI7Color_Header,
UI7Color_Selector,
UI7Color_Checkmark,
UI7Color_FrameBackground,
UI7Color_FragmeBackgroundHovered,
UI7Color_Progressbar,
UI7Color_ListEven,
UI7Color_ListOdd,
};
namespace PD {
namespace UI7 {
/// @brief Theme Class
class Theme {
public:
public:
Theme() { Default(*this); }
~Theme() {}
/// @brief Simple static Loader for the Default Theme
/// @param theme Theme Reference
static void Default(Theme& theme);
/// @brief Revert the last Color Change
Theme& Pop() {
theme[changes[changes.size() - 1].first] =
changes[changes.size() - 1].second;
changes.pop_back();
return *this;
}
/// @brief Revert the last color Change done for a specific color
/// @param c Color to revert change from
Theme& Pop(UI7Color c) {
for (size_t i = changes.size() - 1; i > 0; i--) {
if (changes[i].first == c) {
theme[c] = changes[i].second;
changes.erase(changes.begin() + i);
break;
}
}
return *this;
}
/// @brief Change a Color
/// @param tc Color Identifier
/// @param color Color to change to
Theme& Change(UI7Color tc, u32 color) {
if (theme.find(tc) == theme.end()) {
return *this;
}
changes.push_back(std::make_pair(tc, theme[tc]));
theme[tc] = color;
return *this;
}
/// @brief Get the Color of a Color ReferenceID
/// @param c ReferenceID
u32 Get(UI7Color c) const {
auto e = theme.find(c);
if (e == theme.end()) {
return 0x00000000;
}
return e->second;
}
/// @brief Operator wrapper for get
/// @param c Color ReferenceID
u32 operator[](UI7Color c) const { return Get(c); }
/// @brief Change but just sets [can implement completly new ids]
/// @param tc Color ID (Can be self creeated ones as well)
/// @param clr Color it should be set to
void Set(UI7Color tc, u32 clr) { theme[tc] = clr; }
private:
std::unordered_map<u32, u32> theme;
std::vector<std::pair<UI7Color, u32>> changes;
};
} // namespace UI7
/// Using UI7Color as a Class to be able to
/// define it as struct as well as using it as enum
class UI7Color {

View File

@ -25,13 +25,19 @@ SOFTWARE.
#include <pd/ui7/drawlist.hpp>
#include <pd/ui7/flags.hpp>
#include <pd/ui7/id.hpp>
#include <pd/ui7/menu.hpp>
#include <pd/ui7/theme.hpp>
#include <unordered_map>
namespace PD {
class UI7Context : SmartCtor<UI7Context> {
namespace UI7 {
class Context : public SmartCtor<Context> {
public:
UI7Context() {}
~UI7Context() {}
Context() {}
~Context() {}
void Update(float delta);
private:
// Timing
@ -41,13 +47,15 @@ class UI7Context : SmartCtor<UI7Context> {
// Context
bool in_menu;
// Debug
bool debug;
bool debugging;
// Menu Handlers
std::unordered_map<u32, Menu::Ref> menus;
Menu::Ref current;
// Context DrawList
UI7DrawList::Ref debug;
UI7DrawList::Ref front;
UI7DrawList::Ref back;
DrawList::Ref debug;
DrawList::Ref front;
DrawList::Ref back;
// Promt Handler
};
} // namespace UI7
} // namespace PD

View File

@ -33,16 +33,21 @@ void App::Run() {
this->Init();
last_time = Sys::GetTime();
while (aptMainLoop()) {
u64 current = Sys::GetTime();
u64 dt = current - last_time;
app_time += float(dt / 1000.f);
input_mgr->Update();
u64 current = Sys::GetNanoTime();
float dt = static_cast<float>(current - last_time) / 1000000.f;
app_time += dt / 1000.f;
last_time = current;
fps = 1000.f / (float)dt;
fps = 1000.f / dt;
PD::TT::Beg("App_MainLoop");
if (!this->MainLoop(dt, app_time)) {
break;
}
PD::TT::End("App_MainLoop");
PD::TT::Beg("Ovl_Update");
overlay_mgr->Update(dt);
msg_mgr->Update(dt);
PD::TT::End("Ovl_Update");
renderer->Render();
}
this->Deinit();
@ -54,11 +59,17 @@ void App::PreInit() {
gfxInitDefault();
cfguInit();
romfsInit();
input_mgr = Hid::New();
renderer = LI::Renderer::New();
msg_mgr = MessageMgr::New(renderer);
overlay_mgr = OverlayMgr::New(renderer, input_mgr);
}
void App::PostDeinit() {
renderer = nullptr;
msg_mgr = nullptr;
overlay_mgr = nullptr;
input_mgr = nullptr;
gfxExit();
cfguExit();
romfsExit();

View File

@ -45,7 +45,9 @@ bool StringEndsWith(const std::string& str,
}
std::wstring MakeWstring(const std::string& s) {
return std::wstring(s.begin(), s.end());
// 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();
}
const std::string FormatNanos(unsigned long long nanos) {
@ -125,4 +127,12 @@ const std::string ToHex(const T& v) {
s << "0x" << std::setfill('0') << std::setw(sizeof(v) * 2) << std::hex << v;
return s.str();
}
u32 FastHash(const std::string& s) {
u32 hash = 5381;
for (auto& it : s) {
hash = (hash * 33) + static_cast<u8>(it);
}
return hash;
}
} // namespace PD::Strings

78
source/controls/hid.cpp Normal file
View File

@ -0,0 +1,78 @@
#include <3ds.h>
#include <pd/controls/hid.hpp>
/// Reform of the RenderD7 095 Hid Api
/// Using Custom Keybindings for future
/// Porting of the library
namespace PD {
Hid::Hid() {
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 Hid::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);
}
bool Hid::IsEvent(Event e, Key keys) { return key_events[0][e] & keys; }
void Hid::SwappyTable() {
auto tkd = key_events[1][Event_Down];
auto tkh = key_events[1][Event_Held];
auto tku = key_events[1][Event_Up];
key_events[1][Event_Down] = key_events[0][Event_Down];
key_events[1][Event_Held] = key_events[0][Event_Held];
key_events[1][Event_Up] = key_events[0][Event_Up];
key_events[0][Event_Down] = tkd;
key_events[0][Event_Held] = tkh;
key_events[0][Event_Up] = tku;
}
} // namespace PD

View File

@ -75,10 +75,12 @@ void Font::LoadTTF(const std::string& path, int height) {
off[0] = 0;
}
c.uv(vec4(static_cast<float>(off.x() / (float)quad),
static_cast<float>(1.f - (off.y() / (float)quad)),
static_cast<float>((float)(off.x() + w) / (float)quad),
static_cast<float>((float)(off.y() + h) / (float)quad)));
vec4 uvs;
uvs[0] = static_cast<float>(off.x() / (float)quad);
uvs[1] = static_cast<float>(1.f - (off.y() / (float)quad));
uvs[2] = static_cast<float>((float)(off.x() + w) / (float)quad);
uvs[3] = static_cast<float>(1.f - (float)(off.y() + h) / (float)quad);
c.uv(uvs);
c.tex(tex);
c.size(vec2(w, h));
@ -94,7 +96,8 @@ void Font::LoadTTF(const std::string& path, int height) {
font_tex[map_pos + 3] = bitmap[x + y * w];
}
}
off[0] += w;
// Small Patch to avoid some possible artifacts
off[0] += w + 1;
if (off[0] + w > quad) {
off[1] += pixel_height;
off[0] = 0;
@ -123,7 +126,9 @@ void Font::LoadSystemFont() {
const auto fnt_info = fontGetInfo(fnt);
const auto glyph_info = fontGetGlyphInfo(fnt);
this->textures.resize(glyph_info->nSheets + 1);
pixel_height = glyph_info->cellHeight;
/// Modify the Pixel Height by 1.1f to fit the
/// Size og ttf font Rendering
pixel_height = glyph_info->cellHeight * 1.1f;
for (size_t i = 0; i < glyph_info->nSheets; i++) {
auto stex = Texture::New();
auto tx = new C3D_Tex;
@ -226,9 +231,9 @@ Renderer::Renderer(RenderFlags flags) {
white = Texture::New(pixels, 16, 16);
UseTex(white);
font = Font::New();
font->LoadSystemFont();
// font->LoadTTF("romfs:/ComicNeue.ttf", 32);
// Not Loading as Systemfont is freezing
// font = Font::New();
// font->LoadSystemFont();
area_size = top->GetSize();
}
@ -238,12 +243,18 @@ Renderer::~Renderer() {
C3D_Fini();
}
bool Renderer::InBox(vec2 pos, vec2 szs, vec4 rect) {
bool Renderer::InBox(const vec2& pos, const vec2& szs, const vec4& rect) {
return (pos[0] < rect[2] || pos[1] < rect[3] || pos[0] + szs[0] > rect[0] ||
pos[1] + szs[1] > rect[1]);
}
bool Renderer::InBox(vec2 alpha, vec2 bravo, vec2 charlie, vec4 rect) {
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) ||
@ -256,7 +267,7 @@ void Renderer::RotateCorner(vec2& v, float s, float c) {
v = vec2(x, y);
}
Rect Renderer::CreateRect(vec2 pos, vec2 size, float angle) {
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]),
@ -279,7 +290,7 @@ Rect Renderer::CreateRect(vec2 pos, vec2 size, float angle) {
corner[3] + pos + c);
}
Rect Renderer::CreateLine(vec2 a, vec2 b, int t) {
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();
@ -306,7 +317,7 @@ 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, vec4 uv,
void Renderer::QuadCommand(Command::Ref cmd, const Rect& quad, const vec4& uv,
u32 col) {
cmd->PushIndex(0).PushIndex(1).PushIndex(2);
cmd->PushIndex(0).PushIndex(2).PushIndex(3);
@ -320,8 +331,8 @@ void Renderer::QuadCommand(Command::Ref cmd, const Rect& quad, vec4 uv,
Vertex(vec2(quad.Bot().x(), quad.Bot().y()), vec2(uv.x(), uv.w()), col));
}
void Renderer::TriangleCommand(Command::Ref cmd, vec2 a, vec2 b, vec2 c,
u32 col) {
void Renderer::TriangleCommand(Command::Ref cmd, const vec2& a, const vec2& b,
const vec2& c, u32 col) {
cmd->Index(cmd_idx++).Layer(current_layer).Tex(current_tex);
cmd->PushIndex(2).PushIndex(1).PushIndex(0);
cmd->PushVertex(Vertex(a, vec2(0.f, 1.f), col));
@ -330,9 +341,9 @@ void Renderer::TriangleCommand(Command::Ref cmd, vec2 a, vec2 b, vec2 c,
}
/// TO BE REWRITTEN
void Renderer::TextCommand(std::vector<Command::Ref>& cmds, vec2 pos, u32 color,
const std::string& text, LITextFlags flags,
vec2 box) {
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;
}
@ -378,7 +389,7 @@ void Renderer::TextCommand(std::vector<Command::Ref>& cmds, vec2 pos, u32 color,
}
}
if (jt == '\t') {
off[0] += 4 * cfs;
off[0] += 16 * cfs;
} else {
if (jt != ' ') {
int lr = current_layer;
@ -395,6 +406,10 @@ void Renderer::TextCommand(std::vector<Command::Ref>& cmds, vec2 pos, u32 color,
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;
}
@ -449,8 +464,14 @@ vec2 Renderer::GetTextDimensions(const std::string& text) {
x = 0.f;
break;
case '\t':
x += 8 * cfs;
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()) {
@ -472,19 +493,16 @@ void Renderer::UpdateRenderMode(const RenderMode& mode) {
switch (mode) {
case RenderMode_SysFont:
C3D_TexEnvInit(env);
C3D_TexEnvSrc(env, C3D_RGB, GPU_PRIMARY_COLOR, (GPU_TEVSRC)0,
(GPU_TEVSRC)0);
C3D_TexEnvSrc(env, C3D_RGB, GPU_PRIMARY_COLOR);
C3D_TexEnvFunc(env, C3D_RGB, GPU_REPLACE);
C3D_TexEnvSrc(env, C3D_Alpha, GPU_PRIMARY_COLOR, GPU_TEXTURE0,
(GPU_TEVSRC)0);
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:
C3D_TexEnvInit(env);
C3D_TexEnvSrc(env, C3D_Both, GPU_TEXTURE0, GPU_PRIMARY_COLOR,
(GPU_TEVSRC)0);
C3D_TexEnvSrc(env, C3D_Both, GPU_TEXTURE0);
C3D_TexEnvFunc(env, C3D_Both, GPU_MODULATE);
break;
}
@ -506,9 +524,9 @@ void Renderer::RenderOn(bool bot) {
commands = cmds.size();
size_t index = 0;
if (flags & RenderFlags_LRS) {
OptiCommandList(cmds);
}
// if (flags & RenderFlags_LRS) {
OptiCommandList(cmds);
//}
while (index < cmds.size()) {
C3D_Tex* tex = cmds[index]->Tex()->GetTex();
@ -589,7 +607,8 @@ void Renderer::Render() {
}
}
void Renderer::DrawRect(vec2 pos, vec2 size, u32 color, vec4 uv) {
void Renderer::DrawRect(const vec2& pos, const vec2& size, u32 color,
const vec4& uv) {
if (!InBox(pos, size, GetViewport())) {
// Instand abort as it is out of screen
return;
@ -601,12 +620,13 @@ void Renderer::DrawRect(vec2 pos, vec2 size, u32 color, vec4 uv) {
draw_list[bottom].push_back(cmd);
}
void Renderer::DrawRectSolid(vec2 pos, vec2 size, u32 color) {
void Renderer::DrawRectSolid(const vec2& pos, const vec2& size, u32 color) {
UseTex();
DrawRect(pos, size, color);
}
void Renderer::DrawTriangle(vec2 a, vec2 b, vec2 c, u32 color) {
void Renderer::DrawTriangle(const vec2& a, const vec2& b, const vec2& c,
u32 color) {
if (!InBox(a, b, c, GetViewport())) {
return;
}
@ -617,7 +637,8 @@ void Renderer::DrawTriangle(vec2 a, vec2 b, vec2 c, u32 color) {
draw_list[bottom].push_back(cmd);
}
void Renderer::DrawCircle(vec2 center_pos, float r, u32 color, int segments) {
void Renderer::DrawCircle(const vec2& center_pos, float r, u32 color,
int segments) {
if (segments < 3) {
return;
}
@ -639,7 +660,7 @@ void Renderer::DrawCircle(vec2 center_pos, float r, u32 color, int segments) {
draw_list[bottom].push_back(cmd);
}
void Renderer::DrawLine(vec2 a, vec2 b, u32 color, int t) {
void Renderer::DrawLine(const vec2& a, const vec2& b, u32 color, int t) {
UseTex();
Rect line = CreateLine(a, b, t);
@ -649,13 +670,13 @@ void Renderer::DrawLine(vec2 a, vec2 b, u32 color, int t) {
draw_list[bottom].push_back(cmd);
}
void Renderer::DrawImage(vec2 pos, Texture::Ref tex, vec2 scale) {
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(vec2 pos, u32 color, const std::string& text, u32 flags,
vec2 ap) {
void Renderer::DrawText(const vec2& pos, u32 color, const std::string& text,
u32 flags, const vec2& ap) {
TextCommand(draw_list[bottom], pos, color, text, flags, ap);
}

View File

@ -0,0 +1,549 @@
#include <pd/external/json.hpp>
#include <pd/maths/color.hpp>
#include <pd/overlays/keyboard.hpp>
#include <pd/tools/gamepad_icons.hpp>
namespace PD {
struct Key {
Key(const std::string& key, const vec2& p, const vec2& s,
Keyboard::KeyOperation o) {
k = key;
pos = p;
size = s;
op = o;
}
std::string k;
vec2 pos;
vec2 size;
Keyboard::KeyOperation op;
};
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),
// 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),
// 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),
// 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),
// 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),
},
{
// 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),
// 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),
// 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),
// 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),
// 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),
},
{
// 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),
// 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),
// 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),
// 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),
// 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),
},
};
void DumpLayout(const std::string& path) {
nlohmann::json l0;
l0["name"] = "Default US";
for (int i = 0; i < 3; i++) {
nlohmann::json l1;
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["op"] = layouts[i][j].op;
l1.push_back(key);
}
l0[std::to_string(i)] = l1;
}
std::ofstream off(path);
off << l0.dump(3);
off.close();
}
/// The Only One (too) is a static var to make sur
/// THe Keyboard can only exist once in the overlay mgr
int Keyboard::too = 0;
void Keyboard::MoveSelector() {
/// Move from Current position to New Position
selector.From(selector).To(layouts[0][raw_sel].pos).In(0.2f);
/// If Button Size Changed, animate to the new size
if (cselszs != layouts[0][raw_sel].size) {
cselszs = layouts[0][raw_sel].size;
sel_szs.Swap();
sel_szs.To(cselszs).In(0.2);
}
}
void Keyboard::LoadTheKeys(LI::Renderer::Ref ren) {
for (int i = 0; i < 3; i++) {
keys[i] = LI::StaticObject::New();
for (auto it : layouts[i]) {
vec2 pos = it.pos + it.size * 0.5 - ren->GetTextDimensions(it.k) * 0.5;
auto c = LI::Command::New();
auto r = ren->CreateRect(it.pos, it.size, 0.f);
int l = ren->Layer();
ren->UseTex();
ren->Layer(1);
ren->SetupCommand(c);
ren->QuadCommand(c, r, vec4(0.f, 1.f, 1.f, 0.f), 0xff444444);
keys[i]->PushCommand(c);
ren->Layer(2);
ren->TextCommand(keys[i]->List(), pos, 0xffffffff, it.k, 0, 0);
ren->Layer(l);
}
ren->OptiCommandList(keys[i]->List());
}
keys_loadet = true;
}
void Keyboard::Movement(Hid::Ref inp) {
/// Any Key if no selector
if (raw_sel < 0) {
/// Initial Selector PopUp
if (inp->IsUp((Hid::Key)(inp->Up | inp->Down | inp->Left | inp->Right))) {
raw_sel = 0;
vec2 dst = layouts[0][0].pos;
cselszs = layouts[0][0].size;
selector.As(selector.Linear).From(dst + (cselszs * 0.5)).To(dst).In(0.1f);
sel_szs.As(sel_szs.Linear).From(0).To(cselszs).In(0.1f);
}
} else {
/// Go Up Movement
if (inp->IsUp(inp->Up)) {
// beginning pos of the keyboard
vec2 sp = flymgr;
// Keys are offset by 22
sp.y() += 22;
// temp pos to save selector tween to
vec2 tp = selector;
// set its offset to startpos to
// create a box over all rows
tp.y() = sp.y();
// Get the selector tween size
vec2 tszs = sel_szs;
// Get the last keys pos and size
// to calculate the size of temp szs
auto const& lok = layouts[0][layouts[0].size() - 1];
tszs.y() = lok.pos.y() + lok.size.y();
// Backwards loop
for (int i = raw_sel; i >= 0; i--) {
if (LI::Renderer::InBox(
sp + layouts[0][i].pos - vec2(0, layouts[0][i].size.y()),
layouts[0][i].size, vec4(tp, tszs))) {
raw_sel = i;
break;
}
}
MoveSelector();
}
/// Go Down Movement
if (inp->IsUp(inp->Down)) {
vec2 fly = flymgr;
vec2 sel = selector;
vec2 selszs = sel_szs;
vec4 box(fly + sel, selszs);
// clang-format off
int start = layouts[0][raw_sel].pos.y() ==
layouts[0][layouts[0].size() - 1].pos.y()
? 0 : raw_sel + 3;
// clang-format on
for (int i = start; i < (int)layouts[0].size(); i++) {
if (LI::Renderer::InBox(fly + layouts[0][i].pos, sel_szs, box)) {
raw_sel = i;
break;
}
}
MoveSelector();
}
if (inp->IsUp(inp->Right)) {
if ((raw_sel + 1 >= (int)layouts[0].size()) ||
layouts[0][raw_sel].pos.y() != layouts[0][raw_sel + 1].pos.y()) {
for (int i = raw_sel - 1; i > 0; i--) {
if (i - 1 <= 0) {
raw_sel = 0;
break;
}
if (layouts[0][i].pos.y() != layouts[0][i - 1].pos.y()) {
raw_sel = i;
break;
}
}
} else {
raw_sel++;
}
MoveSelector();
}
if (inp->IsUp(inp->Left)) {
if (raw_sel - 1 < 0 ||
layouts[0][raw_sel].pos.y() != layouts[0][raw_sel - 1].pos.y()) {
for (int i = raw_sel; i < (int)layouts[0].size(); i++) {
if (i >= (int)layouts[0].size()) {
raw_sel = (int)layouts[0].size();
}
if (layouts[0][i].pos.y() != layouts[0][i + 1].pos.y()) {
raw_sel = i;
break;
}
}
} else {
raw_sel--;
}
MoveSelector();
}
}
}
void Keyboard::DoOperation(KeyOperation op, const std::string& kname) {
switch (op) {
case AppendSelf:
*text += kname;
if (mode == 2) {
mode = 0;
}
break;
case OpCancel:
Rem();
*text = copy;
break;
case OpConfirm:
Rem();
break;
case Shift:
mode = mode == 2 ? 0 : 2;
break;
case Caps:
mode = mode == 1 ? 0 : 1;
break;
case Backspace: {
std::string c = *text;
*text = c.substr(0, c.size() - 1);
} break;
case Space:
*text += ' ';
break;
case Tab:
*text += '\t';
break;
default:
break;
}
}
void Keyboard::RecolorBy(KeyOperation op, u32 color, int cm) {
int i = 0;
/// Not the fastest but the best for custom layouts
for (auto& it : layouts[cm]) {
if (it.op == op) {
keys[cm]->ReColorQuad(i, PD::Color(0xff222222));
break;
}
i++;
}
}
void Keyboard::InputOpBind(Hid::Key k, KeyOperation op, Hid::Ref inp, int cm) {
if (inp->IsUp(k)) {
RecolorBy(op, PD::Color(0xff222222), cm);
DoOperation(op, "");
} else if (inp->IsHeld(k)) {
RecolorBy(op, PD::Color(0xff333333), cm);
}
}
void Keyboard::Update(float delta, LI::Renderer::Ref ren, Hid::Ref inp) {
/// Load Keys if not present
if (!keys_loadet) {
inp->Clear();
LoadTheKeys(ren);
}
/// Unlock Input
inp->Unlock();
/// Kill Overlay if rem was toggeled and
/// Animation is finished
if (rem && flymgr.IsFinished()) {
// Should be already unlocked ...
// ist mit aber egal
inp->Unlock();
Kill();
return; // Break to not lock again
}
/// Process Controller Movement
Movement(inp);
/// Declare RenderLayer (10 above the latest)
ren->Layer(ren->Layer() + 10);
/// Update animations
flymgr.Update(delta);
selector.Update(delta);
sel_szs.Update(delta);
/// Blend Top or|and Bottom Screen
if (flags & Flags_BlendBottom || flags & Flags_BlendTop) {
Color fade(0.3f, 0.3f, 0.3f);
if (rem) {
fade.a(fade.a() * (1.f - flymgr.Progress()));
} else {
fade.a(fade.a() * flymgr.Progress());
}
if (flags & Flags_BlendTop) {
ren->OnScreen(Screen::Top);
ren->DrawRectSolid(0, vec2(400, 240), fade);
}
if (flags & Flags_BlendBottom) {
ren->OnScreen(Screen::Bottom);
ren->DrawRectSolid(0, vec2(320, 240), fade);
}
}
/// Get the current start possition
vec2 start = flymgr;
// Draw head and Keyboard background
ren->DrawRectSolid(vec2(0, start.y()), vec2(320, 17), 0xaa000000);
ren->DrawRectSolid(vec2(0, start.y()), vec2(320, 125), 0xaa222222);
/// Grab the base layer and go one up for texts
int l = ren->Layer();
ren->Layer(l + 2);
// if (ren->Font()->SystemFont()) {
// std::stringstream s;
// s << GamePadIcons::GetIcon(GamePadIcons::B) << " Backspace ";
// s << GamePadIcons::GetIcon(GamePadIcons::Y) << " Space\n";
// s << GamePadIcons::GetIcon(GamePadIcons::X) << " Cancel ";
// s << GamePadIcons::GetIcon(GamePadIcons::Start) << " Confirm\n";
// s << GamePadIcons::GetIcon(GamePadIcons::L) << " Shift ";
// 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->GetTextDimensions(s.str()).y()+16),
// 0xffffffff, s.str());
// }
ren->DrawText(vec2(5, start.y()), 0xffffffff, "> " + *text);
ren->Layer(l + 1);
/// Offset Keys start height by 22
start[1] += 22;
/// Cache Mode to not render on 0, 0
int cm = mode;
keys[cm]->ReCopy();
int ii = 0;
for (auto& it : layouts[mode]) {
PD::Color bgc(0xff444444);
if (((ren->InBox(inp->TouchPosLast(), vec4(start + it.pos, it.size)) &&
inp->IsHeld(inp->Touch)) ||
(inp->IsHeld(inp->A) && ii == raw_sel)) &&
flymgr.IsFinished()) {
bgc = PD::Color(0xff333333);
}
if (((ren->InBox(inp->TouchPosLast(), vec4(start + it.pos, it.size)) &&
inp->IsUp(inp->Touch)) ||
(inp->IsUp(inp->A) && ii == raw_sel)) &&
flymgr.IsFinished()) {
bgc = PD::Color(0xff222222);
DoOperation(it.op, it.k);
}
/// This is hardcoded shit guessing that the
/// Buttons are in the beginning of the list
/// (Should be the case as OptiCmdList sorts by layer)
keys[cm]->ReColorQuad(ii, bgc);
ii++;
}
// Bind Key Operations
InputOpBind(inp->B, Backspace, inp, cm);
InputOpBind(inp->Y, Space, inp, cm);
InputOpBind(inp->Start, OpConfirm, inp, cm);
InputOpBind(inp->X, OpCancel, inp, cm);
InputOpBind(inp->L, Shift, inp, cm);
InputOpBind(inp->R, Caps, inp, cm);
if (raw_sel != -1) {
ren->Layer(l);
ren->DrawRectSolid(start + selector - vec2(1), vec2(sel_szs) + vec2(2),
0xffffffff);
ren->Layer(l);
}
keys[cm]->ReLayer(l);
keys[cm]->MoveIt(start);
for (auto it : keys[cm]->List()) {
ren->PushCommand(it);
}
inp->Lock();
}
} // namespace PD

View File

@ -0,0 +1,126 @@
#include <pd/overlays/message_mgr.hpp>
namespace PD {
MessageMgr::Container::Container(const std::string& title,
const std::string& msg) {
this->title = title;
this->msg = msg;
size = vec2(150, 50);
// Precalculate colors
col_bg = PD::Color("#111111aa");
col_text = PD::Color("#ffffff");
// Setup Flyin Animation
FlyIn();
}
void MessageMgr::Container::Render(PD::LI::Renderer::Ref ren) {
// Create a Temp var to not recalculate
// the position everytime we use it
// even if the calculation would always
// result in the same we would waste a lot
// of cpu performance which is a big issue
// espeacilly on the Old3ds...
vec2 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) {
kill = true;
}
// If should be removed modify the color by fade
// Use a temp var as well to not call it twice
if (tbr) {
float f = 1.f - pos.Progress();
col_bg.a(col_bg.a() * f);
col_text.a(col_text.a() * f);
}
// Create a backup Layer to Render
// Text onto the next layer
int l = ren->Layer();
ren->DrawRectSolid(tpos, size, col_bg);
ren->Layer(l + 1);
ren->DrawText(tpos + vec2(4, 2), col_text, title);
ren->DrawText(tpos + vec2(4, 16), col_text, msg);
ren->Layer(l);
}
void MessageMgr::Container::Update(int slot, float delta) {
// Increase lifetime
lifetime += delta / 1000.f;
// Trigger move up Animation if
// the slot got changed
if (s != slot) {
ToBeMoved(slot);
s = slot;
}
// Update the animations
pos.Update(delta);
// Trigger the remove Event if lifetime
// goes beyond 4 secods
if (lifetime > 4 && !tbr) {
ToBeRemoved();
}
}
void MessageMgr::Container::FlyIn() {
// Come from out of the screen to 5, 185 into
// The screen as EaseInSine in 0.5 seconds
pos.From(vec2(-size[0], 240 - size[1] - 5))
.To(vec2(5, 240 - size[1] - 5))
.In(0.5)
.As(pos.EaseInSine);
}
void MessageMgr::Container::ToBeMoved(int slot) {
// Bit more special
// Get the current pos as temp one
vec2 tpos = pos;
// Calculate a temp Startpos which
// is the endpos of Flyin if
// it is fully playd (see next comment)
float spos = 240 - size[1] - 5;
// Now from the Current Position Move up by
// The Number of slot * the size.y + 5
// and make sure it flys diagonal if the
// Flyin hasn't ended yet. This animation uses EaseInSine
// And does it's move in 0.4 seconds to not have
// The new one and this one in collision for to much time
pos.From(tpos)
.To(vec2(5, spos - slot * (size[1] + 5)))
.As(pos.EaseInSine)
.In(0.4);
}
void MessageMgr::Container::ToBeRemoved() {
// This effect uses EaseOutSine and as well uses the fade anim
tbr = true;
// We Force set the Position to Finished to avoid collision
// to the ToBeMoved Animation And then set an EaseOutSine in 0.5
// seconds to move the Message 30 pixels up while fading out
pos.Finish();
vec2 tpos = pos;
pos.From(tpos).To(tpos - vec2(0, 30)).As(pos.EaseOutSine).In(0.5);
}
void MessageMgr::Push(const std::string& title, const std::string& text) {
// Simply Add a New Message Container
msgs.push_back(Container::New(title, text));
}
void MessageMgr::Update(float delta) {
// Go two layers up and Render on Top
ren->Layer(ren->Layer() + 2);
ren->OnScreen(Screen::Top);
for (size_t i = 0; i < msgs.size(); i++) {
// Update the Animation Handlers and Move older
// Messages up if a new one got pushed
msgs[i]->Update(msgs.size() - i - 1, delta);
msgs[i]->Render(ren);
}
/// OMG HE LOOPS TWICE OVER THE OBJECTS TO
/// CHECK IF THEY CAN BE REMOVED WOW.......
/// Just my Stupid fix for the flickering
/// Of the other messages if one get's removed
for (size_t i = 0; i < msgs.size(); i++) {
if (msgs[i]->ShouldBeRemoved()) {
msgs.erase(msgs.begin() + i);
}
}
}
} // namespace PD

View File

@ -0,0 +1,14 @@
#include <pd/overlays/overlay_mgr.hpp>
namespace PD {
void OverlayMgr::Push(Overlay::Ref overlay) { overlays.push_back(overlay); }
void OverlayMgr::Update(float delta) {
for (size_t i = 0; i < overlays.size(); i++) {
if (overlays[i]->IsKilled()) {
overlays.erase(overlays.begin() + i);
continue;
}
overlays[i]->Update(delta, ren, inp);
}
}
} // namespace PD

View File

@ -0,0 +1,46 @@
#include <pd/common/strings.hpp>
#include <pd/common/sys.hpp>
#include <pd/overlays/performance.hpp>
namespace PD {
int Performance::too = 0;
void Performance::Update(float delta, LI::Renderer::Ref ren, Hid::Ref inp) {
if (*skill) {
Kill();
}
ren->OnScreen(Screen::Top);
ren->TextScale(0.6);
vec2 pos;
Line(pos, std::format("FPS {:.1f} FPS | {:.2f}ms", 1000.f / delta, delta),
ren);
Line(pos, "Ren [AVG]: " + TSA("LI_RenderAll"), ren);
Line(pos, "App [AVG]: " + TSA("App_MainLoop"), ren);
Line(pos, "Ovl [AVG]: " + TSA("Ovl_Update"), ren);
Line(pos,
"VI: [" + std::to_string(ren->Vertices()) + ", " +
std::to_string(ren->Indices()) + "]",
ren);
Line(pos,
"DC: [" + std::to_string(ren->DrawCalls()) + ", " +
std::to_string(ren->Commands()) + "]",
ren);
ren->DefaultTextScale();
}
void Performance::Line(vec2& pos, const std::string& text,
LI::Renderer::Ref ren) {
auto tbs = ren->GetTextDimensions(text);
int l = ren->Layer();
ren->DrawRectSolid(pos, tbs, 0xaa000000);
ren->Layer(l + 1);
ren->DrawText(pos, 0xffff00ff, text);
ren->Layer(l);
pos[1] += tbs[1]; // Auto set new pos
}
std::string Performance::TSA(const std::string& id) {
return PD::Strings::FormatNanos(
PD::Sys::GetTraceRef(id)->GetProtocol()->GetAverage());
}
} // namespace PD

View File

@ -0,0 +1,57 @@
#include <pd/tools/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

@ -24,7 +24,8 @@ SOFTWARE.
#include <pd/ui7/drawlist.hpp>
namespace PD {
void UI7DrawList::AddRectangle(vec2 pos, vec2 szs, const UI7Color& clr) {
namespace UI7 {
void DrawList::AddRectangle(vec2 pos, vec2 szs, const UI7Color& clr) {
if (!ren->InBox(pos, szs, ren->GetViewport())) {
return;
}
@ -37,8 +38,8 @@ void UI7DrawList::AddRectangle(vec2 pos, vec2 szs, const UI7Color& clr) {
commands.push_back(cmd);
}
void UI7DrawList::AddTriangle(vec2 pos0, vec2 pos1, vec2 pos2,
const UI7Color& clr) {
void DrawList::AddTriangle(vec2 pos0, vec2 pos1, vec2 pos2,
const UI7Color& clr) {
if (!ren->InBox(pos0, pos1, pos2, ren->GetViewport())) {
return;
}
@ -50,16 +51,15 @@ void UI7DrawList::AddTriangle(vec2 pos0, vec2 pos1, vec2 pos2,
commands.push_back(cmd);
}
void UI7DrawList::AddText(vec2 pos, const std::string& text,
const UI7Color& clr, LITextFlags flags = 0,
vec2 box = vec2()) {
void DrawList::AddText(vec2 pos, const std::string& text, const UI7Color& clr,
LITextFlags flags, vec2 box) {
// Dont create a Command here as TextCommand has autosetup
// cause it needs to generate multiple commands if
// Font uses multiple textures
ren->TextCommand(commands, pos, clr, text, flags, box);
}
void UI7DrawList::AddImage(vec2 pos, Texture::Ref img) {
void DrawList::AddImage(vec2 pos, Texture::Ref img) {
if (!ren->InBox(pos, img->GetSize(), ren->GetViewport())) {
return;
}
@ -72,14 +72,14 @@ void UI7DrawList::AddImage(vec2 pos, Texture::Ref img) {
commands.push_back(cmd);
}
void UI7DrawList::Clear() { commands.clear(); }
void DrawList::Clear() { commands.clear(); }
void UI7DrawList::Process() {
void DrawList::Process() {
// UI7 Commands Use LI7 as default feature
ren->OptiCommandList(commands);
for (auto command : commands) {
ren->PushCommand(command);
}
}
} // namespace UI7
} // namespace PD

24
source/ui7/theme.cpp Normal file
View File

@ -0,0 +1,24 @@
#include <pd/maths/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("#EEEEEEFF"));
theme.Set(UI7Color_Button, Color("#111111FF"));
theme.Set(UI7Color_ButtonDead, Color("#080808FF"));
theme.Set(UI7Color_ButtonActive, Color("#2A2A2AFF"));
theme.Set(UI7Color_ButtonDisabled, Color("#222222FF"));
theme.Set(UI7Color_Header, Color("#111111FF"));
theme.Set(UI7Color_Selector, Color("#222222FF"));
theme.Set(UI7Color_Checkmark, Color("#2A2A2AFF"));
theme.Set(UI7Color_FrameBackground, Color("#555555FF"));
theme.Set(UI7Color_FragmeBackgroundHovered, Color("#777777FF"));
theme.Set(UI7Color_Progressbar, Color("#00FF00FF"));
theme.Set(UI7Color_ListEven, Color("#CCCCCCFF"));
theme.Set(UI7Color_ListOdd, Color("#BBBBBBFF"));
}
} // namespace UI7
} // namespace PD

View File

@ -25,6 +25,7 @@ SOFTWARE.
#include <ctime>
#include <pd.hpp>
#include <pd/maths/tween.hpp>
using vec2 = PD::vec2;
using vec3 = PD::vec3;
@ -37,37 +38,36 @@ class Test : public PD::App {
void Init() override {
test = PD::Texture::New("romfs:/icon.png");
cpu = PD::TimeStats::New(100);
Overlays()->Push(PD::New<PD::Performance>(dbg, dbg_screen));
font = PD::LI::Font::New();
// font->LoadSystemFont();
font->LoadTTF("romfs:/ComicNeue.ttf", 32);
Renderer()->Font(font);
ui7 = PD::UI7::Context::New();
}
bool MainLoop(unsigned long long delta, float time) override {
cpu->Add(C3D_GetProcessingTime());
hidScanInput();
bool MainLoop(float delta, float time) override {
DrawFancyBG(time);
Renderer()->TextScale(0.6f);
vec2 start(5, 100);
DebugText(start, "FPS: " + std::to_string((int)GetFps()));
DebugText(start, "DrawCalls: " + std::to_string(Renderer()->DrawCalls()));
DebugText(start, "DrawCommands: " + std::to_string(Renderer()->Commands()));
DebugText(start, "Vertices: " + std::to_string(Renderer()->Vertices()));
DebugText(start, "Indices: " + std::to_string(Renderer()->Indices()));
DebugText(start, "Ren [AVG]: " + PD::Strings::FormatNanos(
PD::Sys::GetTraceRef("LI_RenderAll")
->GetProtocol()
->GetAverage()));
DebugText(start, "App [AVG]: " + PD::Strings::FormatNanos(
PD::Sys::GetTraceRef("App_MainLoop")
->GetProtocol()
->GetAverage()));
Renderer()->DefaultTextScale();
Renderer()->OnScreen(PD::Screen::Bottom);
Renderer()->DrawRectSolid(0, vec2(320, 240), PD::Color("#222222"));
Renderer()->UseTex(test);
Renderer()->DrawImage(vec2(130, 90), test);
Renderer()->Layer(Renderer()->Layer() + 1);
Renderer()->DrawImage(
Renderer()->GetViewport().zw() * 0.5 - test->GetSize() * 0.5, test);
Renderer()->DrawText(5, 0xffffffff, "Hello World!", LITextFlags_None);
if (hidKeysDown() & KEY_START) {
if (Input()->IsDown(PD::Hid::Start)) {
return false;
}
if (Input()->IsDown(Input()->A)) {
Overlays()->Push(PD::New<PD::Performance>(dbg, dbg_screen));
Messages()->Push("Test", "Oder SO");
// what.To(vec2(5, 200)).From(vec2(-100,
// 200)).In(0.5).As(what.EaseInQuad);
}
if (Input()->IsUp(Input()->B)) {
Overlays()->Push(PD::New<PD::Keyboard>(text, state));
// what.To(vec2(5, 180)).From(vec2(5, 200)).In(0.5).As(what.EaseOutQuad);
}
return true;
}
@ -97,16 +97,13 @@ class Test : public PD::App {
.36f + .38f * color_effect));
}
void DebugText(vec2& pos, const std::string& text) {
auto tbs = Renderer()->GetTextDimensions(text);
Renderer()->DrawRectSolid(pos, tbs, 0xaa000000);
Renderer()->DrawText(pos, 0xffff00ff, text);
pos[1] += tbs[1]; // Auto set new pos
}
private:
PD::Texture::Ref test;
PD::TimeStats::Ref cpu;
bool dbg = false, dbg_screen = false;
std::string text;
PD::Keyboard::State state;
PD::UI7::Context::Ref ui7;
PD::LI::Font::Ref font;
};
int main() {