Add files via upload

This commit is contained in:
mrdude2478 2022-04-26 01:27:14 +01:00 committed by GitHub
parent 1105062b04
commit 2409270b37
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
46 changed files with 6571 additions and 0 deletions

View File

@ -0,0 +1,128 @@
#---------------------------------------------------------------------------------
.SUFFIXES:
#---------------------------------------------------------------------------------
ifeq ($(strip $(DEVKITPRO)),)
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
endif
include $(DEVKITPRO)/devkitA64/base_rules
include $(DEVKITPRO)/libnx/switch_rules
#---------------------------------------------------------------------------------
# TARGET is the name of the output
# SOURCES is a list of directories containing source code
# DATA is a list of directories containing data files
# INCLUDES is a list of directories containing header files
#---------------------------------------------------------------------------------
BUILD := build
TARGET := pu
SOURCES := source source/pu source/pu/audio source/pu/ttf source/pu/sdl2 source/pu/ui source/pu/ui/elm source/pu/ui/extras source/pu/ui/render
INCLUDES := include
OUT_LIB := lib
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIC -ftls-model=local-exec
CFLAGS := -g -Wall -D__SWITCH__ -ffunction-sections -fdata-sections $(ARCH)
CFLAGS += $(INCLUDE) -I$(PORTLIBS)/include/freetype2 -DPU_MAJOR=$(PU_MAJOR) -DPU_MINOR=$(PU_MINOR) -DPU_MICRO=$(PU_MICRO) -DPU_VERSION=\"$(PU_MAJOR).$(PU_MINOR).$(PU_MICRO)\"
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++17
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=${DEVKITPRO}/libnx/switch.specs -g $(ARCH) -Wl,-r,-Map,$(notdir $*.map)
SDL_IMAGE_LIBS := -lSDL2_image -lpng -ljpeg
SDL_GFX_LIBS := -lSDL2_gfx
SDL_MIXER_LIBS := -lSDL2_mixer -lmodplug -lmpg123 -lvorbisidec -logg
LIBS := $(SDL_IMAGE_LIBS) $(SDL_GFX_LIBS) $(SDL_MIXER_LIBS) -lEGL -lGLESv2 -lglapi `sdl2-config --libs` `freetype-config --libs`
#---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing
# include and lib
#---------------------------------------------------------------------------------
LIBDIRS := $(PORTLIBS) $(LIBNX)
#---------------------------------------------------------------------------------
# no real need to edit anything past this point unless you need to add additional
# rules for different file extensions
#---------------------------------------------------------------------------------
ifneq ($(BUILD),$(notdir $(CURDIR)))
#---------------------------------------------------------------------------------
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
#---------------------------------------------------------------------------------
# use CXX for linking C++ projects, CC for standard C
#---------------------------------------------------------------------------------
ifeq ($(strip $(CPPFILES)),)
#---------------------------------------------------------------------------------
export LD := $(CC)
#---------------------------------------------------------------------------------
else
#---------------------------------------------------------------------------------
export LD := $(CXX)
#---------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------
export OUTPUT := $(CURDIR)/$(OUT_LIB)/lib$(TARGET).a
export DEPSDIR := $(CURDIR)/$(BUILD)
export OFILES_BIN := $(addsuffix .o,$(BINFILES))
export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
export HFILES := $(addsuffix .h,$(subst .,_,$(BINFILES)))
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
-I$(CURDIR)/$(BUILD)
.PHONY: $(BUILD) clean all
#---------------------------------------------------------------------------------
all: $(BUILD)
$(BUILD):
@[ -d $@ ] || mkdir -p $@
@[ -d $(OUT_LIB) ] || mkdir -p $(OUT_LIB)
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
#---------------------------------------------------------------------------------
clean:
@echo clean ...
@rm -fr $(CURDIR)/$(BUILD)
@rm -fr $(CURDIR)/$(OUT_LIB)
#---------------------------------------------------------------------------------
else
DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
$(OUTPUT) : $(OFILES)
$(OFILES_SRC) : $(HFILES)
#---------------------------------------------------------------------------------
%_bin.h %.bin.o : %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
-include $(DEPENDS)
#---------------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------------

View File

@ -0,0 +1,39 @@
/*
Plutonium library
@file Plutonium
@brief Plutonium library's main header. (include this to use this library properly)
@author XorTroll
@copyright Plutonium project - an easy-to-use UI framework for Nintendo Switch homebrew
*/
#pragma once
#include <pu/pu_Include.hpp>
#include <pu/audio/audio_Music.hpp>
#include <pu/audio/audio_Sfx.hpp>
#include <pu/ui/ui_Application.hpp>
#include <pu/ui/ui_Types.hpp>
#include <pu/ui/ui_Container.hpp>
#include <pu/ui/ui_Dialog.hpp>
#include <pu/ui/ui_Layout.hpp>
#include <pu/ui/ui_Overlay.hpp>
#include <pu/ui/elm/elm_Button.hpp>
#include <pu/ui/elm/elm_Element.hpp>
#include <pu/ui/elm/elm_Image.hpp>
#include <pu/ui/elm/elm_Menu.hpp>
#include <pu/ui/elm/elm_ProgressBar.hpp>
#include <pu/ui/elm/elm_Rectangle.hpp>
#include <pu/ui/elm/elm_TextBlock.hpp>
#include <pu/ui/elm/elm_Toggle.hpp>
#include <pu/ui/extras/extras_Toast.hpp>
#include <pu/ui/render/render_Renderer.hpp>
#include <pu/ui/render/render_SDL2.hpp>

View File

@ -0,0 +1,36 @@
/*
Plutonium library
@file audio_Music.hpp
@brief Music (BGM) support
@author XorTroll
@copyright Plutonium project - an easy-to-use UI framework for Nintendo Switch homebrew
*/
#pragma once
#include <SDL2/SDL_mixer.h>
#include <pu/pu_Include.hpp>
namespace pu::audio {
using Music = Mix_Music*;
Music OpenMusic(const std::string &path);
void PlayMusic(Music mus, const int loops);
void PlayMusicWithFadeIn(Music mus, const i32 llops, const i32 ms);
bool IsPlayingMusic();
void PauseMusic();
void ResumeMusic();
void SetMusicVolume(const i32 vol);
i32 GetMusicVolume();
void FadeOutMusic(const i32 ms);
void RewindMusic();
void StopMusic();
void SetMusicPosition(const double sec);
void DestroyMusic(Music &mus);
}

View File

@ -0,0 +1,26 @@
/*
Plutonium library
@file audio_Sfx.hpp
@brief Sfx (sound effects) support
@author XorTroll
@copyright Plutonium project - an easy-to-use UI framework for Nintendo Switch homebrew
*/
#pragma once
#include <SDL2/SDL_mixer.h>
#include <pu/pu_Include.hpp>
namespace pu::audio {
using Sfx = Mix_Chunk*;
Sfx LoadSfx(const std::string &path);
void PlaySfx(Sfx sfx);
void DestroySfx(Sfx &sfx);
}

View File

@ -0,0 +1,30 @@
/*
Plutonium library
@file pu_Include.hpp
@brief Basic includes and definitions for the library
@copyright Plutonium project - an easy-to-use UI framework for Nintendo Switch homebrew
*/
#pragma once
#include <switch.h>
#include <string>
#include <memory>
// Defines a static function (::New(...)) as a constructor for smart ptrs, also defines a custom type (::Ref) to simplify it
#define PU_SMART_CTOR(type) \
using Ref = std::shared_ptr<type>; \
template<typename ...Args> \
inline static Ref New(Args &&...ctor_args) { \
return std::move(std::make_shared<type>(std::forward<Args>(ctor_args)...)); \
}
namespace pu {
using i32 = s32;
}

View File

@ -0,0 +1,276 @@
/*
SDL_ttf: A companion library to SDL for working with TrueType (tm) fonts
Copyright (C) 2001-2013 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
/* This library is a wrapper around the excellent FreeType 2.0 library,
available at:
http://www.freetype.org/
*/
#ifndef _SDL_TTF_H
#define _SDL_TTF_H
#include <SDL2/SDL.h>
#include <SDL2/begin_code.h>
#include <switch.h>
/* Set up for C function definitions, even when using C++ */
#ifdef __cplusplus
extern "C" {
#endif
/* Printable format: "%d.%d.%d", MAJOR, MINOR, PATCHLEVEL
*/
#define SDL_TTF_MAJOR_VERSION 2
#define SDL_TTF_MINOR_VERSION 0
#define SDL_TTF_PATCHLEVEL 12
/* This macro can be used to fill a version structure with the compile-time
* version of the SDL_ttf library.
*/
#define SDL_TTF_VERSION(X) \
{ \
(X)->major = SDL_TTF_MAJOR_VERSION; \
(X)->minor = SDL_TTF_MINOR_VERSION; \
(X)->patch = SDL_TTF_PATCHLEVEL; \
}
/* Backwards compatibility */
#define TTF_MAJOR_VERSION SDL_TTF_MAJOR_VERSION
#define TTF_MINOR_VERSION SDL_TTF_MINOR_VERSION
#define TTF_PATCHLEVEL SDL_TTF_PATCHLEVEL
#define TTF_VERSION(X) SDL_TTF_VERSION(X)
/* This function gets the version of the dynamically linked SDL_ttf library.
it should NOT be used to fill a version structure, instead you should
use the SDL_TTF_VERSION() macro.
*/
extern DECLSPEC const SDL_version * SDLCALL TTF_Linked_Version(void);
/* ZERO WIDTH NO-BREAKSPACE (Unicode byte order mark) */
#define UNICODE_BOM_NATIVE 0xFEFF
#define UNICODE_BOM_SWAPPED 0xFFFE
/* This function tells the library whether UNICODE text is generally
byteswapped. A UNICODE BOM character in a string will override
this setting for the remainder of that string.
*/
extern DECLSPEC void SDLCALL TTF_ByteSwappedUNICODE(int swapped);
/* The internal structure containing font information */
typedef struct _TTF_Font TTF_Font;
/* Initialize the TTF engine - returns 0 if successful, -1 on error */
extern DECLSPEC int SDLCALL TTF_Init(void);
/* Open a font file and create a font of the specified point size.
* Some .fon fonts will have several sizes embedded in the file, so the
* point size becomes the index of choosing which size. If the value
* is too high, the last indexed size will be the default. */
extern DECLSPEC TTF_Font * SDLCALL TTF_OpenFont(const char *file, int ptsize);
extern DECLSPEC TTF_Font * SDLCALL TTF_OpenFontIndex(const char *file, int ptsize, long index);
extern DECLSPEC TTF_Font * SDLCALL TTF_OpenFontRW(SDL_RWops *src, int freesrc, int ptsize);
extern DECLSPEC TTF_Font * SDLCALL TTF_OpenFontIndexRW(SDL_RWops *src, int freesrc, int ptsize, long index);
/* Set and retrieve the font style */
#define TTF_STYLE_NORMAL 0x00
#define TTF_STYLE_BOLD 0x01
#define TTF_STYLE_ITALIC 0x02
#define TTF_STYLE_UNDERLINE 0x04
#define TTF_STYLE_STRIKETHROUGH 0x08
extern DECLSPEC int SDLCALL TTF_GetFontStyle(const TTF_Font *font);
extern DECLSPEC void SDLCALL TTF_SetFontStyle(TTF_Font *font, int style);
extern DECLSPEC int SDLCALL TTF_GetFontOutline(const TTF_Font *font);
extern DECLSPEC void SDLCALL TTF_SetFontOutline(TTF_Font *font, int outline);
/* Set and retrieve FreeType hinter settings */
#define TTF_HINTING_NORMAL 0
#define TTF_HINTING_LIGHT 1
#define TTF_HINTING_MONO 2
#define TTF_HINTING_NONE 3
extern DECLSPEC int SDLCALL TTF_GetFontHinting(const TTF_Font *font);
extern DECLSPEC void SDLCALL TTF_SetFontHinting(TTF_Font *font, int hinting);
/* Get the total height of the font - usually equal to point size */
extern DECLSPEC int SDLCALL TTF_FontHeight(const TTF_Font *font);
/* Get the offset from the baseline to the top of the font
This is a positive value, relative to the baseline.
*/
extern DECLSPEC int SDLCALL TTF_FontAscent(const TTF_Font *font);
/* Get the offset from the baseline to the bottom of the font
This is a negative value, relative to the baseline.
*/
extern DECLSPEC int SDLCALL TTF_FontDescent(const TTF_Font *font);
/* Get the recommended spacing between lines of text for this font */
extern DECLSPEC int SDLCALL TTF_FontLineSkip(const TTF_Font *font);
/* Get/Set whether or not kerning is allowed for this font */
extern DECLSPEC int SDLCALL TTF_GetFontKerning(const TTF_Font *font);
extern DECLSPEC void SDLCALL TTF_SetFontKerning(TTF_Font *font, int allowed);
/* Get the number of faces of the font */
extern DECLSPEC long SDLCALL TTF_FontFaces(const TTF_Font *font);
/* Get the font face attributes, if any */
extern DECLSPEC int SDLCALL TTF_FontFaceIsFixedWidth(const TTF_Font *font);
extern DECLSPEC char * SDLCALL TTF_FontFaceFamilyName(const TTF_Font *font);
extern DECLSPEC char * SDLCALL TTF_FontFaceStyleName(const TTF_Font *font);
/* Check wether a glyph is provided by the font or not */
extern DECLSPEC int SDLCALL TTF_GlyphIsProvided(const TTF_Font *font, Uint16 ch);
/* Get the metrics (dimensions) of a glyph
To understand what these metrics mean, here is a useful link:
http://freetype.sourceforge.net/freetype2/docs/tutorial/step2.html
*/
extern DECLSPEC int SDLCALL TTF_GlyphMetrics(TTF_Font *font, Uint16 ch,
int *minx, int *maxx,
int *miny, int *maxy, int *advance);
/* Get the dimensions of a rendered string of text */
extern DECLSPEC int SDLCALL TTF_SizeText(TTF_Font *font, const char *text, int *w, int *h);
extern DECLSPEC int SDLCALL TTF_SizeUTF8(TTF_Font *font, const char *text, int *w, int *h);
extern DECLSPEC int SDLCALL TTF_SizeUNICODE(TTF_Font *font, const Uint16 *text, int *w, int *h);
/* Create an 8-bit palettized surface and render the given text at
fast quality with the given font and color. The 0 pixel is the
colorkey, giving a transparent background, and the 1 pixel is set
to the text color.
This function returns the new surface, or NULL if there was an error.
*/
extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_Solid(TTF_Font *font,
const char *text, SDL_Color fg);
extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderUTF8_Solid(TTF_Font *font,
const char *text, SDL_Color fg);
extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderUNICODE_Solid(TTF_Font *font,
const Uint16 *text, SDL_Color fg);
/* Create an 8-bit palettized surface and render the given glyph at
fast quality with the given font and color. The 0 pixel is the
colorkey, giving a transparent background, and the 1 pixel is set
to the text color. The glyph is rendered without any padding or
centering in the X direction, and aligned normally in the Y direction.
This function returns the new surface, or NULL if there was an error.
*/
extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderGlyph_Solid(TTF_Font *font,
Uint16 ch, SDL_Color fg);
/* Create an 8-bit palettized surface and render the given text at
high quality with the given font and colors. The 0 pixel is background,
while other pixels have varying degrees of the foreground color.
This function returns the new surface, or NULL if there was an error.
*/
extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_Shaded(TTF_Font *font,
const char *text, SDL_Color fg, SDL_Color bg);
extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderUTF8_Shaded(TTF_Font *font,
const char *text, SDL_Color fg, SDL_Color bg);
extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderUNICODE_Shaded(TTF_Font *font,
const Uint16 *text, SDL_Color fg, SDL_Color bg);
/* Create an 8-bit palettized surface and render the given glyph at
high quality with the given font and colors. The 0 pixel is background,
while other pixels have varying degrees of the foreground color.
The glyph is rendered without any padding or centering in the X
direction, and aligned normally in the Y direction.
This function returns the new surface, or NULL if there was an error.
*/
extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderGlyph_Shaded(TTF_Font *font,
Uint16 ch, SDL_Color fg, SDL_Color bg);
/* Create a 32-bit ARGB surface and render the given text at high quality,
using alpha blending to dither the font with the given color.
This function returns the new surface, or NULL if there was an error.
*/
extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_Blended(TTF_Font *font,
const char *text, SDL_Color fg);
extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderUTF8_Blended(TTF_Font *font,
const char *text, SDL_Color fg);
extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderUNICODE_Blended(TTF_Font *font,
const Uint16 *text, SDL_Color fg);
/* Create a 32-bit ARGB surface and render the given text at high quality,
using alpha blending to dither the font with the given color.
Text is wrapped to multiple lines on line endings and on word boundaries
if it extends beyond wrapLength in pixels.
This function returns the new surface, or NULL if there was an error.
*/
extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_Blended_Wrapped(TTF_Font *font,
const char *text, SDL_Color fg, Uint32 wrapLength);
extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderUTF8_Blended_Wrapped(TTF_Font *font,
const char *text, SDL_Color fg, Uint32 wrapLength);
extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderUNICODE_Blended_Wrapped(TTF_Font *font,
const Uint16 *text, SDL_Color fg, Uint32 wrapLength);
/* Create a 32-bit ARGB surface and render the given glyph at high quality,
using alpha blending to dither the font with the given color.
The glyph is rendered without any padding or centering in the X
direction, and aligned normally in the Y direction.
This function returns the new surface, or NULL if there was an error.
*/
extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderGlyph_Blended(TTF_Font *font,
Uint16 ch, SDL_Color fg);
/* For compatibility with previous versions, here are the old functions */
#define TTF_RenderText(font, text, fg, bg) \
TTF_RenderText_Shaded(font, text, fg, bg)
#define TTF_RenderUTF8(font, text, fg, bg) \
TTF_RenderUTF8_Shaded(font, text, fg, bg)
#define TTF_RenderUNICODE(font, text, fg, bg) \
TTF_RenderUNICODE_Shaded(font, text, fg, bg)
/* Close an opened font file */
extern DECLSPEC void SDLCALL TTF_CloseFont(TTF_Font *font);
/* De-initialize the TTF engine */
extern DECLSPEC void SDLCALL TTF_Quit(void);
/* Check if the TTF engine is initialized */
extern DECLSPEC int SDLCALL TTF_WasInit(void);
/* Get the kerning size of two glyphs */
extern DECLSPEC int TTF_GetFontKerningSize(TTF_Font *font, int prev_index, int index);
/* Code present in C++ code */
TTF_Font *TTF_CppWrap_FindValidFont(TTF_Font *font, Uint16 ch);
/* Get the pointer to the C++ data */
void *TTF_CppWrap_GetCppPtrRef(TTF_Font *font);
/* Set the pointer to the C++ data */
void TTF_CppWrap_SetCppPtrRef(TTF_Font *font, void *cpp_ptr_ref);
/* We'll use SDL for reporting errors */
#define TTF_SetError SDL_SetError
#define TTF_GetError SDL_GetError
#define TMP_LOG(str) { const char *cstr = str; svcOutputDebugString(cstr, strlen(cstr)); }
/* Ends C function definitions when using C++ */
#ifdef __cplusplus
}
#endif
#include <SDL2/close_code.h>
#endif /* _SDL_TTF_H */

View File

@ -0,0 +1,17 @@
#pragma once
#include <SDL2/SDL.h>
#include <SDL2/SDL2_gfxPrimitives.h>
#include <SDL2/SDL_image.h>
#include <SDL2/SDL_mixer.h>
#include <pu/sdl2/sdl2_CustomTtf.h>
namespace pu::sdl2 {
using Texture = SDL_Texture*;
using Window = SDL_Window*;
using Renderer = SDL_Renderer*;
using Font = TTF_Font*;
using Surface = SDL_Surface*;
}

View File

@ -0,0 +1,87 @@
#pragma once
#include <pu/sdl2/sdl2_Types.hpp>
#include <pu/ui/ui_Types.hpp>
#include <vector>
namespace pu::ttf {
class Font {
private:
using FontFaceDisposingFunction = void(*)(void*);
struct FontFace {
sdl2::Font font;
void *ptr;
size_t ptr_sz;
FontFaceDisposingFunction dispose_fn;
FontFace(void *buf, const size_t buf_size, FontFaceDisposingFunction disp_fn, const u32 font_sz, void *font_class_ptr) : font(nullptr), ptr(buf), ptr_sz(buf_size), dispose_fn(disp_fn) {
this->font = TTF_OpenFontRW(SDL_RWFromMem(this->ptr, this->ptr_sz), 1, font_sz);
if(this->font != nullptr) {
TTF_CppWrap_SetCppPtrRef(this->font, font_class_ptr);
}
}
FontFace() : font(nullptr), ptr(nullptr), ptr_sz(0), dispose_fn(EmptyFontFaceDisposingFunction) {}
inline bool IsSourceValid() {
// AKA - is the base ptr and size valid?
return (this->ptr != nullptr) && (this->ptr_sz > 0);
}
void DisposeFont() {
if(this->font != nullptr) {
TTF_CloseFont(this->font);
this->font = nullptr;
}
}
void Dispose() {
this->DisposeFont();
if(this->IsSourceValid()) {
(this->dispose_fn)(this->ptr);
this->ptr = nullptr;
this->ptr_sz = 0;
}
}
};
std::vector<std::pair<i32, std::unique_ptr<FontFace>>> font_faces;
u32 font_size;
inline sdl2::Font TryGetFirstFont() {
if(!this->font_faces.empty()) {
return this->font_faces.begin()->second->font;
}
return nullptr;
}
public:
static constexpr i32 InvalidFontFaceIndex = -1;
static constexpr u32 DefaultFontSize = 25;
static void EmptyFontFaceDisposingFunction(void*) {}
static inline constexpr bool IsValidFontFaceIndex(const i32 index) {
return index != InvalidFontFaceIndex;
}
Font(const u32 font_sz) : font_size(font_sz) {}
~Font();
i32 LoadFromMemory(void *ptr, const size_t size, FontFaceDisposingFunction disp_fn);
i32 LoadFromFile(const std::string &path);
void Unload(const i32 font_idx);
inline u32 GetFontSize() {
return this->font_size;
}
sdl2::Font FindValidFontFor(const Uint16 ch);
std::pair<u32, u32> GetTextDimensions(const std::string &str);
sdl2::Texture RenderText(const std::string &str, const ui::Color clr);
};
}

View File

@ -0,0 +1,131 @@
/*
Plutonium library
@file Button.hpp
@brief A Button is an Element for option selecting.
@author XorTroll
@copyright Plutonium project - an easy-to-use UI framework for Nintendo Switch homebrew
*/
#pragma once
#include <functional>
#include <pu/ui/elm/elm_Element.hpp>
namespace pu::ui::elm {
class Button : public Element {
public:
using OnClickCallback = std::function<void()>;
static constexpr u8 DarkerColorFactor = 70;
static constexpr u8 HoverAlphaIncrement = 48;
private:
i32 x;
i32 y;
i32 w;
i32 h;
std::string fnt_name;
Color bg_clr;
Color cnt_clr;
std::string cnt;
sdl2::Texture cnt_tex;
OnClickCallback on_click_cb;
bool hover;
i32 hover_alpha;
inline Color MakeHoverBackgroundColor(const i32 alpha) {
i32 base_r = this->bg_clr.r - DarkerColorFactor;
if(base_r < 0) {
base_r = 0;
}
i32 base_g = this->bg_clr.g - DarkerColorFactor;
if(base_g < 0) {
base_g = 0;
}
i32 base_b = this->bg_clr.b - DarkerColorFactor;
if(base_b < 0) {
base_b = 0;
}
auto base_a = this->bg_clr.a;
if(alpha >= 0) {
base_a = static_cast<u8>(alpha);
}
return { static_cast<u8>(base_r), static_cast<u8>(base_g), static_cast<u8>(base_b), base_a };
}
public:
Button(const i32 x, const i32 y, const i32 width, const i32 height, const std::string &content, const Color content_clr, const Color bg_clr);
PU_SMART_CTOR(Button)
~Button();
inline i32 GetX() override {
return this->x;
}
inline void SetX(const i32 x) {
this->x = x;
}
inline i32 GetY() override {
return this->y;
}
inline void SetY(const i32 y) {
this->y = y;
}
inline i32 GetWidth() override {
return this->w;
}
inline void SetWidth(const i32 width) {
this->w = width;
}
inline i32 GetHeight() override {
return this->h;
}
inline void SetHeight(const i32 height) {
this->h = height;
}
inline std::string GetContent() {
return this->cnt;
}
void SetContent(const std::string &content);
inline Color GetContentColor() {
return this->cnt_clr;
}
void SetContentColor(const Color content_clr);
inline Color GetBackgroundColor() {
return this->bg_clr;
}
inline void SetBackgroundColor(const Color bg_clr) {
this->bg_clr = bg_clr;
}
void SetContentFont(const std::string &font_name);
inline void SetOnClick(OnClickCallback on_click_cb) {
this->on_click_cb = on_click_cb;
}
void OnRender(render::Renderer::Ref &drawer, const i32 x, const i32 y) override;
void OnInput(const u64 keys_down, const u64 keys_up, const u64 keys_held, const TouchPoint touch_pos) override;
};
}

View File

@ -0,0 +1,88 @@
/*
Plutonium library
@file Element.hpp
@brief An Element is the base of Plutonium UI's content.
@author XorTroll
@copyright Plutonium project - an easy-to-use UI framework for Nintendo Switch homebrew
*/
#pragma once
#include <pu/ui/render/render_Renderer.hpp>
namespace pu::ui {
class Container;
}
namespace pu::ui::elm {
enum class HorizontalAlign {
Left,
Center,
Right
};
enum class VerticalAlign {
Up,
Center,
Down
};
class Element {
protected:
bool visible;
HorizontalAlign h_align;
VerticalAlign v_align;
Container *parent_container;
public:
Element() : visible(true), h_align(HorizontalAlign::Left), v_align(VerticalAlign::Up), parent_container(nullptr) {}
PU_SMART_CTOR(Element)
virtual ~Element() {}
virtual i32 GetX() = 0;
virtual i32 GetY() = 0;
virtual i32 GetWidth() = 0;
virtual i32 GetHeight() = 0;
virtual void OnRender(render::Renderer::Ref &drawer, const i32 x, const i32 y) = 0;
virtual void OnInput(const u64 keys_down, const u64 keys_up, const u64 keys_held, const TouchPoint touch_pos) = 0;
inline bool IsVisible() {
return this->visible;
}
inline void SetVisible(const bool visible) {
this->visible = visible;
}
inline void SetHorizontalAlign(const HorizontalAlign align) {
this->h_align = align;
}
inline HorizontalAlign GetHorizontalAlign() {
return this->h_align;
}
inline void SetVerticalAlign(const VerticalAlign align) {
this->v_align = align;
}
inline VerticalAlign GetVerticalAlign() {
return this->v_align;
}
inline void SetParentContainer(Container *parent_container) {
this->parent_container = parent_container;
}
i32 GetProcessedX();
i32 GetProcessedY();
};
}

View File

@ -0,0 +1,86 @@
/*
Plutonium library
@file Image.hpp
@brief An Image is an Element showing a picture. (JPEG, PNG, TGA, BMP)
@author XorTroll
@copyright Plutonium project - an easy-to-use UI framework for Nintendo Switch homebrew
*/
#pragma once
#include <pu/ui/elm/elm_Element.hpp>
namespace pu::ui::elm {
class Image : public Element {
private:
std::string img_path;
sdl2::Texture img_tex;
render::TextureRenderOptions rend_opts;
i32 x;
i32 y;
public:
Image(const i32 x, const i32 y, const std::string &image_path);
PU_SMART_CTOR(Image)
~Image();
inline i32 GetX() override {
return this->x;
}
inline void SetX(const i32 x) {
this->x = x;
}
inline i32 GetY() override {
return this->y;
}
inline void SetY(const i32 y) {
this->y = y;
}
inline i32 GetWidth() override {
return this->rend_opts.width;
}
inline void SetWidth(const i32 width) {
this->rend_opts.width = width;
}
inline i32 GetHeight() override {
return this->rend_opts.height;
}
inline void SetHeight(const i32 height) {
this->rend_opts.height = height;
}
inline float GetRotationAngle() {
return this->rend_opts.rot_angle;
}
inline void SetRotationAngle(const float angle) {
this->rend_opts.rot_angle = angle;
}
inline std::string GetImagePath() {
return this->img_path;
}
void SetImage(const std::string &image_path);
inline bool IsImageValid() {
return this->img_tex != nullptr;
}
void OnRender(render::Renderer::Ref &drawer, const i32 x, const i32 y) override;
void OnInput(const u64 keys_down, const u64 keys_up, const u64 keys_held, const TouchPoint touch_pos) override {}
};
}

View File

@ -0,0 +1,291 @@
/*
Plutonium library
@file Menu.hpp
@brief A Menu is a very useful Element for option browsing or selecting.
@author XorTroll
@copyright Plutonium project - an easy-to-use UI framework for Nintendo Switch homebrew
*/
#pragma once
#include <pu/ui/elm/elm_Element.hpp>
#include <chrono>
#include <functional>
namespace pu::ui::elm {
class MenuItem {
public:
using OnKeyCallback = std::function<void()>;
static constexpr Color DefaultColor = { 10, 10, 10, 0xFF };
private:
std::string name;
Color items_clr;
std::string icon_path;
std::vector<OnKeyCallback> on_key_cbs;
std::vector<u64> on_key_cb_keys;
public:
MenuItem(const std::string &name) : name(name), items_clr(DefaultColor) {}
PU_SMART_CTOR(MenuItem)
inline std::string GetName() {
return this->name;
}
inline void SetName(const std::string &name) {
this->name = name;
}
inline Color GetColor() {
return this->items_clr;
}
inline void SetColor(const Color items_clr) {
this->items_clr = items_clr;
}
void AddOnKey(OnKeyCallback on_key_cb, const u64 key = HidNpadButton_A);
inline u32 GetOnKeyCallbackCount() {
return this->on_key_cbs.size();
}
inline OnKeyCallback GetOnKeyCallback(const u32 idx) {
if(idx < this->on_key_cbs.size()) {
return this->on_key_cbs.at(idx);
}
else {
return {};
}
}
inline u64 GetOnKeyCallbackKey(const u32 idx) {
if(idx < this->on_key_cb_keys.size()) {
return this->on_key_cb_keys.at(idx);
}
else {
return {};
}
}
inline std::string GetIconPath() {
return this->icon_path;
}
void SetIcon(const std::string &icon_path);
inline bool HasIcon() {
return !this->icon_path.empty();
}
};
class Menu : public Element {
public:
static constexpr Color DefaultScrollbarColor = { 110, 110, 110, 0xFF };
static constexpr u8 ItemAlphaIncrement = 48;
static constexpr float IconItemSizesFactor = 0.8f;
static constexpr u32 IconMargin = 25;
static constexpr u32 TextMargin = 25;
static constexpr u8 LightScrollbarColorFactor = 30;
static constexpr u32 ScrollbarWidth = 20;
static constexpr u32 ShadowHeight = 5;
static constexpr u8 ShadowBaseAlpha = 160;
using OnSelectionChangedCallback = std::function<void()>;
private:
i32 x;
i32 y;
i32 w;
i32 items_h;
u32 items_to_show;
u32 selected_item_idx;
i32 selected_item_alpha;
i32 prev_selected_item_idx;
i32 prev_selected_item_alpha;
u32 advanced_item_count;
Color scrollbar_clr;
Color items_clr;
Color items_focus_clr;
bool cooldown_enabled;
bool item_touched;
u8 move_mode;
std::chrono::time_point<std::chrono::steady_clock> move_start_time;
OnSelectionChangedCallback on_selection_changed_cb;
std::vector<MenuItem::Ref> items;
std::string font_name;
std::vector<sdl2::Texture> loaded_name_texs;
std::vector<sdl2::Texture> loaded_icon_texs;
void ReloadItemRenders();
inline Color MakeItemsFocusColor(const u8 alpha) {
return { this->items_focus_clr.r, this->items_focus_clr.g, this->items_focus_clr.b, alpha };
}
inline constexpr Color MakeLighterScrollbarColor() {
i32 base_r = this->scrollbar_clr.r - LightScrollbarColorFactor;
if(base_r < 0) {
base_r = 0;
}
i32 base_g = this->scrollbar_clr.g - LightScrollbarColorFactor;
if(base_g < 0) {
base_g = 0;
}
i32 base_b = this->scrollbar_clr.b - LightScrollbarColorFactor;
if(base_b < 0) {
base_b = 0;
}
return { static_cast<u8>(base_r), static_cast<u8>(base_g), static_cast<u8>(base_b), this->scrollbar_clr.a };
}
inline void HandleOnSelectionChanged() {
if(this->on_selection_changed_cb) {
(this->on_selection_changed_cb)();
}
}
inline void RunSelectedItemCallback(const u64 keys) {
auto item = this->items.at(this->selected_item_idx);
const auto cb_count = item->GetOnKeyCallbackCount();
for(u32 i = 0; i < cb_count; i++) {
if(keys & item->GetOnKeyCallbackKey(i)) {
if(!this->cooldown_enabled) {
auto cb = item->GetOnKeyCallback(i);
if(cb) {
cb();
}
}
}
}
this->cooldown_enabled = false;
}
inline u32 GetItemCount() {
auto item_count = this->items_to_show;
if(item_count > this->items.size()) {
item_count = this->items.size();
}
if((item_count + this->advanced_item_count) > this->items.size()) {
item_count = this->items.size() - this->advanced_item_count;
}
return item_count;
}
public:
Menu(const i32 x, const i32 y, const i32 width, const Color items_clr, const Color items_focus_clr, const i32 items_height, const u32 items_to_show);
PU_SMART_CTOR(Menu)
inline i32 GetX() override {
return this->x;
}
inline void SetX(const i32 x) {
this->x = x;
}
inline i32 GetY() override {
return this->y;
}
inline void SetY(const i32 y) {
this->y = y;
}
inline i32 GetWidth() override {
return this->w;
}
inline void SetWidth(const i32 width) {
this->w = width;
}
inline i32 GetHeight() override {
return this->items_h * this->items_to_show;
}
inline i32 GetItemsHeight() {
return this->items_h;
}
inline void SetItemsHeight(const i32 items_height) {
this->items_h = items_height;
}
inline i32 GetNumberOfItemsToShow() {
return this->items_to_show;
}
inline void SetNumberOfItemsToShow(const i32 items_to_show) {
this->items_to_show = items_to_show;
}
inline Color GetItemsColor() {
return this->items_clr;
}
inline void SetItemsColor(const Color items_clr) {
this->items_clr = items_clr;
}
inline Color GetItemsFocusColor() {
return this->items_focus_clr;
}
inline void SetItemsFocusColor(const Color items_focus_clr) {
this->items_focus_clr = items_focus_clr;
}
inline Color GetScrollbarColor() {
return this->scrollbar_clr;
}
inline void SetScrollbarColor(const Color scrollbar_clr) {
this->scrollbar_clr = scrollbar_clr;
}
inline void SetOnSelectionChanged(OnSelectionChangedCallback on_selection_changed_cb) {
this->on_selection_changed_cb = on_selection_changed_cb;
}
inline void AddItem(MenuItem::Ref &item) {
this->items.push_back(item);
}
void ClearItems();
inline void SetCooldownEnabled(const bool enabled) {
this->cooldown_enabled = enabled;
}
inline MenuItem::Ref &GetSelectedItem() {
return this->items.at(this->selected_item_idx);
}
inline std::vector<MenuItem::Ref> &GetItems() {
return this->items;
}
inline i32 GetSelectedIndex() {
return this->selected_item_idx;
}
void SetSelectedIndex(const u32 idx);
void OnRender(render::Renderer::Ref &drawer, const i32 x, const i32 y) override;
void OnInput(const u64 keys_down, const u64 keys_up, const u64 keys_held, const TouchPoint touch_pos) override;
};
}

View File

@ -0,0 +1,124 @@
/*
Plutonium library
@file ProgressBar.hpp
@brief A ProgressBar is an Element which represents a progress (a percentage) by filling a bar.
@author XorTroll
@copyright Plutonium project - an easy-to-use UI framework for Nintendo Switch homebrew
*/
#pragma once
#include <pu/ui/elm/elm_Element.hpp>
namespace pu::ui::elm {
class ProgressBar : public Element {
public:
static constexpr Color DefaultProgressColor = { 139, 195, 74, 255 };
static constexpr Color DefaultBackgroundColor = { 140, 140, 140, 255 };
private:
i32 x;
i32 y;
i32 w;
i32 h;
double val;
double max_val;
Color progress_clr;
Color bg_clr;
public:
ProgressBar(const i32 x, const i32 y, const i32 width, const i32 height, const double max_val) : Element(), x(x), y(y), w(width), h(height), val(0), max_val(max_val), progress_clr(DefaultProgressColor), bg_clr(DefaultBackgroundColor) {}
PU_SMART_CTOR(ProgressBar)
inline i32 GetX() override {
return this->x;
}
inline void SetX(const i32 x) {
this->x = x;
}
inline i32 GetY() override {
return this->y;
}
inline void SetY(const i32 y) {
this->y = y;
}
inline i32 GetWidth() override {
return this->w;
}
inline void SetWidth(const i32 width) {
this->w = width;
}
inline i32 GetHeight() override {
return this->h;
}
inline void SetHeight(const i32 height) {
this->h = height;
}
inline Color GetProgressColor() {
return this->progress_clr;
}
inline void SetProgressColor(const Color progress_clr) {
this->progress_clr = progress_clr;
}
inline Color GetBackgroundColor() {
return this->bg_clr;
}
inline void SetBackgroundColor(const Color bg_clr) {
this->bg_clr = bg_clr;
}
inline double GetProgress() {
return this->val;
}
void SetProgress(const double progress);
inline void IncrementProgress(const double extra_progress) {
this->SetProgress(this->val + extra_progress);
}
inline void DecrementProgress(const double extra_progress) {
this->SetProgress(this->val - extra_progress);
}
inline void SetMaxProgress(const double max_progress) {
this->max_val = max_progress;
}
inline double GetMaxProgress() {
return this->max_val;
}
inline void FillProgress() {
this->SetProgress(this->max_val);
}
inline void ClearProgress() {
this->SetProgress(0);
}
inline bool IsCompleted() {
return this->val == this->max_val;
}
void OnRender(render::Renderer::Ref &drawer, const i32 x, const i32 y) override;
void OnInput(const u64 keys_down, const u64 keys_up, const u64 keys_held, const TouchPoint touch_pos) override {}
};
}

View File

@ -0,0 +1,86 @@
/*
Plutonium library
@file Rectangle.hpp
@brief A Rectangle is an Element which simply draws a filled rectangle.
@author XorTroll
@copyright Plutonium project - an easy-to-use UI framework for Nintendo Switch homebrew
*/
#pragma once
#include <pu/ui/elm/elm_Element.hpp>
namespace pu::ui::elm {
class Rectangle : public Element
{
private:
i32 x;
i32 y;
i32 w;
i32 h;
Color clr;
i32 border_radius;
public:
Rectangle(const i32 x, const i32 y, const i32 width, const i32 height, const Color clr, const i32 border_radius = 0) : Element(), x(x), y(y), w(width), h(height), clr(clr), border_radius(border_radius) {}
PU_SMART_CTOR(Rectangle)
inline i32 GetX() override {
return this->x;
}
inline void SetX(const i32 x) {
this->x = x;
}
inline i32 GetY() override {
return this->y;
}
inline void SetY(const i32 y) {
this->y = y;
}
inline i32 GetWidth() override {
return this->w;
}
inline void SetWidth(const i32 width) {
this->w = width;
}
inline i32 GetHeight() override {
return this->h;
}
inline void SetHeight(const i32 height) {
this->h = height;
}
inline i32 GetBorderRadius() {
return this->border_radius;
}
inline void SetBorderRadius(const i32 border_radius) {
this->border_radius = border_radius;
}
inline Color GetColor() {
return this->clr;
}
inline void SetColor(const Color clr) {
this->clr = clr;
}
void OnRender(render::Renderer::Ref &drawer, const i32 x, const i32 y) override;
void OnInput(const u64 keys_down, const u64 keys_up, const u64 keys_held, const TouchPoint touch_pos) override {}
};
}

View File

@ -0,0 +1,71 @@
/*
Plutonium library
@file TextBlock.hpp
@brief A TextBlock is a very useful Element which is used to draw text on the screen.
@author XorTroll
@copyright Plutonium project - an easy-to-use UI framework for Nintendo Switch homebrew
*/
#pragma once
#include <pu/ui/elm/elm_Element.hpp>
namespace pu::ui::elm {
class TextBlock : public Element {
public:
static constexpr Color DefaultColor = { 0, 0, 0, 0xFF };
private:
i32 x;
i32 y;
Color clr;
std::string text;
sdl2::Texture text_tex;
std::string fnt_name;
public:
TextBlock(const i32 x, const i32 y, const std::string &text);
PU_SMART_CTOR(TextBlock)
~TextBlock();
inline i32 GetX() override {
return this->x;
}
inline void SetX(const i32 x) {
this->x = x;
}
inline i32 GetY() override {
return this->y;
}
inline void SetY(const i32 y) {
this->y = y;
}
i32 GetWidth() override;
i32 GetHeight() override;
inline std::string GetText() {
return this->text;
}
void SetText(const std::string &text);
void SetFont(const std::string &font_name);
inline Color GetColor() {
return this->clr;
}
void SetColor(const Color clr);
void OnRender(render::Renderer::Ref &drawer, const i32 x, const i32 y) override;
void OnInput(const u64 keys_down, const u64 keys_up, const u64 keys_held, const TouchPoint touch_pos) override {}
};
}

View File

@ -0,0 +1,94 @@
/*
Plutonium library
@file Toggle.hpp
@brief A Toggle is an Element used to switch between two options by toggling the item.
@author XorTroll
@copyright Plutonium project - an easy-to-use UI framework for Nintendo Switch homebrew
*/
#pragma once
#include <pu/ui/elm/elm_Element.hpp>
namespace pu::ui::elm {
class Toggle : public Element {
public:
static constexpr u32 ContentHorizontalMargin = 30;
static constexpr u32 ContentVerticalMargin = 20;
static constexpr u8 ToggleAlphaIncrement = 48;
static constexpr Color MakeBackgroundColor(const u8 alpha) {
return { 130, 130, 130, alpha };
}
private:
i32 x;
i32 y;
u64 key;
bool checked;
Color clr;
std::string fnt_name;
i32 toggle_alpha;
std::string cnt;
sdl2::Texture cnt_tex;
public:
Toggle(const i32 x, const i32 y, const std::string &content, const u64 toggle_key, const Color clr);
PU_SMART_CTOR(Toggle)
~Toggle();
inline i32 GetX() override {
return this->x;
}
inline void SetX(const i32 x) {
this->x = x;
}
inline i32 GetY() override {
return this->y;
}
inline void SetY(const i32 y) {
this->y = y;
}
i32 GetWidth() override;
i32 GetHeight() override;
inline std::string GetContent() {
return this->cnt;
}
void SetContent(const std::string &content);
void SetFont(const std::string &font_name);
inline Color GetColor() {
return this->clr;
}
void SetColor(const Color clr);
inline u64 GetKey() {
return this->key;
}
inline void SetKey(const u64 toggle_key) {
this->key = toggle_key;
}
inline bool IsChecked() {
return this->checked;
}
void OnRender(render::Renderer::Ref &drawer, const i32 x, const i32 y) override;
void OnInput(const u64 keys_down, const u64 keys_up, const u64 keys_held, const TouchPoint touch_pos) override;
};
}

View File

@ -0,0 +1,41 @@
/*
Plutonium library
@file extras_Toast.hpp
@brief An Overlay similar to Android's toast notifications
@author XorTroll
@copyright Plutonium project - an easy-to-use UI framework for Nintendo Switch homebrew
*/
#pragma once
#include <pu/ui/ui_Overlay.hpp>
#include <pu/ui/elm/elm_TextBlock.hpp>
namespace pu::ui::extras {
class Toast final : public Overlay {
public:
static constexpr i32 DefaultY = 550;
static constexpr i32 HeightAndTextHeightFactor = 3;
static constexpr i32 HorizontalMargin = 50;
static constexpr u8 BaseAlpha = 200;
private:
pu::ui::elm::TextBlock::Ref text;
void AdjustDimensions();
public:
Toast(const std::string &text, const std::string &font_name, const Color text_clr, const Color bg_clr);
PU_SMART_CTOR(Toast)
void SetText(const std::string &text);
void OnPreRender(render::Renderer::Ref &drawer) override;
void OnPostRender(render::Renderer::Ref &drawer) override;
};
}

View File

@ -0,0 +1,199 @@
/*
Plutonium library
@file render_Renderer.hpp
@brief A Renderer is the object performing basic rendering. (simply, a SDL2 wrapper)
@author XorTroll
@copyright Plutonium project - an easy-to-use UI framework for Nintendo Switch homebrew
*/
#pragma once
#include <pu/ui/ui_Types.hpp>
#include <pu/ui/render/render_SDL2.hpp>
#include <vector>
namespace pu::ui::render {
constexpr u32 ScreenWidth = 1280;
constexpr u32 ScreenHeight = 720;
struct RendererInitOptions {
u32 sdl_flags;
u32 sdl_render_flags;
u32 width;
u32 height;
bool init_ttf;
std::vector<u32> extra_default_font_sizes;
std::string default_font_path;
bool init_mixer;
u32 audio_mixer_flags;
bool init_img;
u32 sdl_img_flags;
bool init_pl;
bool init_romfs;
RendererInitOptions(const u32 sdl_flags, const u32 sdl_render_flags, const u32 w = ScreenWidth, const u32 h = ScreenHeight) : sdl_flags(sdl_flags), sdl_render_flags(sdl_render_flags), width(w), height(h), init_ttf(false), extra_default_font_sizes(), default_font_path(), init_mixer(false), audio_mixer_flags(0), init_img(false), sdl_img_flags(0), init_pl(false), init_romfs(false) {}
inline void UseTTF(const std::string &default_font_path = "") {
this->init_ttf = true;
// Empty font path = using shared font
if(!default_font_path.empty()) {
this->default_font_path = default_font_path;
}
else {
this->init_pl = true;
}
}
inline void SetExtraDefaultFontSize(const u32 font_size) {
this->extra_default_font_sizes.push_back(font_size);
}
inline void UseAudio(const u32 audio_mixer_flags) {
this->init_mixer = true;
this->audio_mixer_flags = audio_mixer_flags;
}
inline void UseImage(const u32 sdl_img_flags) {
this->init_img = true;
this->sdl_img_flags = sdl_img_flags;
}
inline void UseRomfs() {
this->init_romfs = true;
}
};
constexpr u32 MixerAllFlags = MIX_INIT_FLAC | MIX_INIT_MOD | MIX_INIT_MP3 | MIX_INIT_OGG;
constexpr u32 IMGAllFlags = IMG_INIT_PNG | IMG_INIT_JPG | IMG_INIT_TIF | IMG_INIT_WEBP;
constexpr u32 RendererSoftwareFlags = SDL_RENDERER_SOFTWARE;
constexpr u32 RendererHardwareFlags = SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED;
struct TextureRenderOptions {
i32 alpha_mod;
i32 width;
i32 height;
float rot_angle;
static constexpr i32 NoAlpha = -1;
static constexpr i32 NoWidth = -1;
static constexpr i32 NoHeight = -1;
static constexpr float NoRotation = -1.0f;
static constexpr TextureRenderOptions Default() {
return { NoAlpha, NoWidth, NoHeight, NoRotation };
}
static constexpr TextureRenderOptions WithCustomAlpha(const u8 alpha) {
return { alpha, NoWidth, NoHeight, NoRotation };
}
static constexpr TextureRenderOptions WithCustomDimensions(const i32 width, const i32 height) {
return { NoAlpha, width, height, NoRotation };
}
static constexpr TextureRenderOptions WithCustomAlphaAndDimensions(const u8 alpha, const i32 width, const i32 height) {
return { alpha, width, height, NoRotation };
}
};
class Renderer {
private:
RendererInitOptions init_opts;
bool ok_romfs;
bool ok_pl;
bool initialized;
i32 base_x;
i32 base_y;
i32 base_a;
inline u8 GetActualAlpha(const u8 input_a) {
if(this->base_a >= 0) {
return static_cast<u8>(this->base_a);
}
else {
return input_a;
}
}
public:
Renderer(const RendererInitOptions init_opts) : init_opts(init_opts), ok_romfs(false), ok_pl(false), initialized(false), base_x(0), base_y(0), base_a(0) {}
PU_SMART_CTOR(Renderer)
void Initialize();
void Finalize();
inline bool HasInitialized() {
return this->initialized;
}
inline bool HasRomFs() {
return this->ok_romfs;
}
void InitializeRender(const Color clr);
void FinalizeRender();
void RenderTexture(sdl2::Texture texture, const i32 x, const i32 y, const TextureRenderOptions opts = TextureRenderOptions::Default());
void RenderRectangle(const Color clr, const i32 x, const i32 y, const i32 width, const i32 height);
void RenderRectangleFill(const Color clr, const i32 x, const i32 y, const i32 width, const i32 height);
inline void RenderRectangleOutline(const Color clr, const i32 x, const i32 y, const i32 width, const i32 height, const i32 border_width) {
this->RenderRectangleFill(clr, x - border_width, y - border_width, width + (border_width * 2), height + (border_width * 2));
}
void RenderRoundedRectangle(const Color clr, const i32 x, const i32 y, const i32 width, const i32 height, const i32 radius);
void RenderRoundedRectangleFill(const Color clr, const i32 x, const i32 y, const i32 width, const i32 height, const i32 radius);
void RenderCircle(const Color clr, const i32 x, const i32 y, const i32 radius);
void RenderCircleFill(const Color clr, const i32 x, const i32 y, const i32 radius);
void RenderShadowSimple(const i32 x, const i32 y, const i32 width, const i32 height, const i32 base_alpha, const u8 main_alpha = 0xFF);
inline void SetBaseRenderPosition(const i32 x, const i32 y) {
this->base_x = x;
this->base_y = y;
}
inline void ResetBaseRenderPosition() {
this->SetBaseRenderPosition(0, 0);
}
inline void SetBaseRenderAlpha(const u8 alpha) {
this->base_a = alpha;
}
inline void ResetBaseRenderAlpha() {
this->base_a = -1;
}
};
// Global rendering
sdl2::Renderer GetMainRenderer();
sdl2::Window GetMainWindow();
sdl2::Surface GetMainSurface();
std::pair<u32, u32> GetDimensions();
// Text rendering
bool AddSharedFont(const std::string &font_name, const u32 font_size, const PlSharedFontType type);
bool AddAllSharedFonts(const std::string &font_name, const u32 font_size);
bool AddFontFile(const std::string &font_name, const u32 font_size, const std::string &path);
inline void AddDefaultFontFromShared(const u32 font_size) {
AddAllSharedFonts(MakeDefaultFontName(font_size), font_size);
}
inline void AddDefaultFontFromFile(const u32 font_size, const std::string &path) {
AddFontFile(MakeDefaultFontName(font_size), font_size, path);
}
sdl2::Texture RenderText(const std::string &font_name, const std::string &text, const Color clr);
i32 GetTextWidth(const std::string &font_name, const std::string &text);
i32 GetTextHeight(const std::string &font_name, const std::string &text);
}

View File

@ -0,0 +1,27 @@
/*
Plutonium library
@file render_SDL2.hpp
@brief Wrapper code to simplify SDL2 usage
@author XorTroll
@copyright Plutonium project - an easy-to-use UI framework for Nintendo Switch homebrew
*/
#pragma once
#include <pu/ui/ui_Types.hpp>
#include <pu/sdl2/sdl2_Types.hpp>
namespace pu::ui::render {
sdl2::Texture ConvertToTexture(sdl2::Surface surface);
sdl2::Texture LoadImage(const std::string &path);
i32 GetTextureWidth(sdl2::Texture texture);
i32 GetTextureHeight(sdl2::Texture texture);
void SetAlphaValue(sdl2::Texture texture, const u8 alpha);
void DeleteTexture(sdl2::Texture &texture);
}

View File

@ -0,0 +1,142 @@
/*
Plutonium library
@file ui_Application.hpp
@brief An Application is the base to use the UI system of this library.
@author XorTroll
@copyright Plutonium project - an easy-to-use UI framework for Nintendo Switch homebrew
*/
#pragma once
#include <pu/ui/ui_Dialog.hpp>
#include <pu/ui/ui_Layout.hpp>
#include <pu/ui/ui_Overlay.hpp>
#include <chrono>
namespace pu::ui {
class Application {
public:
using OnInputCallback = std::function<void(const u64, const u64, const u64, const TouchPoint)>;
using RenderCallback = std::function<void()>;
using RenderOverFunction = std::function<bool(render::Renderer::Ref&)>;
static constexpr u8 DefaultFadeAlphaIncrement = 35;
protected:
bool loaded;
bool in_render_over;
RenderOverFunction render_over_fn;
bool is_shown;
u8 fade_alpha_increment;
i32 fade_alpha;
Layout::Ref lyt;
Overlay::Ref ovl;
u64 ovl_timeout_ms;
std::chrono::steady_clock::time_point ovl_start_time;
std::vector<RenderCallback> render_cbs;
OnInputCallback on_ipt_cb;
render::Renderer::Ref renderer;
PadState input_pad;
public:
Application(render::Renderer::Ref renderer);
PU_SMART_CTOR(Application)
inline void LoadLayout(Layout::Ref lyt) {
this->lyt = lyt;
}
template<typename L>
inline std::shared_ptr<L> GetLayout() {
static_assert(std::is_base_of_v<ui::Layout, L>);
return std::static_pointer_cast<L>(this->lyt);
}
void Prepare();
// Force create a derived Application class which initializes everything here
virtual void OnLoad() = 0;
inline void AddRenderCallback(RenderCallback render_cb) {
this->render_cbs.push_back(render_cb);
}
inline void SetOnInput(OnInputCallback on_ipt_cb) {
this->on_ipt_cb = on_ipt_cb;
}
inline i32 ShowDialog(Dialog::Ref &dialog) {
return dialog->Show(this);
}
i32 CreateShowDialog(const std::string &title, const std::string &content, const std::vector<std::string> &opts, const bool use_last_opt_as_cancel, const std::string &icon_path = "");
inline void StartOverlay(Overlay::Ref ovl) {
if(this->ovl == nullptr) {
this->ovl = ovl;
}
}
void StartOverlayWithTimeout(Overlay::Ref ovl, const u64 ms);
void EndOverlay();
void Show();
inline void ShowWithFadeIn() {
this->FadeIn();
this->Show();
}
inline bool IsShown() {
return this->is_shown;
}
inline bool CanBeShown() {
return this->loaded && (this->lyt != nullptr);
}
bool CallForRender();
bool CallForRenderWithRenderOver(RenderOverFunction render_over_fn);
void FadeIn();
void FadeOut();
inline bool IsFadedIn() {
return this->fade_alpha > 0;
}
inline void SetFadeAlphaIncrement(const u8 fade_alpha_increment) {
this->fade_alpha_increment = fade_alpha_increment;
}
void OnRender();
void Close();
inline void CloseWithFadeOut() {
this->FadeOut();
this->Close();
}
inline u64 GetButtonsDown() {
return padGetButtonsDown(&this->input_pad);
}
inline u64 GetButtonsUp() {
return padGetButtonsUp(&this->input_pad);
}
inline u64 GetButtonsHeld() {
return padGetButtons(&this->input_pad);
}
inline HidTouchScreenState GetTouchState() {
HidTouchScreenState state = {};
hidGetTouchScreenStates(&state, 1);
return state;
}
};
}

View File

@ -0,0 +1,88 @@
/*
Plutonium library
@file ui_Container.hpp
@brief A Container is a basic object which contains a bunch of Elements.
@author XorTroll
@copyright Plutonium project - an easy-to-use UI framework for Nintendo Switch homebrew
*/
#pragma once
#include <pu/ui/elm/elm_Element.hpp>
#include <vector>
#include <bits/stdc++.h>
namespace pu::ui {
class Container {
protected:
i32 x;
i32 y;
i32 w;
i32 h;
std::vector<elm::Element::Ref> elems;
public:
Container(const i32 x, const i32 y, const i32 width, const i32 height) : x(x), y(y), w(width), h(height), elems() {}
PU_SMART_CTOR(Container)
inline void Add(elm::Element::Ref elem) {
this->elems.push_back(elem);
}
inline elm::Element::Ref &At(const i32 idx) {
return this->elems.at(idx);
}
inline bool Has(elm::Element::Ref &elem) {
return std::find(this->elems.begin(), this->elems.end(), elem) != this->elems.end();
}
inline void Clear() {
this->elems.clear();
}
inline size_t GetCount() {
return this->elems.size();
}
inline void SetX(const i32 x) {
this->x = x;
}
inline i32 GetX() {
return this->x;
}
inline void SetY(const i32 y) {
this->y = y;
}
inline i32 GetY() {
return this->y;
}
inline void SetWidth(const i32 width) {
this->w = width;
}
inline i32 GetWidth() {
return this->w;
}
inline void SetHeight(const i32 height) {
this->h = height;
}
inline i32 GetHeight() {
return this->h;
}
void PreRender();
};
}

View File

@ -0,0 +1,128 @@
/*
Plutonium library
@file ui_Dialog.hpp
@brief A Dialog is an easy way to ask the user to choose between several options.
@author XorTroll
@copyright Plutonium project - an easy-to-use UI framework for Nintendo Switch homebrew
*/
#pragma once
#include <pu/ui/render/render_Renderer.hpp>
#include <vector>
namespace pu::ui {
class Application;
class Dialog {
public:
static constexpr Color DefaultTitleColor = { 0xA, 0xA, 0xA, 0xFF };
static constexpr Color DefaultContentColor = { 0x14, 0x14, 0x14, 0xFF };
static constexpr Color DefaultOptionColor = { 0xA, 0xA, 0xA, 0xFF };
static constexpr u32 DialogExtraBaseWidth = 250;
static constexpr u32 DialogBorderRadius = 35;
static constexpr u32 SpaceBetweenOptions = 20;
static constexpr u32 TitleExtraWidth = 90;
static constexpr u32 ContentExtraWidth = 90;
static constexpr u32 SpaceBetweenContentAndOptions = 140;
static constexpr u32 TitleTopMargin = 20;
static constexpr u32 TitleX = 45;
static constexpr u32 TitleY = 55;
static constexpr u32 ContentX = 45;
static constexpr u32 ContentY = 140;
static constexpr u32 IconExtraHeight = 25;
static constexpr u32 OptionsBaseHorizontalMargin = 45;
static constexpr u32 OptionHeight = 60;
static constexpr u32 OptionHorizontalMargin = 30;
static constexpr u32 OptionBorderRadius = OptionHeight / 3;
static constexpr u32 OptionBottomMargin = 25;
static constexpr u8 MaxScreenFadeAlpha = 125;
static constexpr u32 IconMargin = 30;
static inline constexpr Color MakeDialogColor(const u8 alpha) {
return { 0xE1, 0xE1, 0xE1, alpha };
}
static inline constexpr Color MakeOverColor(const u8 alpha) {
return { 0xB4, 0xB4, 0xC8, alpha };
}
static constexpr u8 OverAlphaIncrement = 48;
static constexpr u8 FadeAlphaIncrement = 25;
private:
std::string title_font_name;
std::string cnt_font_name;
std::string opt_font_name;
std::string title;
std::string cnt;
sdl2::Texture title_tex;
sdl2::Texture cnt_tex;
std::vector<std::string> opts;
std::vector<sdl2::Texture> opt_texs;
std::string cancel_opt;
u32 selected_opt_idx;
i32 selected_opt_over_alpha;
i32 prev_selected_opt_idx;
i32 prev_selected_opt_over_alpha;
bool user_cancelled;
sdl2::Texture icon_tex;
public:
Dialog(const std::string &title, const std::string &content);
PU_SMART_CTOR(Dialog)
~Dialog();
void AddOption(const std::string &opt_name);
inline void SetCancelOption(const std::string &opt_name) {
this->cancel_opt = opt_name;
}
inline void RemoveCancelOption() {
this->SetCancelOption("");
}
inline bool HasCancelOption() {
return !this->cancel_opt.empty();
}
void SetIcon(const std::string &icon_path);
inline constexpr bool HasIcon() {
return this->icon_tex != nullptr;
}
i32 Show(Application *app_ref);
inline constexpr bool UserCancelled() {
return this->user_cancelled;
}
inline bool IsOk() {
if(this->user_cancelled) {
return false;
}
if(this->HasCancelOption() && (this->selected_opt_idx == (this->opt_texs.size() - 1))) {
return false;
}
return true;
}
};
}

View File

@ -0,0 +1,82 @@
/*
Plutonium library
@file ui_Layout.hpp
@brief Contains pu::Layout class, the object used to render within applications
@author XorTroll
@copyright Plutonium project - an easy-to-use UI framework for Nintendo Switch homebrew
*/
#pragma once
#include <pu/ui/ui_Container.hpp>
#include <functional>
namespace pu::ui {
class Layout : public Container {
public:
using OnInputCallback = std::function<void(const u64, const u64, const u64, const TouchPoint)>;
using RenderCallback = std::function<void()>;
static constexpr Color DefaultBackgroundColor = { 0xE1, 0xE1, 0xE1, 0xFF };
private:
bool has_image;
Color over_bg_color;
TouchPoint sim_touch_pos;
sdl2::Texture over_bg_tex;
OnInputCallback on_ipt;
std::vector<RenderCallback> render_cbs;
public:
Layout() : Container(0, 0, render::ScreenWidth, render::ScreenHeight), has_image(false), over_bg_color(DefaultBackgroundColor), sim_touch_pos(), over_bg_tex(), on_ipt(), render_cbs() {}
PU_SMART_CTOR(Layout)
~Layout();
inline bool HasChildren() {
return !this->elems.empty();
}
inline void SetOnInput(OnInputCallback on_ipt_cb) {
this->on_ipt = on_ipt_cb;
}
inline OnInputCallback GetOnInput() {
return this->on_ipt;
}
inline void AddRenderCallback(RenderCallback render_cb) {
this->render_cbs.push_back(render_cb);
}
inline std::vector<RenderCallback> &GetRenderCallbacks() {
return this->render_cbs;
}
inline bool HasBackgroundImage() {
return this->has_image;
}
inline sdl2::Texture GetBackgroundImageTexture() {
return this->over_bg_tex;
}
inline Color GetBackgroundColor() {
return this->over_bg_color;
}
void SetBackgroundImage(const std::string &path);
void SetBackgroundColor(const Color clr);
inline void SimulateTouchPosition(const TouchPoint sim_touch_pos) {
this->sim_touch_pos = sim_touch_pos;
}
TouchPoint ConsumeSimulatedTouchPosition();
};
}

View File

@ -0,0 +1,53 @@
/*
Plutonium library
@file ui_Overlay.hpp
@brief An overlay is some kind of "pop-up", like notification messages or similar items
@author XorTroll
@copyright Plutonium project - an easy-to-use UI framework for Nintendo Switch homebrew
*/
#pragma once
#include <pu/ui/ui_Container.hpp>
namespace pu::ui {
class Overlay : public Container {
public:
static constexpr i32 DefaultRadius = 25;
static constexpr i32 MaxFadeAlpha = 200;
static constexpr i32 FadeAlphaVariation = 25;
private:
i32 fade_a;
Color bg_clr;
i32 rad;
bool is_ending;
bool round;
public:
Overlay(const i32 x, const i32 y, const i32 width, const i32 height, const Color bg_clr, const bool round = true, const i32 radius = DefaultRadius) : Container(x, y, width, height), fade_a(0), bg_clr(bg_clr), rad(radius), is_ending(false), round(round) {}
PU_SMART_CTOR(Overlay)
inline void SetRadius(const i32 radius) {
this->rad = radius;
}
inline i32 GetRadius() {
return this->rad;
}
virtual void OnPreRender(render::Renderer::Ref &drawer) {}
virtual void OnPostRender(render::Renderer::Ref &drawer) {}
bool Render(render::Renderer::Ref &drawer);
inline void NotifyEnding(const bool ending) {
this->is_ending = ending;
}
};
}

View File

@ -0,0 +1,82 @@
/*
Plutonium library
@file ui_Types.hpp
@brief Several basic types helpful for UI and rendering, such as Color
@author XorTroll
@copyright Plutonium project - an easy-to-use UI framework for Nintendo Switch homebrew
*/
#pragma once
#include <pu/pu_Include.hpp>
namespace pu::ui {
// Font sizes Plutonium components use by default
enum class DefaultFontSize : u32 {
Small,
Medium,
MediumLarge,
Large,
Count
};
static inline constexpr std::array<u32, static_cast<u32>(DefaultFontSize::Count)> DefaultFontSizes = { 18, 20, 25, 30 };
inline std::string MakeDefaultFontName(const u32 font_size) {
return "DefaultFont@" + std::to_string(font_size);
}
inline constexpr u32 GetDefaultFontSize(const DefaultFontSize kind) {
return DefaultFontSizes[static_cast<u32>(kind)];
}
inline std::string GetDefaultFont(const DefaultFontSize kind) {
return MakeDefaultFontName(GetDefaultFontSize(kind));
}
struct Color {
u8 r;
u8 g;
u8 b;
u8 a;
constexpr Color() : r(0), g(0), b(0), a(0xFF) {}
constexpr Color(const u8 r, const u8 g, const u8 b, const u8 a) : r(r), g(g), b(b), a(a) {}
static Color FromHex(const std::string &str_clr);
};
static inline constexpr bool TouchHitsRegion(const i32 touch_x, const i32 touch_y, const i32 region_x, const i32 region_y, const i32 region_w, const i32 region_h) {
return (touch_x >= region_x) && (touch_x < (region_x + region_w)) && (touch_y >= region_y) && (touch_y < (region_y + region_h));
}
constexpr u64 TouchPseudoKey = HidNpadButton_29;
struct TouchPoint {
i32 x;
i32 y;
constexpr TouchPoint() : x(-1), y(-1) {}
constexpr TouchPoint(const u32 x, const u32 y) : x(x), y(y) {}
inline constexpr bool IsEmpty() const {
return (this->x < 0) && (this->y < 0);
}
inline constexpr bool HitsRegion(const i32 region_x, const i32 region_y, const i32 region_w, const i32 region_h) const {
if(this->IsEmpty()) {
return false;
}
return TouchHitsRegion(this->x, this->y, region_x, region_y, region_w, region_h);
}
};
}

View File

@ -0,0 +1,58 @@
#include <pu/audio/audio_Music.hpp>
namespace pu::audio {
Music OpenMusic(const std::string &path) {
return Mix_LoadMUS(path.c_str());
}
void PlayMusic(Music mus, const i32 loops) {
Mix_PlayMusic(mus, loops);
}
void PlayMusicWithFadeIn(Music mus, const i32 loops, const i32 ms) {
Mix_FadeInMusic(mus, loops, ms);
}
bool IsPlayingMusic() {
return Mix_PlayingMusic();
}
void PauseMusic() {
Mix_PauseMusic();
}
void ResumeMusic() {
Mix_ResumeMusic();
}
void SetMusicVolume(const i32 vol) {
Mix_VolumeMusic(vol);
}
i32 GetMusicVolume() {
return Mix_VolumeMusic(-1);
}
void FadeOutMusic(const i32 ms) {
Mix_FadeOutMusic(ms);
}
void RewindMusic() {
Mix_RewindMusic();
}
void StopMusic() {
Mix_HaltMusic();
}
void SetMusicPosition(const double sec) {
Mix_SetMusicPosition(sec);
}
void DestroyMusic(Music &mus) {
Mix_FreeMusic(mus);
mus = nullptr;
}
}

View File

@ -0,0 +1,18 @@
#include <pu/audio/audio_Sfx.hpp>
namespace pu::audio {
Sfx LoadSfx(const std::string &path) {
return Mix_LoadWAV(path.c_str());
}
void PlaySfx(Sfx sfx) {
Mix_PlayChannel(-1, sfx, 0);
}
void DestroySfx(Sfx &sfx) {
Mix_FreeChunk(sfx);
sfx = nullptr;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,142 @@
#include <pu/ttf/ttf_Font.hpp>
#include <pu/ui/render/render_Renderer.hpp>
#include <pu/ui/render/render_SDL2.hpp>
namespace pu::ttf {
namespace {
void FileBufferFontFaceDisposingFunction(void *ptr) {
if(ptr != nullptr) {
auto file_buf = reinterpret_cast<u8*>(ptr);
delete[] file_buf;
}
}
}
Font::~Font() {
for(auto &[idx, font] : this->font_faces) {
font->Dispose();
}
}
i32 Font::LoadFromMemory(void *ptr, const size_t size, FontFaceDisposingFunction disp_fn) {
const auto idx = rand();
auto font = std::make_unique<FontFace>(ptr, size, disp_fn, this->font_size, reinterpret_cast<void*>(this));
this->font_faces.push_back({ idx, std::move(font) });
return idx;
}
i32 Font::LoadFromFile(const std::string &path) {
auto f = fopen(path.c_str(), "rb");
if(f) {
fseek(f, 0, SEEK_END);
const auto f_size = ftell(f);
rewind(f);
if(f_size > 0) {
auto font_buf = new u8[f_size]();
fread(font_buf, 1, f_size, f);
fclose(f);
return this->LoadFromMemory(font_buf, f_size, FileBufferFontFaceDisposingFunction);
}
fclose(f);
}
return InvalidFontFaceIndex;
}
void Font::Unload(const i32 font_idx) {
u32 i = 0;
for(auto &[idx, font]: this->font_faces) {
if(idx == font_idx) {
this->font_faces.erase(this->font_faces.begin() + i);
break;
}
i++;
}
}
sdl2::Font Font::FindValidFontFor(const Uint16 ch) {
for(const auto &[idx, font] : this->font_faces) {
if(TTF_GlyphIsProvided(font->font, ch)) {
return font->font;
}
}
return nullptr;
}
namespace {
inline void ProcessLineDimensionsImpl(sdl2::Font font, std::string &str, u32 &w, u32 &h) {
i32 str_w = 0;
i32 str_h = 0;
TTF_SizeUTF8(font, str.c_str(), &str_w, &str_h);
const auto str_w_32 = static_cast<u32>(str_w);
const auto str_h_32 = static_cast<u32>(str_h);
if(str_w_32 > w) {
w = str_w_32;
}
h += str_h_32;
str = "";
}
}
std::pair<u32, u32> Font::GetTextDimensions(const std::string &str) {
u32 w = 0;
u32 h = 0;
auto font = this->TryGetFirstFont();
if(font != nullptr) {
std::string tmp_line;
for(const auto &ch: str) {
if(ch == '\n') {
ProcessLineDimensionsImpl(font, tmp_line, w, h);
}
else {
tmp_line += ch;
}
}
if(!tmp_line.empty()) {
ProcessLineDimensionsImpl(font, tmp_line, w, h);
}
}
return { w, h };
}
sdl2::Texture Font::RenderText(const std::string &str, const ui::Color clr) {
auto font = this->TryGetFirstFont();
if(font != nullptr) {
const auto [w, _] = ui::render::GetDimensions();
auto srf = TTF_RenderUTF8_Blended_Wrapped(font, str.c_str(), { clr.r, clr.g, clr.b, clr.a }, w);
return ui::render::ConvertToTexture(srf);
}
else {
return nullptr;
}
}
}
extern "C" {
pu::sdl2::Font TTF_CppWrap_FindValidFont(pu::sdl2::Font font, Uint16 ch) {
if(font != nullptr) {
auto raw_font_ptr = TTF_CppWrap_GetCppPtrRef(font);
if(raw_font_ptr != nullptr) {
auto font_ptr = reinterpret_cast<pu::ttf::Font*>(raw_font_ptr);
auto find_font = font_ptr->FindValidFontFor(ch);
if(find_font == nullptr) {
return font;
}
else {
return find_font;
}
}
}
return font;
}
}

View File

@ -0,0 +1,89 @@
#include <pu/ui/elm/elm_Button.hpp>
namespace pu::ui::elm {
Button::Button(const i32 x, const i32 y, const i32 width, const i32 height, const std::string &content, const Color content_clr, const Color bg_clr) : Element::Element() {
this->x = x;
this->y = y;
this->w = width;
this->h = height;
this->cnt = content;
this->cnt_clr = content_clr;
this->bg_clr = bg_clr;
this->hover = false;
this->hover_alpha = 0xFF;
this->fnt_name = GetDefaultFont(DefaultFontSize::MediumLarge);
this->cnt_tex = nullptr;
this->SetContent(content);
this->on_click_cb = {};
}
Button::~Button() {
render::DeleteTexture(this->cnt_tex);
}
void Button::SetContent(const std::string &content) {
this->cnt = content;
render::DeleteTexture(this->cnt_tex);
this->cnt_tex = render::RenderText(this->fnt_name, content, this->cnt_clr);
}
void Button::SetContentColor(const Color content_clr) {
this->cnt_clr = content_clr;
this->SetContent(this->cnt);
}
void Button::SetContentFont(const std::string &font_name) {
this->fnt_name = font_name;
this->SetContent(this->cnt);
}
void Button::OnRender(render::Renderer::Ref &drawer, const i32 x, const i32 y) {
drawer->RenderRectangleFill(this->bg_clr, x, y, this->w, this->h);
if(this->hover) {
if(this->hover_alpha < 0xFF) {
const auto hover_bg_clr = this->MakeHoverBackgroundColor(this->hover_alpha);
drawer->RenderRectangleFill(hover_bg_clr, x, y, this->w, this->h);
this->hover_alpha += HoverAlphaIncrement;
}
else {
this->hover_alpha = 0xFF;
const auto darker_bg_clr = this->MakeHoverBackgroundColor(-1);
drawer->RenderRectangleFill(darker_bg_clr, x, y, this->w, this->h);
}
}
else {
if(this->hover_alpha > 0) {
const auto hover_bg_clr = this->MakeHoverBackgroundColor(this->hover_alpha);
drawer->RenderRectangleFill(hover_bg_clr, x, y, this->w, this->h);
this->hover_alpha -= HoverAlphaIncrement;
}
else {
this->hover_alpha = 0;
drawer->RenderRectangleFill(this->bg_clr, x, y, this->w, this->h);
}
}
const auto cnt_width = render::GetTextureWidth(this->cnt_tex);
const auto cnt_height = render::GetTextureHeight(this->cnt_tex);
const auto cnt_x = x + ((this->w - cnt_width) / 2);
const auto cnt_y = y + ((this->h - cnt_height) / 2);
drawer->RenderTexture(this->cnt_tex, cnt_x, cnt_y);
}
void Button::OnInput(const u64 keys_down, const u64 keys_up, const u64 keys_held, const TouchPoint touch_pos) {
if(this->hover) {
if(touch_pos.IsEmpty()) {
(this->on_click_cb)();
this->hover = false;
this->hover_alpha = 0xFF;
}
}
else {
if(touch_pos.HitsRegion(this->x, this->y, this->w, this->h)) {
this->hover = true;
this->hover_alpha = 0;
}
}
}
}

View File

@ -0,0 +1,38 @@
#include <pu/ui/elm/elm_Element.hpp>
#include <pu/ui/ui_Layout.hpp>
namespace pu::ui::elm {
i32 Element::GetProcessedX() {
auto x = this->GetX();
if(this->parent_container != nullptr) {
auto container = reinterpret_cast<Container*>(this->parent_container);
x += container->GetX();
if(this->h_align == HorizontalAlign::Center) {
x = container->GetX() + ((container->GetWidth() - this->GetWidth()) / 2);
}
else if(this->h_align == HorizontalAlign::Right) {
x = container->GetX() + (container->GetWidth() - this->GetWidth());
}
}
return x;
}
i32 Element::GetProcessedY() {
auto y = this->GetY();
if(this->parent_container != nullptr) {
auto container = reinterpret_cast<Container*>(this->parent_container);
y += container->GetY();
if(this->v_align == VerticalAlign::Center) {
y = container->GetY() + ((container->GetHeight() - this->GetHeight()) / 2);
}
else if(this->v_align == VerticalAlign::Down) {
y = container->GetY() + (container->GetHeight() - this->GetHeight());
}
}
return y;
}
}

View File

@ -0,0 +1,34 @@
#include <pu/ui/elm/elm_Image.hpp>
#include <sys/stat.h>
namespace pu::ui::elm {
Image::Image(const i32 x, const i32 y, const std::string &image_path) : Element::Element() {
this->x = x;
this->y = y;
this->img_tex = nullptr;
this->rend_opts = render::TextureRenderOptions::Default();
this->SetImage(image_path);
}
Image::~Image() {
render::DeleteTexture(this->img_tex);
}
void Image::SetImage(const std::string &image_path) {
render::DeleteTexture(this->img_tex);
struct stat st;
if(stat(image_path.c_str(), &st) == 0) {
this->img_path = image_path;
this->img_tex = render::LoadImage(image_path);
this->rend_opts.width = render::GetTextureWidth(this->img_tex);
this->rend_opts.height = render::GetTextureHeight(this->img_tex);
}
}
void Image::OnRender(render::Renderer::Ref &drawer, const i32 x, const i32 y) {
drawer->RenderTexture(this->img_tex, x, y, this->rend_opts);
}
}

View File

@ -0,0 +1,310 @@
#include <pu/ui/elm/elm_Menu.hpp>
#include <sys/stat.h>
namespace pu::ui::elm {
void MenuItem::AddOnKey(OnKeyCallback on_key_cb, const u64 key) {
this->on_key_cbs.push_back(on_key_cb);
this->on_key_cb_keys.push_back(key);
}
void MenuItem::SetIcon(const std::string &icon_path) {
struct stat st;
if(stat(icon_path.c_str(), &st) == 0) {
this->icon_path = icon_path;
}
else {
this->icon_path = "";
}
}
void Menu::ReloadItemRenders() {
for(auto &name_tex : this->loaded_name_texs) {
render::DeleteTexture(name_tex);
}
this->loaded_name_texs.clear();
for(auto &icon_tex : this->loaded_icon_texs) {
render::DeleteTexture(icon_tex);
}
this->loaded_icon_texs.clear();
const auto item_count = this->GetItemCount();
for(u32 i = this->advanced_item_count; i < (this->advanced_item_count + item_count); i++) {
auto &item = this->items.at(i);
auto name_tex = render::RenderText(this->font_name, item->GetName(), item->GetColor());
this->loaded_name_texs.push_back(name_tex);
if(item->HasIcon()) {
auto icon_tex = render::LoadImage(item->GetIconPath());
this->loaded_icon_texs.push_back(icon_tex);
}
else {
this->loaded_icon_texs.push_back(nullptr);
}
}
}
Menu::Menu(const i32 x, const i32 y, const i32 width, const Color items_clr, const Color items_focus_clr, const i32 items_height, const u32 items_to_show) : Element::Element() {
this->x = x;
this->y = y;
this->w = width;
this->items_clr = items_clr;
this->scrollbar_clr = DefaultScrollbarColor;
this->items_h = items_height;
this->items_to_show = items_to_show;
this->prev_selected_item_idx = 0;
this->selected_item_idx = 0;
this->advanced_item_count = 0;
this->selected_item_alpha = 0xFF;
this->prev_selected_item_alpha = 0;
this->on_selection_changed_cb = {};
this->cooldown_enabled = false;
this->item_touched = false;
this->items_focus_clr = items_focus_clr;
this->move_mode = 0;
this->font_name = GetDefaultFont(DefaultFontSize::MediumLarge);
}
void Menu::ClearItems() {
this->items.clear();
for(auto &name_tex : this->loaded_name_texs) {
render::DeleteTexture(name_tex);
}
this->loaded_name_texs.clear();
for(auto &icon_tex : this->loaded_icon_texs) {
render::DeleteTexture(icon_tex);
}
this->loaded_icon_texs.clear();
this->selected_item_idx = 0;
this->prev_selected_item_idx = 0;
this->advanced_item_count = 0;
}
void Menu::SetSelectedIndex(const u32 idx) {
if(idx < this->items.size()) {
this->selected_item_idx = idx;
this->advanced_item_count = 0;
if(this->selected_item_idx >= (this->items.size() - this->items_to_show)) {
this->advanced_item_count = this->items.size() - this->items_to_show;
}
else if(this->selected_item_idx < this->items_to_show) {
this->advanced_item_count = 0;
}
else {
this->advanced_item_count = this->selected_item_idx;
}
this->ReloadItemRenders();
this->selected_item_alpha = 0xFF;
this->prev_selected_item_alpha = 0;
}
}
void Menu::OnRender(render::Renderer::Ref &drawer, const i32 x, const i32 y) {
if(!this->items.empty()) {
const auto item_count = this->GetItemCount();
if(this->loaded_name_texs.empty()) {
this->ReloadItemRenders();
}
auto cur_item_y = y;
for(u32 i = this->advanced_item_count; i < (this->advanced_item_count + item_count); i++) {
const auto loaded_tex_idx = i - this->advanced_item_count;
auto name_tex = this->loaded_name_texs.at(loaded_tex_idx);
auto icon_tex = this->loaded_icon_texs.at(loaded_tex_idx);
if(this->selected_item_idx == i) {
drawer->RenderRectangleFill(this->items_clr, x, cur_item_y, this->w, this->items_h);
if(this->selected_item_alpha < 0xFF) {
const auto focus_clr = this->MakeItemsFocusColor(this->selected_item_alpha);
drawer->RenderRectangleFill(focus_clr, x, cur_item_y, this->w, this->items_h);
this->selected_item_alpha += ItemAlphaIncrement;
}
else {
drawer->RenderRectangleFill(this->items_focus_clr, x, cur_item_y, this->w, this->items_h);
}
}
else if(this->prev_selected_item_idx == static_cast<i32>(i)) {
drawer->RenderRectangleFill(this->items_clr, x, cur_item_y, this->w, this->items_h);
if(this->prev_selected_item_alpha > 0) {
const auto focus_clr = this->MakeItemsFocusColor(this->prev_selected_item_alpha);
drawer->RenderRectangleFill(focus_clr, x, cur_item_y, this->w, this->items_h);
this->prev_selected_item_alpha -= ItemAlphaIncrement;
}
else {
drawer->RenderRectangleFill(this->items_clr, x, cur_item_y, this->w, this->items_h);
}
}
else {
drawer->RenderRectangleFill(this->items_clr, x, cur_item_y, this->w, this->items_h);
}
auto &item = this->items.at(i);
const auto name_height = render::GetTextureHeight(name_tex);
auto name_x = x + TextMargin;
const auto name_y = cur_item_y + ((this->items_h - name_height) / 2);
if(item->HasIcon()) {
const auto factor = (float)render::GetTextureHeight(icon_tex) / (float)render::GetTextureWidth(icon_tex);
auto icon_width = (i32)(this->items_h * IconItemSizesFactor);
auto icon_height = icon_width;
if(factor < 1) {
icon_height = (i32)(icon_width * factor);
}
else {
icon_width = (i32)(icon_height * factor);
}
const auto icon_x = x + IconMargin;
const auto icon_y = cur_item_y + (this->items_h - icon_height) / 2;
name_x = icon_x + icon_width + TextMargin;
drawer->RenderTexture(icon_tex, icon_x, icon_y, render::TextureRenderOptions::WithCustomDimensions(icon_width, icon_height));
}
drawer->RenderTexture(name_tex, name_x, name_y);
cur_item_y += this->items_h;
}
if(this->items_to_show < this->items.size()) {
const auto scrollbar_x = x + (this->w - ScrollbarWidth);
const auto scrollbar_height = this->GetHeight();
drawer->RenderRectangleFill(this->scrollbar_clr, scrollbar_x, y, ScrollbarWidth, scrollbar_height);
const auto light_scrollbar_clr = this->MakeLighterScrollbarColor();
const auto scrollbar_front_height = (this->items_to_show * scrollbar_height) / this->items.size();
const auto scrollbar_front_y = y + (this->advanced_item_count * (scrollbar_height / this->items.size()));
drawer->RenderRectangleFill(light_scrollbar_clr, scrollbar_x, scrollbar_front_y, ScrollbarWidth, scrollbar_front_height);
}
drawer->RenderShadowSimple(x, cur_item_y, this->w, ShadowHeight, ShadowBaseAlpha);
}
}
void Menu::OnInput(const u64 keys_down, const u64 keys_up, const u64 keys_held, const TouchPoint touch_pos) {
if(this->items.empty()) {
return;
}
if(this->move_mode == 1) {
const auto cur_time = std::chrono::steady_clock::now();
const auto time_diff_ms = std::chrono::duration_cast<std::chrono::milliseconds>(cur_time - this->move_start_time).count();
if(time_diff_ms >= 150) {
this->move_mode = 2;
}
}
if(!touch_pos.IsEmpty()) {
const auto x = this->GetProcessedX();
auto cur_item_y = this->GetProcessedY();
const auto item_count = this->GetItemCount();
for(u32 i = this->advanced_item_count; i < (this->advanced_item_count + item_count); i++) {
if(touch_pos.HitsRegion(x, cur_item_y, this->w, this->items_h)) {
this->item_touched = true;
this->prev_selected_item_idx = this->selected_item_idx;
this->selected_item_idx = i;
this->HandleOnSelectionChanged();
if(i == this->selected_item_idx) {
this->selected_item_alpha = 0xFF;
}
else if(static_cast<i32>(i) == this->prev_selected_item_idx) {
this->prev_selected_item_alpha = 0;
}
break;
}
cur_item_y += this->items_h;
}
}
else if(this->item_touched) {
if((this->selected_item_alpha >= 0xFF) && (this->prev_selected_item_alpha <= 0)) {
if(this->cooldown_enabled) {
this->cooldown_enabled = false;
}
else {
this->RunSelectedItemCallback(TouchPseudoKey);
}
this->item_touched = false;
}
}
else {
if(keys_down & HidNpadButton_AnyDown) {
auto move = true;
if(keys_held & HidNpadButton_StickRDown) {
move = false;
if(this->move_mode == 0) {
this->move_start_time = std::chrono::steady_clock::now();
this->move_mode = 1;
}
else if(move_mode == 2) {
this->move_mode = 0;
move = true;
}
}
if(move) {
if(!this->items.empty() && (this->selected_item_idx < (this->items.size() - 1))) {
if((this->selected_item_idx - this->advanced_item_count) == (this->items_to_show - 1)) {
this->advanced_item_count++;
this->selected_item_idx++;
this->HandleOnSelectionChanged();
this->ReloadItemRenders();
}
else {
this->prev_selected_item_idx = this->selected_item_idx;
this->selected_item_idx++;
this->HandleOnSelectionChanged();
this->selected_item_alpha = 0;
this->prev_selected_item_alpha = 0xFF;
}
}
else {
this->selected_item_idx = 0;
this->advanced_item_count = 0;
if(this->items.size() >= this->items_to_show) {
this->ReloadItemRenders();
}
}
}
}
else if(keys_down & HidNpadButton_AnyUp) {
auto move = true;
if(keys_held & HidNpadButton_StickRUp) {
move = false;
if(this->move_mode == 0) {
this->move_start_time = std::chrono::steady_clock::now();
this->move_mode = 1;
}
else if(this->move_mode == 2) {
this->move_mode = 0;
move = true;
}
}
if(move) {
if(this->selected_item_idx > 0) {
if(this->selected_item_idx == this->advanced_item_count) {
this->advanced_item_count--;
this->selected_item_idx--;
this->HandleOnSelectionChanged();
this->ReloadItemRenders();
}
else {
this->prev_selected_item_idx = this->selected_item_idx;
this->selected_item_idx--;
this->HandleOnSelectionChanged();
this->selected_item_alpha = 0;
this->prev_selected_item_alpha = 0xFF;
}
}
else {
this->selected_item_idx = this->items.size() - 1;
this->advanced_item_count = 0;
if(this->items.size() >= this->items_to_show) {
this->advanced_item_count = this->items.size() - this->items_to_show;
this->ReloadItemRenders();
}
}
}
}
else {
this->RunSelectedItemCallback(keys_down);
}
}
}
}

View File

@ -0,0 +1,22 @@
#include <pu/ui/elm/elm_ProgressBar.hpp>
namespace pu::ui::elm {
void ProgressBar::SetProgress(const double progress) {
if(progress >= this->max_val) {
this->val = this->max_val;
}
else {
this->val = progress;
}
}
void ProgressBar::OnRender(render::Renderer::Ref &drawer, const i32 x, const i32 y) {
const auto progress_width = (i32)((this->val / this->max_val) * (double)this->w);
// TODO: set radius?
const auto radius = (this->h / 3);
drawer->RenderRoundedRectangleFill(this->bg_clr, x, y, this->w, this->h, radius);
drawer->RenderRoundedRectangleFill(this->progress_clr, x, y, progress_width, this->h, radius);
}
}

View File

@ -0,0 +1,9 @@
#include <pu/ui/elm/elm_Rectangle.hpp>
namespace pu::ui::elm {
void Rectangle::OnRender(render::Renderer::Ref &drawer, const i32 x, const i32 y) {
drawer->RenderRectangleFill(this->clr, x, y, this->w, this->h);
}
}

View File

@ -0,0 +1,46 @@
#include <pu/ui/elm/elm_TextBlock.hpp>
namespace pu::ui::elm {
TextBlock::TextBlock(const i32 x, const i32 y, const std::string &text) : Element::Element() {
this->x = x;
this->y = y;
this->clr = DefaultColor;
this->text_tex = nullptr;
this->fnt_name = GetDefaultFont(DefaultFontSize::MediumLarge);
this->SetText(text);
}
TextBlock::~TextBlock() {
render::DeleteTexture(this->text_tex);
}
i32 TextBlock::GetWidth() {
return render::GetTextureWidth(this->text_tex);
}
i32 TextBlock::GetHeight() {
return render::GetTextureHeight(this->text_tex);
}
void TextBlock::SetText(const std::string &text) {
this->text = text;
render::DeleteTexture(this->text_tex);
this->text_tex = render::RenderText(this->fnt_name, text, this->clr);
}
void TextBlock::SetFont(const std::string &font_name) {
this->fnt_name = font_name;
this->SetText(this->text);
}
void TextBlock::SetColor(const Color clr) {
this->clr = clr;
this->SetText(this->text);
}
void TextBlock::OnRender(render::Renderer::Ref &drawer, const i32 x, const i32 y) {
drawer->RenderTexture(this->text_tex, x, y);
}
}

View File

@ -0,0 +1,80 @@
#include <pu/ui/elm/elm_Toggle.hpp>
namespace pu::ui::elm {
Toggle::Toggle(const i32 x, const i32 y, const std::string &content, const u64 toggle_key, const Color clr) : Element::Element() {
this->x = x;
this->y = y;
this->key = toggle_key;
this->clr = clr;
this->cnt_tex = nullptr;
this->fnt_name = GetDefaultFont(DefaultFontSize::MediumLarge);
this->toggle_alpha = 0xFF;
this->checked = false;
this->SetContent(content);
}
Toggle::~Toggle() {
render::DeleteTexture(this->cnt_tex);
}
i32 Toggle::GetWidth() {
return render::GetTextureWidth(this->cnt_tex) + 2 * ContentHorizontalMargin;
}
i32 Toggle::GetHeight() {
return render::GetTextureHeight(this->cnt_tex) + 2 * ContentVerticalMargin;
}
void Toggle::SetContent(const std::string &content) {
this->cnt = content;
render::DeleteTexture(this->cnt_tex);
this->cnt_tex = render::RenderText(this->fnt_name, content, this->clr);
}
void Toggle::SetFont(const std::string &font_name) {
this->fnt_name = font_name;
this->SetContent(this->cnt);
}
void Toggle::SetColor(const Color clr) {
this->clr = clr;
this->SetContent(this->cnt);
}
void Toggle::OnRender(render::Renderer::Ref &drawer, const i32 x, const i32 y) {
const auto bg_width = this->GetWidth();
const auto bg_height = this->GetHeight();
const auto cnt_x = x + ContentHorizontalMargin;
const auto cnt_y = y + ContentVerticalMargin;
if(this->checked) {
drawer->RenderRectangleFill(MakeBackgroundColor(0xFF), x, y, bg_width, bg_height);
if(this->toggle_alpha < 0xFF) {
drawer->RenderRectangleFill(MakeBackgroundColor(0xFF - this->toggle_alpha), x, y, bg_width, bg_height);
this->toggle_alpha += ToggleAlphaIncrement;
}
else {
drawer->RenderRectangleFill(MakeBackgroundColor(0xFF), x, y, bg_width, bg_height);
}
}
else {
drawer->RenderRectangleFill(this->clr, x, y, bg_width, bg_height);
if(this->toggle_alpha > 0)
{
drawer->RenderRectangleFill(MakeBackgroundColor(this->toggle_alpha), x, y, bg_width, bg_height);
this->toggle_alpha -= ToggleAlphaIncrement;
}
else {
drawer->RenderRectangleFill(this->clr, x, y, bg_width, bg_height);
}
}
drawer->RenderTexture(this->cnt_tex, cnt_x, cnt_y);
}
void Toggle::OnInput(const u64 keys_down, const u64 keys_up, const u64 keys_held, const TouchPoint touch_pos) {
if((keys_down & this->key) || ((this->key == TouchPseudoKey) && touch_pos.HitsRegion(this->x, this->y, this->GetWidth(), this->GetHeight()))) {
this->checked = !this->checked;
}
}
}

View File

@ -0,0 +1,38 @@
#include <pu/ui/extras/extras_Toast.hpp>
namespace pu::ui::extras {
Toast::Toast(const std::string &text, const std::string &font_name, const Color text_clr, const Color bg_clr) : Overlay(0, DefaultY, 0, 0, bg_clr) {
this->text = elm::TextBlock::New(0, 0, text);
this->text->SetFont(font_name);
this->text->SetColor(text_clr);
this->text->SetHorizontalAlign(elm::HorizontalAlign::Center);
this->text->SetVerticalAlign(elm::VerticalAlign::Center);
this->AdjustDimensions();
this->Add(this->text);
}
void Toast::AdjustDimensions() {
const auto text_width = this->text->GetWidth();
const auto text_height = this->text->GetHeight();
const auto toast_width = text_width + 2 * HorizontalMargin;
const auto toast_height = text_height * HeightAndTextHeightFactor;
this->SetX((render::ScreenWidth - toast_width) / 2);
this->SetWidth(toast_width);
this->SetHeight(toast_height);
}
void Toast::SetText(const std::string &text) {
this->text->SetText(text);
this->AdjustDimensions();
}
void Toast::OnPreRender(render::Renderer::Ref &drawer) {
drawer->SetBaseRenderAlpha(BaseAlpha);
}
void Toast::OnPostRender(render::Renderer::Ref &drawer) {
drawer->ResetBaseRenderAlpha();
}
}

View File

@ -0,0 +1,349 @@
#include <pu/ui/render/render_Renderer.hpp>
#include <pu/ttf/ttf_Font.hpp>
namespace pu::ui::render {
namespace {
// Global rendering vars
sdl2::Renderer g_Renderer = nullptr;
sdl2::Window g_Window = nullptr;
sdl2::Surface g_WindowSurface = nullptr;
// Global font object
std::vector<std::pair<std::string, std::shared_ptr<ttf::Font>>> g_FontTable;
inline bool DoAddSharedFont(std::shared_ptr<ttf::Font> &font, const PlSharedFontType type) {
// Let's assume pl services are initialized, and return if anything unexpected happens
PlFontData data = {};
if(R_FAILED(plGetSharedFontByType(&data, type))) {
return false;
}
if(!ttf::Font::IsValidFontFaceIndex(font->LoadFromMemory(data.address, data.size, ttf::Font::EmptyFontFaceDisposingFunction))) {
return false;
}
return true;
}
inline bool ExistsFont(const std::string &font_name) {
for(const auto &[name, font]: g_FontTable) {
if(name == font_name) {
return true;
}
}
return false;
}
}
void Renderer::Initialize() {
if(!this->initialized) {
if(this->init_opts.init_romfs) {
this->ok_romfs = R_SUCCEEDED(romfsInit());
}
if(this->init_opts.init_pl) {
// TODO: choose pl service type?
this->ok_pl = R_SUCCEEDED(plInitialize(PlServiceType_User));
}
// TODO: check sdl return errcodes!
SDL_Init(this->init_opts.sdl_flags);
g_Window = SDL_CreateWindow("Plutonium-SDL2", 0, 0, this->init_opts.width, this->init_opts.height, 0);
g_Renderer = SDL_CreateRenderer(g_Window, -1, this->init_opts.sdl_render_flags);
g_WindowSurface = SDL_GetWindowSurface(g_Window);
SDL_SetRenderDrawBlendMode(g_Renderer, SDL_BLENDMODE_BLEND);
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "2");
if(this->init_opts.init_img) {
IMG_Init(this->init_opts.sdl_img_flags);
}
if(this->init_opts.init_ttf) {
TTF_Init();
if(!this->init_opts.default_font_path.empty()) {
for(const auto size: DefaultFontSizes) {
AddDefaultFontFromFile(size, this->init_opts.default_font_path);
}
for(const auto size: this->init_opts.extra_default_font_sizes) {
AddDefaultFontFromFile(size, this->init_opts.default_font_path);
}
}
else {
for(const auto size: DefaultFontSizes) {
AddDefaultFontFromShared(size);
}
for(const auto size: this->init_opts.extra_default_font_sizes) {
AddDefaultFontFromShared(size);
}
}
}
if(this->init_opts.init_mixer) {
Mix_Init(this->init_opts.audio_mixer_flags);
Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, MIX_DEFAULT_CHANNELS, 4096);
}
this->initialized = true;
this->base_a = TextureRenderOptions::NoAlpha;
this->base_x = 0;
this->base_y = 0;
}
}
void Renderer::Finalize() {
if(this->initialized) {
// Close all the fonts before closing TTF
g_FontTable.clear();
if(this->init_opts.init_ttf) {
TTF_Quit();
}
if(this->init_opts.init_img) {
IMG_Quit();
}
if(this->init_opts.init_mixer) {
Mix_CloseAudio();
}
if(this->ok_pl) {
plExit();
}
if(this->ok_romfs) {
romfsExit();
}
SDL_DestroyRenderer(g_Renderer);
SDL_FreeSurface(g_WindowSurface);
SDL_DestroyWindow(g_Window);
SDL_Quit();
this->initialized = false;
}
}
void Renderer::InitializeRender(const Color clr) {
SDL_SetRenderDrawColor(g_Renderer, clr.r, clr.g, clr.b, clr.a);
SDL_RenderClear(g_Renderer);
}
void Renderer::FinalizeRender() {
SDL_RenderPresent(g_Renderer);
}
void Renderer::RenderTexture(sdl2::Texture texture, const i32 x, const i32 y, const TextureRenderOptions opts) {
if(texture == nullptr) {
return;
}
SDL_Rect pos = {
.x = x + this->base_x,
.y = y + this->base_y
};
if(opts.width != TextureRenderOptions::NoWidth) {
pos.w = opts.width;
}
else {
SDL_QueryTexture(texture, nullptr, nullptr, &pos.w, nullptr);
}
if(opts.height != TextureRenderOptions::NoHeight) {
pos.h = opts.height;
}
else {
SDL_QueryTexture(texture, nullptr, nullptr, nullptr, &pos.h);
}
float angle = 0;
if(opts.rot_angle != TextureRenderOptions::NoRotation) {
angle = opts.rot_angle;
}
if(opts.alpha_mod != TextureRenderOptions::NoAlpha) {
SetAlphaValue(texture, static_cast<u8>(opts.alpha_mod));
}
if(this->base_a >= 0) {
SetAlphaValue(texture, static_cast<u8>(this->base_a));
}
SDL_RenderCopyEx(g_Renderer, texture, nullptr, &pos, angle, nullptr, SDL_FLIP_NONE);
}
void Renderer::RenderRectangle(const Color clr, const i32 x, const i32 y, const i32 width, const i32 height) {
const SDL_Rect rect = {
.x = x + this->base_x,
.y = y + this->base_y,
.w = width,
.h = height
};
SDL_SetRenderDrawColor(g_Renderer, clr.r, clr.g, clr.b, this->GetActualAlpha(clr.a));
SDL_RenderDrawRect(g_Renderer, &rect);
}
void Renderer::RenderRectangleFill(const Color clr, const i32 x, const i32 y, const i32 width, const i32 height) {
const SDL_Rect rect = {
.x = x + this->base_x,
.y = y + this->base_y,
.w = width,
.h = height
};
SDL_SetRenderDrawColor(g_Renderer, clr.r, clr.g, clr.b, this->GetActualAlpha(clr.a));
SDL_RenderFillRect(g_Renderer, &rect);
}
void Renderer::RenderRoundedRectangle(const Color clr, const i32 x, const i32 y, const i32 width, const i32 height, const i32 radius) {
auto proper_radius = radius;
if((2 * proper_radius) > width) {
proper_radius = width / 2;
}
if((2 * proper_radius) > height) {
proper_radius = height / 2;
}
roundedRectangleRGBA(g_Renderer, x + this->base_x, y + this->base_y, x + this->base_x + width, y + this->base_y + height, proper_radius, clr.r, clr.g, clr.b, this->GetActualAlpha(clr.a));
SDL_SetRenderDrawBlendMode(g_Renderer, SDL_BLENDMODE_BLEND);
}
void Renderer::RenderRoundedRectangleFill(const Color clr, const i32 x, const i32 y, const i32 width, const i32 height, const i32 radius) {
auto proper_radius = radius;
if((2 * proper_radius) > width) {
proper_radius = width / 2;
}
if((2 * proper_radius) > height) {
proper_radius = height / 2;
}
roundedBoxRGBA(g_Renderer, x + this->base_x, y + this->base_y, x + this->base_x + width, y + this->base_y + height, proper_radius, clr.r, clr.g, clr.b, this->GetActualAlpha(clr.a));
SDL_SetRenderDrawBlendMode(g_Renderer, SDL_BLENDMODE_BLEND);
}
void Renderer::RenderCircle(const Color clr, const i32 x, const i32 y, const i32 radius) {
circleRGBA(g_Renderer, x + this->base_x, y + this->base_y, radius - 1, clr.r, clr.g, clr.b, this->GetActualAlpha(clr.a));
aacircleRGBA(g_Renderer, x + this->base_x, y + this->base_y, radius - 1, clr.r, clr.g, clr.b, this->GetActualAlpha(clr.a));
}
void Renderer::RenderCircleFill(const Color clr, const i32 x, const i32 y, const i32 radius) {
filledCircleRGBA(g_Renderer, x + this->base_x, y + this->base_y, radius - 1, clr.r, clr.g, clr.b, this->GetActualAlpha(clr.a));
aacircleRGBA(g_Renderer, x + this->base_x, y + this->base_y, radius - 1, clr.r, clr.g, clr.b, this->GetActualAlpha(clr.a));
}
void Renderer::RenderShadowSimple(const i32 x, const i32 y, const i32 width, const i32 height, const i32 base_alpha, const u8 main_alpha) {
auto crop = false;
auto shadow_width = width;
auto shadow_x = x;
auto shadow_y = y;
for(auto cur_a = base_alpha; cur_a > 0; cur_a -= (180 / height)) {
const Color shadow_clr = { 130, 130, 130, static_cast<u8>(cur_a * (main_alpha / 0xFF)) };
this->RenderRectangleFill(shadow_clr, shadow_x + this->base_x, shadow_y + this->base_y, shadow_width, 1);
if(crop) {
shadow_width -= 2;
shadow_x++;
}
crop = !crop;
shadow_y++;
}
}
sdl2::Renderer GetMainRenderer() {
return g_Renderer;
}
sdl2::Window GetMainWindow() {
return g_Window;
}
sdl2::Surface GetMainSurface() {
return g_WindowSurface;
}
std::pair<u32, u32> GetDimensions() {
i32 w = 0;
i32 h = 0;
SDL_GetWindowSize(g_Window, &w, &h);
return { static_cast<u32>(w), static_cast<u32>(h) };
}
bool AddSharedFont(const std::string &font_name, const u32 font_size, const PlSharedFontType type) {
if(ExistsFont(font_name)) {
return false;
}
auto font = std::make_shared<ttf::Font>(font_size);
if(!DoAddSharedFont(font, type)) {
return false;
}
g_FontTable.push_back(std::make_pair(font_name, std::move(font)));
return true;
}
bool AddAllSharedFonts(const std::string &font_name, const u32 font_size) {
if(ExistsFont(font_name)) {
return false;
}
auto font = std::make_shared<ttf::Font>(font_size);
if(!DoAddSharedFont(font, PlSharedFontType_Standard)) {
return false;
}
if(!DoAddSharedFont(font, PlSharedFontType_NintendoExt)) {
return false;
}
if(!DoAddSharedFont(font, PlSharedFontType_ChineseSimplified)) {
return false;
}
if(!DoAddSharedFont(font, PlSharedFontType_ExtChineseSimplified)) {
return false;
}
if(!DoAddSharedFont(font, PlSharedFontType_ChineseTraditional)) {
return false;
}
if(!DoAddSharedFont(font, PlSharedFontType_KO)) {
return false;
}
g_FontTable.push_back(std::make_pair(font_name, std::move(font)));
return true;
}
bool AddFontFile(const std::string &font_name, const u32 font_size, const std::string &path) {
if(ExistsFont(font_name)) {
return false;
}
auto font = std::make_shared<ttf::Font>(font_size);
if(!ttf::Font::IsValidFontFaceIndex(font->LoadFromFile(path))) {
return false;
}
g_FontTable.push_back(std::make_pair(font_name, std::move(font)));
return true;
}
sdl2::Texture RenderText(const std::string &font_name, const std::string &text, const Color clr) {
for(auto &[name, font]: g_FontTable) {
if(name == font_name) {
return font->RenderText(text, clr);
}
}
return nullptr;
}
i32 GetTextWidth(const std::string &font_name, const std::string &text) {
for(auto &[name, font]: g_FontTable) {
if(name == font_name) {
const auto [w, _] = font->GetTextDimensions(text);
return static_cast<i32>(w);
}
}
return 0;
}
i32 GetTextHeight(const std::string &font_name, const std::string &text) {
for(auto &[name, font]: g_FontTable) {
if(name == font_name) {
const auto [_, h] = font->GetTextDimensions(text);
return static_cast<i32>(h);
}
}
return 0;
}
}

View File

@ -0,0 +1,56 @@
#include <pu/ui/render/render_SDL2.hpp>
#include <pu/ui/render/render_Renderer.hpp>
namespace pu::ui::render {
sdl2::Texture ConvertToTexture(sdl2::Surface surface) {
if(surface == nullptr) {
return nullptr;
}
auto tex = SDL_CreateTextureFromSurface(GetMainRenderer(), surface);
SDL_FreeSurface(surface);
return tex;
}
sdl2::Texture LoadImage(const std::string &path) {
return ConvertToTexture(IMG_Load(path.c_str()));
}
i32 GetTextureWidth(sdl2::Texture texture) {
if(texture == nullptr) {
return 0;
}
i32 w = 0;
SDL_QueryTexture(texture, nullptr, nullptr, &w, nullptr);
return w;
}
i32 GetTextureHeight(sdl2::Texture texture) {
if(texture == nullptr) {
return 0;
}
i32 h = 0;
SDL_QueryTexture(texture, nullptr, nullptr, nullptr, &h);
return h;
}
void SetAlphaValue(sdl2::Texture texture, const u8 alpha) {
if(texture == nullptr) {
return;
}
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
SDL_SetTextureAlphaMod(texture, alpha);
}
void DeleteTexture(sdl2::Texture &texture) {
if(texture != nullptr) {
SDL_DestroyTexture(texture);
texture = nullptr;
}
}
}

View File

@ -0,0 +1,212 @@
#include <pu/ui/ui_Application.hpp>
namespace pu::ui {
Application::Application(render::Renderer::Ref renderer) {
this->renderer = renderer;
// TODO: do it outside ctor, get result...?
this->renderer->Initialize();
this->is_shown = false;
this->on_ipt_cb = {};
this->in_render_over = false;
this->ovl = nullptr;
this->lyt = nullptr;
this->loaded = false;
this->render_over_fn = {};
this->fade_alpha = 0xFF;
this->fade_alpha_increment = DefaultFadeAlphaIncrement;
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
padInitializeDefault(&this->input_pad);
}
void Application::Prepare() {
if(!this->loaded) {
this->OnLoad();
this->loaded = true;
}
}
i32 Application::CreateShowDialog(const std::string &title, const std::string &content, const std::vector<std::string> &opts, const bool use_last_opt_as_cancel, const std::string &icon_path) {
auto dialog = Dialog::New(title, content);
for(u32 i = 0; i < opts.size(); i++) {
const auto &opt = opts.at(i);
if(use_last_opt_as_cancel && (i == (opts.size() - 1))) {
dialog->SetCancelOption(opt);
}
else {
dialog->AddOption(opt);
}
}
if(!icon_path.empty()) {
dialog->SetIcon(icon_path);
}
const auto opt = this->ShowDialog(dialog);
if(dialog->UserCancelled()) {
return -1;
}
else if(!dialog->IsOk()) {
return -2;
}
else {
return opt;
}
}
void Application::StartOverlayWithTimeout(Overlay::Ref ovl, const u64 ms) {
if(this->ovl == nullptr) {
this->ovl = ovl;
this->ovl_timeout_ms = ms;
this->ovl_start_time = std::chrono::steady_clock::now();
}
}
void Application::EndOverlay() {
if(this->ovl != nullptr) {
this->ovl->NotifyEnding(false);
this->ovl_timeout_ms = 0;
this->ovl = nullptr;
}
}
void Application::Show() {
if(!this->CanBeShown()) {
return;
}
this->is_shown = true;
while(this->is_shown) {
this->CallForRender();
}
}
bool Application::CallForRender() {
if(!this->CanBeShown()) {
return false;
}
auto continue_render = true;
this->renderer->InitializeRender(this->lyt->GetBackgroundColor());
this->OnRender();
if(this->in_render_over) {
continue_render = (this->render_over_fn)(this->renderer);
this->in_render_over = false;
this->render_over_fn = {};
}
this->renderer->FinalizeRender();
return continue_render;
}
bool Application::CallForRenderWithRenderOver(RenderOverFunction render_over_fn) {
this->in_render_over = true;
this->render_over_fn = render_over_fn;
return this->CallForRender();
}
void Application::FadeIn() {
this->fade_alpha = 0;
while(true) {
this->CallForRender();
this->fade_alpha += this->fade_alpha_increment;
if(this->fade_alpha > 0xFF) {
this->fade_alpha = 0xFF;
this->CallForRender();
break;
}
}
}
void Application::FadeOut() {
this->fade_alpha = 0xFF;
while(true) {
this->CallForRender();
this->fade_alpha -= this->fade_alpha_increment;
if(this->fade_alpha < 0) {
this->fade_alpha = 0;
this->CallForRender();
break;
}
}
}
void Application::OnRender() {
padUpdate(&this->input_pad);
const auto keys_down = this->GetButtonsDown();
const auto keys_up = this->GetButtonsUp();
const auto keys_held = this->GetButtonsHeld();
const auto tch_state = this->GetTouchState();
TouchPoint tch_pos = {};
if(tch_state.count > 0) {
tch_pos = { tch_state.touches[0].x, tch_state.touches[0].y };
}
const auto sim_tch_pos = this->lyt->ConsumeSimulatedTouchPosition();
if(!sim_tch_pos.IsEmpty()) {
tch_pos = sim_tch_pos;
}
for(auto &render_cb: this->render_cbs) {
if(render_cb) {
render_cb();
}
}
this->lyt->PreRender();
for(auto &lyt_render_cb: this->lyt->GetRenderCallbacks()) {
if(lyt_render_cb) {
lyt_render_cb();
}
}
if(!this->in_render_over) {
if(this->on_ipt_cb) {
(this->on_ipt_cb)(keys_down, keys_up, keys_held, tch_pos);
}
}
if(this->lyt->HasBackgroundImage()) {
this->renderer->RenderTexture(this->lyt->GetBackgroundImageTexture(), 0, 0);
}
if(!this->in_render_over) {
auto lyt_on_ipt_cb = this->lyt->GetOnInput();
if(lyt_on_ipt_cb) {
lyt_on_ipt_cb(keys_down, keys_up, keys_held, tch_pos);
}
}
for(u32 i = 0; i < this->lyt->GetCount(); i++) {
auto elm = this->lyt->At(i);
if(elm->IsVisible()) {
elm->OnRender(this->renderer, elm->GetProcessedX(), elm->GetProcessedY());
if(!this->in_render_over) {
elm->OnInput(keys_down, keys_up, keys_held, tch_pos);
}
}
}
if(this->ovl != nullptr) {
const auto ovl_continue_render = this->ovl->Render(this->renderer);
if(this->ovl_timeout_ms > 0) {
const auto time_now = std::chrono::steady_clock::now();
const u64 elapsed_time_ms = std::chrono::duration_cast<std::chrono::milliseconds>(time_now - this->ovl_start_time).count();
if(elapsed_time_ms >= this->ovl_timeout_ms) {
this->ovl->NotifyEnding(true);
}
}
if(!ovl_continue_render) {
this->EndOverlay();
}
}
this->renderer->RenderRectangleFill({ 0, 0, 0, static_cast<u8>(0xFF - this->fade_alpha) }, 0, 0, render::ScreenWidth, render::ScreenHeight);
}
void Application::Close() {
this->is_shown = false;
this->renderer->Finalize();
}
}

View File

@ -0,0 +1,11 @@
#include <pu/ui/ui_Container.hpp>
namespace pu::ui {
void Container::PreRender() {
for(auto &elm : this->elems) {
elm->SetParentContainer(this);
}
}
}

View File

@ -0,0 +1,250 @@
#include <pu/ui/ui_Dialog.hpp>
#include <pu/ui/ui_Application.hpp>
namespace pu::ui {
Dialog::Dialog(const std::string &title, const std::string &content) {
this->title_font_name = GetDefaultFont(DefaultFontSize::Large);
this->cnt_font_name = GetDefaultFont(DefaultFontSize::Medium);
this->opt_font_name = GetDefaultFont(DefaultFontSize::Small);
this->title = title;
this->cnt = content;
this->title_tex = render::RenderText(this->title_font_name, title, DefaultTitleColor);
this->cnt_tex = render::RenderText(this->cnt_font_name, content, DefaultContentColor);
this->icon_tex = nullptr;
this->selected_opt_idx = 0;
this->prev_selected_opt_idx = 0;
this->selected_opt_over_alpha = 0xFF;
this->prev_selected_opt_over_alpha = 0;
this->user_cancelled = false;
}
Dialog::~Dialog() {
render::DeleteTexture(this->title_tex);
render::DeleteTexture(this->cnt_tex);
render::DeleteTexture(this->icon_tex);
for(auto &opt_tex: this->opt_texs) {
render::DeleteTexture(opt_tex);
}
}
void Dialog::AddOption(const std::string &opt_name) {
this->opts.push_back(opt_name);
this->opt_texs.push_back(render::RenderText(this->opt_font_name, opt_name, DefaultOptionColor));
}
void Dialog::SetIcon(const std::string &icon_path) {
render::DeleteTexture(this->icon_tex);
this->icon_tex = render::LoadImage(icon_path);
}
i32 Dialog::Show(Application *app_ref) {
if(this->HasCancelOption()) {
this->AddOption(this->cancel_opt);
}
if(this->opt_texs.empty()) {
return 0;
}
auto opts_width = (SpaceBetweenOptions * (this->opt_texs.size() - 1)) + 2 * OptionsBaseHorizontalMargin;
for(const auto &opt_tex : this->opt_texs) {
const auto opt_width = render::GetTextureWidth(opt_tex) + 2 * OptionHorizontalMargin;
opts_width += opt_width;
}
auto dialog_width = opts_width;
const auto cnt_width = render::GetTextureWidth(this->cnt_tex) + ContentExtraWidth;
if(cnt_width > dialog_width) {
dialog_width = cnt_width;
}
const auto title_width = render::GetTextureWidth(this->title_tex) + TitleExtraWidth;
if(title_width > dialog_width) {
dialog_width = title_width;
}
const auto title_cnt_height = TitleTopMargin + render::GetTextureHeight(this->title_tex) + render::GetTextureHeight(this->cnt_tex) + SpaceBetweenContentAndOptions;
auto opt_base_y = title_cnt_height;
if(this->HasIcon()) {
const auto icon_height = render::GetTextureHeight(this->icon_tex) + 2 * IconMargin;
if(icon_height > opt_base_y) {
opt_base_y = icon_height;
}
const auto icon_width = render::GetTextureWidth(this->icon_tex) + 2 * IconMargin;
const auto icon_title_width = title_width + icon_width;
if(icon_title_width > dialog_width) {
dialog_width = icon_title_width;
}
const auto icon_cnt_width = cnt_width + icon_width;
if(icon_cnt_width > dialog_width) {
dialog_width = icon_cnt_width;
}
const auto icon_opts_width = opts_width + icon_width;
if(icon_opts_width > dialog_width) {
dialog_width = icon_opts_width;
}
}
if(dialog_width > render::ScreenWidth) {
dialog_width = render::ScreenWidth;
}
auto dialog_height = opt_base_y + OptionHeight + OptionBottomMargin;
if(dialog_height > render::ScreenHeight) {
dialog_height = render::ScreenHeight;
}
const auto dialog_x = (render::ScreenWidth - dialog_width) / 2;
const auto dialog_y = (render::ScreenHeight - dialog_height) / 2;
opt_base_y += dialog_y;
auto is_finishing = false;
i32 initial_fade_alpha = 0;
while(true) {
const auto ok = app_ref->CallForRenderWithRenderOver([&](render::Renderer::Ref &drawer) -> bool {
const auto keys_down = app_ref->GetButtonsDown();
const auto tch_state = app_ref->GetTouchState();
const TouchPoint tch_pos = { tch_state.touches[0].x, tch_state.touches[0].y };
if(keys_down & HidNpadButton_AnyLeft) {
if(this->selected_opt_idx > 0) {
this->prev_selected_opt_idx = this->selected_opt_idx;
this->selected_opt_idx--;
this->selected_opt_over_alpha = 0;
this->prev_selected_opt_over_alpha = 0xFF;
}
}
else if(keys_down & HidNpadButton_AnyRight) {
if(this->selected_opt_idx < (this->opt_texs.size() - 1)) {
this->prev_selected_opt_idx = this->selected_opt_idx;
this->selected_opt_idx++;
this->selected_opt_over_alpha = 0;
this->prev_selected_opt_over_alpha = 0xFF;
}
}
else if(keys_down & HidNpadButton_A) {
this->user_cancelled = false;
is_finishing = true;
}
else if(keys_down & HidNpadButton_B) {
this->user_cancelled = true;
is_finishing = true;
}
if(tch_state.count > 0) {
auto cur_opt_x = dialog_x + OptionsBaseHorizontalMargin;
for(u32 i = 0; i < this->opts.size(); i++) {
auto &opt_tex = this->opt_texs.at(i);
const auto opt_name_width = render::GetTextureWidth(opt_tex);
const auto opt_width = opt_name_width + 2 * OptionHorizontalMargin;
if(tch_pos.HitsRegion(cur_opt_x, opt_base_y, opt_width, OptionHeight)) {
this->selected_opt_idx = i;
this->user_cancelled = false;
is_finishing = true;
break;
}
cur_opt_x += opt_width + SpaceBetweenOptions;
}
}
const auto dialog_clr = MakeDialogColor(static_cast<u8>(initial_fade_alpha));
auto screen_fade_alpha = initial_fade_alpha;
if(screen_fade_alpha < 0) {
screen_fade_alpha = 0;
}
if(screen_fade_alpha > MaxScreenFadeAlpha) {
screen_fade_alpha = MaxScreenFadeAlpha;
}
const Color screen_fade_clr = { 0, 0, 0, static_cast<u8>(screen_fade_alpha) };
drawer->RenderRectangleFill(screen_fade_clr, 0, 0, render::ScreenWidth, render::ScreenHeight);
drawer->RenderRoundedRectangleFill(dialog_clr, dialog_x, dialog_y, dialog_width, dialog_height, DialogBorderRadius);
render::SetAlphaValue(this->title_tex, initial_fade_alpha);
render::SetAlphaValue(this->cnt_tex, initial_fade_alpha);
drawer->RenderTexture(this->title_tex, dialog_x + TitleX, dialog_y + TitleY);
drawer->RenderTexture(this->cnt_tex, dialog_x + ContentX, dialog_y + ContentY);
if(this->HasIcon()) {
const auto icon_width = render::GetTextureWidth(this->icon_tex);
const auto icon_x = dialog_x + (dialog_width - (icon_width + 2 * IconMargin));
const auto icon_y = dialog_y + IconMargin;
drawer->RenderTexture(this->icon_tex, icon_x, icon_y, render::TextureRenderOptions::WithCustomAlpha(static_cast<u8>(initial_fade_alpha)));
}
auto cur_opt_x = dialog_x + OptionsBaseHorizontalMargin;
for(u32 i = 0; i < this->opt_texs.size(); i++) {
auto &opt_tex = this->opt_texs.at(i);
const auto opt_name_width = render::GetTextureWidth(opt_tex);
const auto opt_name_height = render::GetTextureHeight(opt_tex);
const auto opt_width = opt_name_width + 2 * OptionHorizontalMargin;
const auto opt_name_x = cur_opt_x + OptionHorizontalMargin;
const auto opt_name_y = opt_base_y + ((OptionHeight - opt_name_height) / 2);
if(this->selected_opt_idx == i) {
if(this->selected_opt_over_alpha < 0xFF) {
const auto over_clr = MakeOverColor(static_cast<u8>(this->selected_opt_over_alpha));
drawer->RenderRoundedRectangleFill(over_clr, cur_opt_x, opt_base_y, opt_width, OptionHeight, OptionBorderRadius);
this->selected_opt_over_alpha += OverAlphaIncrement;
}
else {
this->selected_opt_over_alpha = 0xFF;
const auto over_clr = MakeOverColor(static_cast<u8>(initial_fade_alpha));
drawer->RenderRoundedRectangleFill(over_clr, cur_opt_x, opt_base_y, opt_width, OptionHeight, OptionBorderRadius);
}
}
else if(this->prev_selected_opt_idx == static_cast<i32>(i)) {
if(this->prev_selected_opt_over_alpha > 0) {
const auto over_clr = MakeOverColor(static_cast<u8>(this->prev_selected_opt_over_alpha));
drawer->RenderRoundedRectangleFill(over_clr, cur_opt_x, opt_base_y, opt_width, OptionHeight, OptionBorderRadius);
this->prev_selected_opt_over_alpha -= OverAlphaIncrement;
}
else {
this->prev_selected_opt_over_alpha = 0;
}
}
render::SetAlphaValue(opt_tex, static_cast<u8>(initial_fade_alpha));
drawer->RenderTexture(opt_tex, opt_name_x, opt_name_y);
cur_opt_x += opt_width + SpaceBetweenOptions;
}
if(is_finishing) {
if(initial_fade_alpha == 0) {
return false;
}
if(initial_fade_alpha > 0) {
initial_fade_alpha -= FadeAlphaIncrement;
}
if(initial_fade_alpha < 0) {
initial_fade_alpha = 0;
}
}
else {
if(initial_fade_alpha < 0xFF) {
initial_fade_alpha += FadeAlphaIncrement;
}
if(initial_fade_alpha > 0xFF) {
initial_fade_alpha = 0xFF;
}
}
return true;
});
if(!ok) {
app_ref->CallForRenderWithRenderOver([](render::Renderer::Ref&) -> bool { return false; });
break;
}
}
return this->selected_opt_idx;
}
}

View File

@ -0,0 +1,27 @@
#include <pu/ui/ui_Layout.hpp>
namespace pu::ui {
Layout::~Layout() {
render::DeleteTexture(this->over_bg_tex);
}
void Layout::SetBackgroundImage(const std::string &path) {
render::DeleteTexture(this->over_bg_tex);
this->has_image = true;
this->over_bg_tex = render::LoadImage(path);
}
void Layout::SetBackgroundColor(const Color clr) {
render::DeleteTexture(this->over_bg_tex);
this->has_image = false;
this->over_bg_color = clr;
}
TouchPoint Layout::ConsumeSimulatedTouchPosition() {
auto touch_pos_copy = this->sim_touch_pos;
this->sim_touch_pos = {};
return touch_pos_copy;
}
}

View File

@ -0,0 +1,46 @@
#include <pu/ui/ui_Overlay.hpp>
namespace pu::ui {
bool Overlay::Render(render::Renderer::Ref &drawer) {
this->OnPreRender(drawer);
drawer->SetBaseRenderAlpha(static_cast<u8>(this->fade_a));
if(this->round) {
drawer->RenderRoundedRectangleFill(this->bg_clr, this->x, this->y, this->w, this->h, this->rad);
}
else {
drawer->RenderRectangleFill(this->bg_clr, this->x, this->y, this->w, this->h);
}
this->PreRender();
for(auto &elem: this->elems) {
if(elem->IsVisible()) {
elem->OnRender(drawer, elem->GetProcessedX(), elem->GetProcessedY());
}
}
drawer->ResetBaseRenderAlpha();
if(this->is_ending) {
if(this->fade_a > 0) {
this->fade_a -= FadeAlphaVariation;
}
else {
this->fade_a = 0;
}
}
else {
if(this->fade_a < MaxFadeAlpha) {
this->fade_a += FadeAlphaVariation;
}
else {
this->fade_a = MaxFadeAlpha;
}
}
this->OnPostRender(drawer);
const auto is_finished = this->is_ending && (this->fade_a == 0);
if(is_finished) {
this->is_ending = false;
}
return !is_finished;
}
}

View File

@ -0,0 +1,30 @@
#include <pu/ui/ui_Types.hpp>
namespace pu::ui {
Color Color::FromHex(const std::string &str_clr) {
// Format: '#rrggbbaa'
std::string r = "00";
std::string g = "00";
std::string b = "00";
std::string a = "FF";
if(str_clr.length() >= 9) {
a = str_clr.substr(7, 2);
}
if(str_clr.length() >= 7)
{
r = str_clr.substr(1, 2);
g = str_clr.substr(3, 2);
b = str_clr.substr(5, 2);
}
const auto r_val = static_cast<u8>(std::stoul(r, nullptr, 16));
const auto g_val = static_cast<u8>(std::stoul(g, nullptr, 16));
const auto b_val = static_cast<u8>(std::stoul(b, nullptr, 16));
const auto a_val = static_cast<u8>(std::stoul(a, nullptr, 16));
return { r_val, g_val, b_val, a_val };
}
}