ini
This commit is contained in:
parent
f7f0d6e5be
commit
83d51d3e83
30 changed files with 1186 additions and 208 deletions
|
|
@ -76,7 +76,7 @@ BreakBeforeTernaryOperators: true
|
||||||
BreakConstructorInitializers: AfterColon
|
BreakConstructorInitializers: AfterColon
|
||||||
BreakInheritanceList: BeforeColon
|
BreakInheritanceList: BeforeColon
|
||||||
BreakStringLiterals: true
|
BreakStringLiterals: true
|
||||||
ColumnLimit: 400
|
ColumnLimit: 120
|
||||||
CommentPragmas: "^ IWYU pragma:"
|
CommentPragmas: "^ IWYU pragma:"
|
||||||
CompactNamespaces: false
|
CompactNamespaces: false
|
||||||
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||||
|
|
@ -157,7 +157,7 @@ RemoveBracesLLVM: false
|
||||||
RequiresClausePosition: OwnLine
|
RequiresClausePosition: OwnLine
|
||||||
SeparateDefinitionBlocks: Leave
|
SeparateDefinitionBlocks: Leave
|
||||||
ShortNamespaceLines: 1
|
ShortNamespaceLines: 1
|
||||||
SortIncludes: CaseSensitive
|
SortIncludes: Never
|
||||||
SortJavaStaticImport: Before
|
SortJavaStaticImport: Before
|
||||||
SortUsingDeclarations: true
|
SortUsingDeclarations: true
|
||||||
SpaceAfterCStyleCast: true
|
SpaceAfterCStyleCast: true
|
||||||
|
|
|
||||||
|
|
@ -26,4 +26,5 @@ add_subdirectory(Graphics)
|
||||||
add_subdirectory(RayTracer)
|
add_subdirectory(RayTracer)
|
||||||
add_subdirectory(DataAnalysis)
|
add_subdirectory(DataAnalysis)
|
||||||
add_subdirectory(Objects)
|
add_subdirectory(Objects)
|
||||||
|
add_subdirectory(Widgets)
|
||||||
add_subdirectory(LibraryViewer)
|
add_subdirectory(LibraryViewer)
|
||||||
|
|
|
||||||
|
|
@ -42,10 +42,7 @@ public:
|
||||||
|
|
||||||
Server::Server() { mContext = new ServerContext(); }
|
Server::Server() { mContext = new ServerContext(); }
|
||||||
|
|
||||||
Server::~Server() {
|
Server::~Server() { delete mContext; }
|
||||||
delete mContext;
|
|
||||||
assert(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Server::start(ualni port) {
|
void Server::start(ualni port) {
|
||||||
mContext->socket.open(asio::ip::tcp::v4());
|
mContext->socket.open(asio::ip::tcp::v4());
|
||||||
|
|
@ -61,29 +58,23 @@ Server::Socket Server::accept() {
|
||||||
return clientSocket;
|
return clientSocket;
|
||||||
}
|
}
|
||||||
|
|
||||||
int1* Server::read(Socket client) {
|
bool Server::read(Socket client, int1* message, halni messageSize) {
|
||||||
short messageSize;
|
try {
|
||||||
if (asio::read(*(ServerContext::Socket*) client, asio::buffer(&messageSize, 2)) == -1) {
|
std::size_t bytesRead = asio::read(*(ServerContext::Socket*) client, asio::buffer(message, messageSize));
|
||||||
std::cerr << "Failed to read from socket" << std::endl;
|
return bytesRead == messageSize;
|
||||||
((ServerContext::Socket*) client)->close();
|
} catch (const std::exception& e) {
|
||||||
return nullptr;
|
std::cerr << "Error during read: " << e.what() << "\n";
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
auto message = new char[messageSize + 1];
|
|
||||||
message[messageSize] = '\0';
|
|
||||||
if (asio::read(*(ServerContext::Socket*) client, asio::buffer(message, messageSize)) == -1) {
|
|
||||||
std::cerr << "Failed to read from socket" << std::endl;
|
|
||||||
memcpy(message, "Client wanna say something but i cant read", 100);
|
|
||||||
}
|
|
||||||
return message;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server::write(Socket client, const char* message) {
|
bool Server::write(Socket client, const int1* message, halni messageSize) {
|
||||||
auto messageSize = (short) strlen(message);
|
try {
|
||||||
if (asio::write(*(ServerContext::Socket*) client, asio::buffer(&messageSize, 2)) == -1) {
|
std::size_t bytesWritten = asio::write(*(ServerContext::Socket*) client, asio::buffer(message, messageSize));
|
||||||
std::cerr << "Failed to write to socket" << std::endl;
|
return bytesWritten == messageSize;
|
||||||
}
|
} catch (const std::exception& e) {
|
||||||
if (asio::write(*(ServerContext::Socket*) client, asio::buffer(message, messageSize)) == -1) {
|
std::cerr << "Error during write: " << e.what() << "\n";
|
||||||
std::cerr << "Failed to write to socket" << std::endl;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -91,37 +82,35 @@ void Server::write(Socket client, const char* message) {
|
||||||
|
|
||||||
Client::Client() { mContext = new ClientContext(); }
|
Client::Client() { mContext = new ClientContext(); }
|
||||||
|
|
||||||
Client::~Client() {
|
Client::~Client() { delete mContext; }
|
||||||
delete mContext;
|
|
||||||
assert(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Client::connect(const char* IP, ualni PORT) {
|
bool Client::connect(const char* IP, ualni PORT) {
|
||||||
|
try {
|
||||||
asio::ip::tcp::endpoint endpoint(asio::ip::address::from_string(IP), PORT);
|
asio::ip::tcp::endpoint endpoint(asio::ip::address::from_string(IP), PORT);
|
||||||
mContext->socket.connect(endpoint);
|
mContext->socket.connect(endpoint);
|
||||||
|
return true;
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
std::cerr << "Error during read: " << e.what() << "\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
char* Client::read() {
|
bool Client::read(int1* buff, halni size) {
|
||||||
short messageSize;
|
try {
|
||||||
if (asio::read(mContext->socket, asio::buffer(&messageSize, 2)) == -1) {
|
std::size_t bytesRead = asio::read(mContext->socket, asio::buffer(buff, size));
|
||||||
std::cerr << "Failed to read from socket" << std::endl;
|
return bytesRead == size;
|
||||||
return nullptr;
|
} catch (const std::exception& e) {
|
||||||
|
std::cerr << "Error during read: " << e.what() << "\n";
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
auto message = new char[messageSize + 1];
|
|
||||||
message[messageSize] = '\0';
|
|
||||||
if (asio::read(mContext->socket, asio::buffer(message, messageSize)) == -1) {
|
|
||||||
std::cerr << "Failed to read from socket" << std::endl;
|
|
||||||
memcpy(message, "Cant wanna say something but i cant read", 100);
|
|
||||||
}
|
|
||||||
return message;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::write(const char* message) {
|
bool Client::write(const int1* message, halni messageSize) {
|
||||||
auto messageSize = (short) strlen(message);
|
try {
|
||||||
if (asio::write(mContext->socket, asio::buffer(&messageSize, 2)) == -1) {
|
std::size_t bytesWritten = asio::write(mContext->socket, asio::buffer(message, messageSize));
|
||||||
std::cerr << "Failed to write to socket" << std::endl;
|
return bytesWritten == messageSize;
|
||||||
}
|
} catch (const std::exception& e) {
|
||||||
if (asio::write(mContext->socket, asio::buffer(message, messageSize)) == -1) {
|
std::cerr << "Error during write: " << e.what() << "\n";
|
||||||
std::cerr << "Failed to write to socket" << std::endl;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,8 @@ namespace tp {
|
||||||
|
|
||||||
void start(ualni port);
|
void start(ualni port);
|
||||||
Socket accept();
|
Socket accept();
|
||||||
static int1* read(Socket client);
|
bool read(Socket client, int1* message, halni size);
|
||||||
static void write(Socket client, const int1* message);
|
bool write(Socket client, const int1* message, halni size);
|
||||||
};
|
};
|
||||||
|
|
||||||
class Client {
|
class Client {
|
||||||
|
|
@ -29,8 +29,8 @@ namespace tp {
|
||||||
Client();
|
Client();
|
||||||
~Client();
|
~Client();
|
||||||
|
|
||||||
void connect(const int1* IP, ualni PORT);
|
bool connect(const int1* IP, ualni PORT);
|
||||||
int1* read();
|
bool read(int1* buff, halni size);
|
||||||
void write(const int1* message);
|
bool write(const int1* message, halni size);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
2
Externals/imgui
vendored
2
Externals/imgui
vendored
|
|
@ -1 +1 @@
|
||||||
Subproject commit 4144d7d772a800f08693b8b13f7c81f1f22a73c4
|
Subproject commit 1109de38277fd2d14d4dca4c1cb8d4a2c4ff0f95
|
||||||
|
|
@ -32,13 +32,9 @@ void Graphics::Canvas::init() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Graphics::Canvas::deinit() {
|
void Graphics::Canvas::deinit() { nvgDeleteGL3(mContext->vg); }
|
||||||
nvgDeleteGL3(mContext->vg);
|
|
||||||
}
|
|
||||||
|
|
||||||
RectF Graphics::Canvas::getAvaliableArea() {
|
RectF Graphics::Canvas::getAvaliableArea() { return { (halnf) 0, (halnf) 0, mWidth, mHeight }; }
|
||||||
return { (halnf) 0, (halnf) 0, mWidth, mHeight };
|
|
||||||
}
|
|
||||||
|
|
||||||
void Graphics::Canvas::rect(const RectF& rec, const RGBA& col, halnf round) {
|
void Graphics::Canvas::rect(const RectF& rec, const RGBA& col, halnf round) {
|
||||||
nvgBeginPath(mContext->vg);
|
nvgBeginPath(mContext->vg);
|
||||||
|
|
@ -53,10 +49,33 @@ void Graphics::Canvas::rect(const RectF& rec, const RGBA& col, halnf round) {
|
||||||
nvgFill(mContext->vg);
|
nvgFill(mContext->vg);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Graphics::Canvas::text(const String& string, const RectF& aRec, halnf size, Align align, halnf marging, const RGBA& col) {
|
void Graphics::Canvas::pushClamp(const RectF& rec) {
|
||||||
|
RectF intersection = rec;
|
||||||
|
if (mScissors.size()) {
|
||||||
|
mScissors.last().calcIntersection(rec, intersection);
|
||||||
|
}
|
||||||
|
nvgScissor(mContext->vg, intersection.x, intersection.y, intersection.z, intersection.w);
|
||||||
|
mScissors.append(rec);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Graphics::Canvas::popClamp() {
|
||||||
|
DEBUG_ASSERT(mScissors.size());
|
||||||
|
mIsClamping = false;
|
||||||
|
mScissors.pop();
|
||||||
|
if (mScissors.size()) {
|
||||||
|
const auto& rec = mScissors.last();
|
||||||
|
nvgScissor(mContext->vg, rec.x, rec.y, rec.z, rec.w);
|
||||||
|
} else {
|
||||||
|
nvgResetScissor(mContext->vg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Graphics::Canvas::text(
|
||||||
|
const String& string, const RectF& aRec, halnf size, Align align, halnf marging, const RGBA& col
|
||||||
|
) {
|
||||||
RectF rec = { aRec.x + marging, aRec.y + marging, aRec.z - marging * 2, aRec.w - marging * 2 };
|
RectF rec = { aRec.x + marging, aRec.y + marging, aRec.z - marging * 2, aRec.w - marging * 2 };
|
||||||
|
|
||||||
nvgScissor(mContext->vg, rec.x, rec.y, rec.z, rec.w);
|
pushClamp(rec);
|
||||||
|
|
||||||
nvgFontSize(mContext->vg, size);
|
nvgFontSize(mContext->vg, size);
|
||||||
nvgFontFace(mContext->vg, "default");
|
nvgFontFace(mContext->vg, "default");
|
||||||
|
|
@ -89,13 +108,10 @@ void Graphics::Canvas::text(const String& string, const RectF& aRec, halnf size,
|
||||||
nvgTextAlign(mContext->vg, alignNVG);
|
nvgTextAlign(mContext->vg, alignNVG);
|
||||||
|
|
||||||
nvgText(mContext->vg, centerX, centerY, string.read(), nullptr);
|
nvgText(mContext->vg, centerX, centerY, string.read(), nullptr);
|
||||||
nvgResetScissor(mContext->vg);
|
|
||||||
|
popClamp();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Graphics::Canvas::proc() {
|
void Graphics::Canvas::proc() { nvgBeginFrame(mContext->vg, mWidth, mHeight, 1.0); }
|
||||||
nvgBeginFrame(mContext->vg, mWidth, mHeight, 1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Graphics::Canvas::draw() {
|
void Graphics::Canvas::draw() { nvgEndFrame(mContext->vg); }
|
||||||
nvgEndFrame(mContext->vg);
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,9 @@
|
||||||
|
|
||||||
// -------- Debug UI -------- //
|
// -------- Debug UI -------- //
|
||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
#include <imgui_internal.h>
|
|
||||||
#include <imgui_impl_glfw.h>
|
#include <imgui_impl_glfw.h>
|
||||||
#include <imgui_impl_opengl3.h>
|
#include <imgui_impl_opengl3.h>
|
||||||
|
#include <imgui_internal.h>
|
||||||
|
|
||||||
namespace tp {
|
namespace tp {
|
||||||
class Graphics::GUI::Context {
|
class Graphics::GUI::Context {
|
||||||
|
|
@ -33,6 +33,9 @@ void Graphics::GUI::init(Window* window) {
|
||||||
// ImGui_ImplGlfw_GetBackendData();
|
// ImGui_ImplGlfw_GetBackendData();
|
||||||
|
|
||||||
ImGui_ImplOpenGL3_Init("#version 330");
|
ImGui_ImplOpenGL3_Init("#version 330");
|
||||||
|
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
io.Fonts->AddFontFromFileTTF("Font.ttf", 20.f);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Graphics::GUI::deinit() {
|
void Graphics::GUI::deinit() {
|
||||||
|
|
|
||||||
|
|
@ -47,8 +47,7 @@ void Graphics::GL::deinit() {
|
||||||
glDeleteVertexArrays(1, &mContext->vao);
|
glDeleteVertexArrays(1, &mContext->vao);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Graphics::GL::proc() {
|
void Graphics::GL::proc() {}
|
||||||
}
|
|
||||||
|
|
||||||
void Graphics::GL::draw() {
|
void Graphics::GL::draw() {
|
||||||
glClearColor(0.f, 0.f, 0.f, 0.f);
|
glClearColor(0.f, 0.f, 0.f, 0.f);
|
||||||
|
|
@ -176,22 +175,14 @@ auto Window::getContext() -> Context* { return mContext; }
|
||||||
Graphics::Canvas& Window::getCanvas() { return mGraphics.mCanvas; }
|
Graphics::Canvas& Window::getCanvas() { return mGraphics.mCanvas; }
|
||||||
const Window::Events& Window::getEvents() { return mEvents; }
|
const Window::Events& Window::getEvents() { return mEvents; }
|
||||||
|
|
||||||
const Vec2F& Window::Events::getPos() const {
|
const Vec2F& Window::Events::getPos() const { return mContext->inputManager.getPos(); }
|
||||||
return mContext->inputManager.getPos();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Window::Events::isPressed() const {
|
bool Window::Events::isPressed() const { return mContext->inputManager.isPressed(); }
|
||||||
return mContext->inputManager.isPressed();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Window::Events::isDown() const {
|
bool Window::Events::isReleased() const { return mContext->inputManager.isReleased(); }
|
||||||
return mContext->inputManager.isDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
halnf Window::Events::getScrollY() const {
|
bool Window::Events::isDown() const { return mContext->inputManager.isDown(); }
|
||||||
return mContext->inputManager.getScrollY();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Window::Events::isEvent() const {
|
halnf Window::Events::getScrollY() const { return mContext->inputManager.getScrollY(); }
|
||||||
return mContext->inputManager.isEvents();
|
|
||||||
}
|
bool Window::Events::isEvent() const { return mContext->inputManager.isEvents(); }
|
||||||
|
|
|
||||||
|
|
@ -24,25 +24,17 @@ namespace tp {
|
||||||
|
|
||||||
const Vec2F& getPos() const { return mousePos; }
|
const Vec2F& getPos() const { return mousePos; }
|
||||||
|
|
||||||
bool isKeyPressed(int key) const {
|
bool isKeyPressed(int key) const { return keyStates[key] == GLFW_PRESS; }
|
||||||
return keyStates[key] == GLFW_PRESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isKeyDown(int key) const {
|
bool isKeyDown(int key) const { return keyStates[key] == GLFW_REPEAT || keyStates[key] == GLFW_PRESS; }
|
||||||
return keyStates[key] == GLFW_REPEAT || keyStates[key] == GLFW_PRESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isPressed() const {
|
bool isPressed() const { return mouseButtonStates[GLFW_MOUSE_BUTTON_LEFT] == GLFW_PRESS; }
|
||||||
return mouseButtonStates[GLFW_MOUSE_BUTTON_LEFT] == GLFW_PRESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isDown() const {
|
bool isReleased() const { return mouseButtonStates[GLFW_MOUSE_BUTTON_LEFT] == GLFW_RELEASE; }
|
||||||
return mouseButtonStates[GLFW_MOUSE_BUTTON_LEFT] == GLFW_REPEAT || mouseButtonStates[GLFW_MOUSE_BUTTON_LEFT] == GLFW_PRESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isEvents() const {
|
bool isDown() const { return mouseButtonStates[GLFW_MOUSE_BUTTON_LEFT] == GLFW_REPEAT || mouseButtonStates[GLFW_MOUSE_BUTTON_LEFT] == GLFW_PRESS; }
|
||||||
return isEvent;
|
|
||||||
}
|
bool isEvents() const { return isEvent; }
|
||||||
|
|
||||||
double getScrollY() const { return scrollY; }
|
double getScrollY() const { return scrollY; }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -82,6 +82,8 @@ namespace tp {
|
||||||
};
|
};
|
||||||
|
|
||||||
RectF getAvaliableArea();
|
RectF getAvaliableArea();
|
||||||
|
void pushClamp(const RectF& rec);
|
||||||
|
void popClamp();
|
||||||
void rect(const RectF& rec, const RGBA& col, halnf round = 0);
|
void rect(const RectF& rec, const RGBA& col, halnf round = 0);
|
||||||
void text(const String&, const RectF&, halnf size, Align, halnf marging, const RGBA&);
|
void text(const String&, const RectF&, halnf size, Align, halnf marging, const RGBA&);
|
||||||
|
|
||||||
|
|
@ -90,6 +92,8 @@ namespace tp {
|
||||||
private:
|
private:
|
||||||
halnf mWidth = 600;
|
halnf mWidth = 600;
|
||||||
halnf mHeight = 600;
|
halnf mHeight = 600;
|
||||||
|
Buffer<RectF> mScissors;
|
||||||
|
bool mIsClamping = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ namespace tp {
|
||||||
|
|
||||||
const Vec2F& getPos() const;
|
const Vec2F& getPos() const;
|
||||||
bool isPressed() const;
|
bool isPressed() const;
|
||||||
|
bool isReleased() const;
|
||||||
bool isDown() const;
|
bool isDown() const;
|
||||||
halnf getScrollY() const;
|
halnf getScrollY() const;
|
||||||
bool isEvent() const;
|
bool isEvent() const;
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ project(LibraryViewer)
|
||||||
|
|
||||||
### ---------------------- Externals --------------------- ###
|
### ---------------------- Externals --------------------- ###
|
||||||
set(BINDINGS_INCLUDE ../Externals/glfw/include ${GLEW_INCLUDE_DIR} ${PORTAUDIO_INCLUDE_DIR})
|
set(BINDINGS_INCLUDE ../Externals/glfw/include ${GLEW_INCLUDE_DIR} ${PORTAUDIO_INCLUDE_DIR})
|
||||||
set(BINDINGS_LIBS glfw Imgui Nanovg ${GLEW_LIB} ${ALSA_LIBRARY} ${PORTAUDIO_LIB})
|
set(BINDINGS_LIBS glfw ${GLEW_LIB} ${ALSA_LIBRARY} ${PORTAUDIO_LIB})
|
||||||
|
|
||||||
### ---------------------- Static Library --------------------- ###
|
### ---------------------- Static Library --------------------- ###
|
||||||
file(GLOB SOURCES "./private/*.cpp" "./private/*/*.cpp")
|
file(GLOB SOURCES "./private/*.cpp" "./private/*/*.cpp")
|
||||||
|
|
@ -11,7 +11,7 @@ file(GLOB HEADERS "./public/*.hpp" "./public/*/*.hpp" "./applications/*.hpp")
|
||||||
add_library(${PROJECT_NAME} STATIC ${SOURCES} ${HEADERS})
|
add_library(${PROJECT_NAME} STATIC ${SOURCES} ${HEADERS})
|
||||||
|
|
||||||
target_include_directories(${PROJECT_NAME} PUBLIC ./public/ ${BINDINGS_INCLUDE} ./ext/)
|
target_include_directories(${PROJECT_NAME} PUBLIC ./public/ ${BINDINGS_INCLUDE} ./ext/)
|
||||||
target_link_libraries(${PROJECT_NAME} PUBLIC Graphics Connection)
|
target_link_libraries(${PROJECT_NAME} PUBLIC Graphics Connection Widgets)
|
||||||
target_link_libraries(${PROJECT_NAME} PUBLIC ${BINDINGS_LIBS})
|
target_link_libraries(${PROJECT_NAME} PUBLIC ${BINDINGS_LIBS})
|
||||||
|
|
||||||
file(COPY "library" DESTINATION "${CMAKE_BINARY_DIR}/${PROJECT_NAME}/")
|
file(COPY "library" DESTINATION "${CMAKE_BINARY_DIR}/${PROJECT_NAME}/")
|
||||||
|
|
|
||||||
|
|
@ -2,17 +2,12 @@
|
||||||
|
|
||||||
#include "Library.hpp"
|
#include "Library.hpp"
|
||||||
#include "Player.hpp"
|
#include "Player.hpp"
|
||||||
#include "Rect.hpp"
|
#include "Widgets.hpp"
|
||||||
#include "Animations.hpp"
|
|
||||||
#include "imgui.h"
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename Events, typename Canvas>
|
||||||
concept DrawableConcept = true;
|
|
||||||
|
|
||||||
template <typename Events, typename Canvas> requires(DrawableConcept<Canvas>)
|
|
||||||
class ResizerWidget {
|
class ResizerWidget {
|
||||||
public:
|
public:
|
||||||
ResizerWidget() {}
|
ResizerWidget() = default;
|
||||||
|
|
||||||
void proc(const Events& events, const tp::RectF& areaParent, const tp::RectF& area) {
|
void proc(const Events& events, const tp::RectF& areaParent, const tp::RectF& area) {
|
||||||
if (!areaParent.isOverlap(area)) {
|
if (!areaParent.isOverlap(area)) {
|
||||||
|
|
@ -61,7 +56,8 @@ public:
|
||||||
tp::halnf value = 0;
|
tp::halnf value = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Events, typename Canvas> requires(DrawableConcept<Canvas>)
|
template <typename Events, typename Canvas>
|
||||||
|
requires(DrawableConcept<Canvas>)
|
||||||
class TrackInfoWidget {
|
class TrackInfoWidget {
|
||||||
struct SortType {
|
struct SortType {
|
||||||
tp::String text;
|
tp::String text;
|
||||||
|
|
@ -70,7 +66,8 @@ class TrackInfoWidget {
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TrackInfoWidget(const Track* track = nullptr) : mTrack(track) {
|
TrackInfoWidget(const Track* track = nullptr) :
|
||||||
|
mTrack(track) {
|
||||||
items.append({ "Date Added" });
|
items.append({ "Date Added" });
|
||||||
items.append({ "Date Last Played" });
|
items.append({ "Date Last Played" });
|
||||||
}
|
}
|
||||||
|
|
@ -78,7 +75,6 @@ public:
|
||||||
void proc(const Events& events, const tp::RectF& areaParent, const tp::RectF& area) {
|
void proc(const Events& events, const tp::RectF& areaParent, const tp::RectF& area) {
|
||||||
if (!mTrack) return;
|
if (!mTrack) return;
|
||||||
if (!areaParent.isOverlap(area)) return;
|
if (!areaParent.isOverlap(area)) return;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void draw(Canvas& canvas, const tp::RectF& areaParent, const tp::RectF& area) {
|
void draw(Canvas& canvas, const tp::RectF& areaParent, const tp::RectF& area) {
|
||||||
|
|
@ -94,7 +90,12 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderUI() {
|
void RenderUI() {
|
||||||
ImGui::Begin("InfoWindow", 0, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoResize);
|
ImGui::Begin(
|
||||||
|
"InfoWindow",
|
||||||
|
0,
|
||||||
|
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoBackground |
|
||||||
|
ImGuiWindowFlags_NoResize
|
||||||
|
);
|
||||||
|
|
||||||
if (mTrack) {
|
if (mTrack) {
|
||||||
|
|
||||||
|
|
@ -189,8 +190,8 @@ public:
|
||||||
int filterExisting = 0; // all existing no-existing
|
int filterExisting = 0; // all existing no-existing
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <typename Events, typename Canvas>
|
||||||
template <typename Events, typename Canvas> requires(DrawableConcept<Canvas>)
|
requires(DrawableConcept<Canvas>)
|
||||||
class ScrollBarWidget {
|
class ScrollBarWidget {
|
||||||
public:
|
public:
|
||||||
ScrollBarWidget() = default;
|
ScrollBarWidget() = default;
|
||||||
|
|
@ -264,11 +265,12 @@ public:
|
||||||
tp::halnf mPositionFraction = 0.f;
|
tp::halnf mPositionFraction = 0.f;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <typename Events, typename Canvas>
|
||||||
template <typename Events, typename Canvas> requires(DrawableConcept<Canvas>)
|
requires(DrawableConcept<Canvas>)
|
||||||
class TrackWidget {
|
class TrackWidget {
|
||||||
public:
|
public:
|
||||||
TrackWidget(const Track* track = nullptr) : mTrack(track) {
|
TrackWidget(const Track* track = nullptr) :
|
||||||
|
mTrack(track) {
|
||||||
col.mColor.setAnimTime(0);
|
col.mColor.setAnimTime(0);
|
||||||
col.mColor.setNoTransition({ 0.15f, 0.15f, 0.15f, 0.f });
|
col.mColor.setNoTransition({ 0.15f, 0.15f, 0.15f, 0.f });
|
||||||
};
|
};
|
||||||
|
|
@ -294,7 +296,9 @@ public:
|
||||||
const tp::RectF imageArea = { area.x + marging, area.y + marging, area.w - marging * 2, area.w - marging * 2 };
|
const tp::RectF imageArea = { area.x + marging, area.y + marging, area.w - marging * 2, area.w - marging * 2 };
|
||||||
canvas.rect(imageArea, { 0.25f, 0.25f, 0.25f, 1.f }, 4.f);
|
canvas.rect(imageArea, { 0.25f, 0.25f, 0.25f, 1.f }, 4.f);
|
||||||
|
|
||||||
const tp::RectF textArea = { area.x + area.w + marging, area.y + marging, area.z - area.w - marging * 2, area.w - marging * 2 };
|
const tp::RectF textArea = {
|
||||||
|
area.x + area.w + marging, area.y + marging, area.z - area.w - marging * 2, area.w - marging * 2
|
||||||
|
};
|
||||||
// canvas.rect(textArea, { 0.25f, 0.25f, 0.25f, 1.f }, 4.f);
|
// canvas.rect(textArea, { 0.25f, 0.25f, 0.25f, 1.f }, 4.f);
|
||||||
|
|
||||||
const tp::RectF textAreaName = { textArea.x, textArea.y, textArea.z, textArea.w * 0.5f };
|
const tp::RectF textAreaName = { textArea.x, textArea.y, textArea.z, textArea.w * 0.5f };
|
||||||
|
|
@ -311,10 +315,13 @@ public:
|
||||||
const Track* mTrack;
|
const Track* mTrack;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Events, typename Canvas> requires(DrawableConcept<Canvas>)
|
template <typename Events, typename Canvas>
|
||||||
|
requires(DrawableConcept<Canvas>)
|
||||||
class LibraryWidget {
|
class LibraryWidget {
|
||||||
public:
|
public:
|
||||||
LibraryWidget(Library* lib, Player* player) : mLibrary(lib), mPlayer(player) {
|
LibraryWidget(Library* lib, Player* player) :
|
||||||
|
mLibrary(lib),
|
||||||
|
mPlayer(player) {
|
||||||
for (auto track : mLibrary->mTraks) {
|
for (auto track : mLibrary->mTraks) {
|
||||||
mTraks.append(TrackWidget<Events, Canvas>(&track.data()));
|
mTraks.append(TrackWidget<Events, Canvas>(&track.data()));
|
||||||
}
|
}
|
||||||
|
|
@ -333,7 +340,9 @@ public:
|
||||||
|
|
||||||
areaCP = { aArea.x, aArea.y + aArea.w - controllPanelSize, aArea.z, controllPanelSize };
|
areaCP = { aArea.x, aArea.y + aArea.w - controllPanelSize, aArea.z, controllPanelSize };
|
||||||
area = { aArea.x, aArea.y, mResizeWidget.value, aArea.w - controllPanelSize };
|
area = { aArea.x, aArea.y, mResizeWidget.value, aArea.w - controllPanelSize };
|
||||||
areaInfo = { mResizeWidget.value + resizeSize, aArea.y, aArea.z - mResizeWidget.value - resizeSize, aArea.w - controllPanelSize } ;
|
areaInfo = {
|
||||||
|
mResizeWidget.value + resizeSize, aArea.y, aArea.z - mResizeWidget.value - resizeSize, aArea.w - controllPanelSize
|
||||||
|
};
|
||||||
areaResize = { mResizeWidget.value, aArea.y, resizeSize, areaInfo.w };
|
areaResize = { mResizeWidget.value, aArea.y, resizeSize, areaInfo.w };
|
||||||
|
|
||||||
mScroll.mSizeFraction = (area.w - 10) / (mTraksFiltered.size() * trackSize);
|
mScroll.mSizeFraction = (area.w - 10) / (mTraksFiltered.size() * trackSize);
|
||||||
|
|
@ -348,7 +357,8 @@ public:
|
||||||
auto idx = 0;
|
auto idx = 0;
|
||||||
|
|
||||||
for (auto track : mTraksFiltered) {
|
for (auto track : mTraksFiltered) {
|
||||||
auto trackArea = tp::RectF{ area.x + 10, area.y + 10 + idx * trackSize - offset, area.z - 20 - scrollSize, trackSize - 5 };
|
auto trackArea =
|
||||||
|
tp::RectF{ area.x + 10, area.y + 10 + idx * trackSize - offset, area.z - 20 - scrollSize, trackSize - 5 };
|
||||||
|
|
||||||
track->proc(events, area, trackArea);
|
track->proc(events, area, trackArea);
|
||||||
|
|
||||||
|
|
@ -372,7 +382,8 @@ public:
|
||||||
auto idx = 0;
|
auto idx = 0;
|
||||||
|
|
||||||
for (auto track : mTraksFiltered) {
|
for (auto track : mTraksFiltered) {
|
||||||
auto const trackArea = tp::RectF{ area.x + 10, area.y + 10 + idx * trackSize - offset, area.z - 20 - scrollSize, trackSize - 5 };
|
auto const trackArea =
|
||||||
|
tp::RectF{ area.x + 10, area.y + 10 + idx * trackSize - offset, area.z - 20 - scrollSize, trackSize - 5 };
|
||||||
if (track->mTrack == mCurrentTrack.mTrack) canvas.rect(trackArea, { 0.2f, 0.2f, 0.2f, 0.5f }, 5);
|
if (track->mTrack == mCurrentTrack.mTrack) canvas.rect(trackArea, { 0.2f, 0.2f, 0.2f, 0.5f }, 5);
|
||||||
track->draw(canvas, area, trackArea);
|
track->draw(canvas, area, trackArea);
|
||||||
idx++;
|
idx++;
|
||||||
|
|
|
||||||
|
|
@ -76,13 +76,13 @@ namespace tp {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void calcIntersection(Rect<Type>& in, Rect<Type>& out) const {
|
void calcIntersection(const Rect<Type>& in, Rect<Type>& out) const {
|
||||||
if (isOverlap(in)) {
|
if (isOverlap(in)) {
|
||||||
out = *this;
|
out = *this;
|
||||||
for (char i = 0; i < 2; i++) {
|
for (char i = 0; i < 2; i++) {
|
||||||
clamp(out.pos[i], in.pos[i], in.pos[i] + in.size[i]);
|
out.pos[i] = tp::clamp(out.pos[i], in.pos[i], in.pos[i] + in.size[i]);
|
||||||
Type p2 = pos[i] + size[i];
|
Type p2 = pos[i] + size[i];
|
||||||
clamp(p2, in.pos[i], in.pos[i] + in.size[i]);
|
p2 = tp::clamp(p2, in.pos[i], in.pos[i] + in.size[i]);
|
||||||
out.size[i] = p2 - out.pos[i];
|
out.size[i] = p2 - out.pos[i];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
25
Widgets/CMakeLists.txt
Normal file
25
Widgets/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
project(Widgets)
|
||||||
|
|
||||||
|
### ---------------------- Static Library --------------------- ###
|
||||||
|
file(GLOB SOURCES "./private/*.cpp" "./private/*/*.cpp")
|
||||||
|
file(GLOB HEADERS "./public/*.hpp")
|
||||||
|
|
||||||
|
add_library(${PROJECT_NAME} STATIC ${SOURCES} ${HEADERS})
|
||||||
|
target_include_directories(${PROJECT_NAME} PUBLIC ./public/)
|
||||||
|
target_link_libraries(${PROJECT_NAME} PUBLIC Math Strings)
|
||||||
|
|
||||||
|
### -------------------------- Tests -------------------------- ###
|
||||||
|
enable_testing()
|
||||||
|
file(GLOB TEST_SOURCES "./tests/*.cpp")
|
||||||
|
add_executable(${PROJECT_NAME}Tests ${TEST_SOURCES})
|
||||||
|
target_link_libraries(${PROJECT_NAME}Tests ${PROJECT_NAME} Utils)
|
||||||
|
add_test(NAME ${PROJECT_NAME}Tests COMMAND ${PROJECT_NAME}Tests)
|
||||||
|
|
||||||
|
### -------------------------- Applications -------------------------- ###
|
||||||
|
|
||||||
|
add_executable(ExampleGui examples/Entry.cpp)
|
||||||
|
|
||||||
|
target_link_libraries(ExampleGui ${PROJECT_NAME} Graphics Imgui Nanovg glfw ${GLEW_LIB})
|
||||||
|
target_include_directories(ExampleGui PUBLIC ../Externals/glfw/include ${GLEW_INCLUDE_DIR})
|
||||||
|
|
||||||
|
file(COPY "examples/Font.ttf" DESTINATION "${CMAKE_BINARY_DIR}/${PROJECT_NAME}/")
|
||||||
46
Widgets/examples/Entry.cpp
Normal file
46
Widgets/examples/Entry.cpp
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
|
||||||
|
#include "ExampleGUI.hpp"
|
||||||
|
#include "Window.hpp"
|
||||||
|
|
||||||
|
using namespace tp;
|
||||||
|
|
||||||
|
void runApp() {
|
||||||
|
auto window = tp::Window::createWindow(800, 600, "Window 1");
|
||||||
|
|
||||||
|
tp::ComplexWidget<tp::Window::Events, tp::Graphics::Canvas> gui;
|
||||||
|
|
||||||
|
if (window) {
|
||||||
|
while (!window->shouldClose()) {
|
||||||
|
window->processEvents();
|
||||||
|
|
||||||
|
auto area = window->getCanvas().getAvaliableArea();
|
||||||
|
|
||||||
|
gui.proc(window->getEvents(), {}, { area.x, area.y, area.z, area.w });
|
||||||
|
gui.draw(window->getCanvas());
|
||||||
|
|
||||||
|
tp::sleep(20);
|
||||||
|
|
||||||
|
window->draw();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tp::Window::destroyWindow(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
|
||||||
|
tp::ModuleManifest* deps[] = { &tp::gModuleWidgets, nullptr };
|
||||||
|
tp::ModuleManifest binModule("Chat", nullptr, nullptr, deps);
|
||||||
|
|
||||||
|
if (!binModule.initialize()) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
tp::GlobalGUIConfig config;
|
||||||
|
tp::gGlobalGUIConfig = &config;
|
||||||
|
runApp();
|
||||||
|
}
|
||||||
|
|
||||||
|
binModule.deinitialize();
|
||||||
|
}
|
||||||
281
Widgets/examples/ExampleGUI.hpp
Normal file
281
Widgets/examples/ExampleGUI.hpp
Normal file
|
|
@ -0,0 +1,281 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Widgets.hpp"
|
||||||
|
|
||||||
|
namespace tp {
|
||||||
|
|
||||||
|
template <typename Events, typename Canvas>
|
||||||
|
class UserWidget : public Widget<Events, Canvas> {
|
||||||
|
public:
|
||||||
|
UserWidget() {
|
||||||
|
this->createConfig("User");
|
||||||
|
this->addColor("Base", "Base");
|
||||||
|
this->addValue("Size", "FontSize");
|
||||||
|
this->addValue("Padding", "Padding");
|
||||||
|
this->addColor("ColUser", "Front");
|
||||||
|
this->addColor("Accent", "Accent");
|
||||||
|
this->addValue("Rounding", "Rounding");
|
||||||
|
}
|
||||||
|
|
||||||
|
void proc(const Events& events, const RectF& areaParent, const RectF& aArea) override {
|
||||||
|
this->mArea = aArea;
|
||||||
|
this->mArea.w = 30;
|
||||||
|
this->mVisible = areaParent.isOverlap(aArea);
|
||||||
|
if (!this->mVisible) return;
|
||||||
|
mIsHover = aArea.isInside(events.getPos());
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw(Canvas& canvas) const override {
|
||||||
|
if (!this->mVisible) return;
|
||||||
|
|
||||||
|
if (mIsHover) canvas.rect(this->mArea, this->getColor("Accent"), this->getValue("Rounding"));
|
||||||
|
else canvas.rect(this->mArea, this->getColor("Base"), this->getValue("Rounding"));
|
||||||
|
|
||||||
|
const auto padding = this->getValue("Padding");
|
||||||
|
const auto size = this->getValue("Size");
|
||||||
|
const auto colUser = this->getColor("ColUser");
|
||||||
|
|
||||||
|
canvas.text(mUser.read(), this->mArea, size, Canvas::CC, padding, colUser);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
String mUser = "UserName";
|
||||||
|
bool mIsHover = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Events, typename Canvas>
|
||||||
|
class MessageWidget : public Widget<Events, Canvas> {
|
||||||
|
public:
|
||||||
|
MessageWidget() {
|
||||||
|
this->createConfig("Message");
|
||||||
|
this->addColor("Base", "Base");
|
||||||
|
this->addValue("Size", "FontSize");
|
||||||
|
this->addValue("SizeUser", "FontSizeDim");
|
||||||
|
this->addValue("Padding", "Padding");
|
||||||
|
this->addColor("ColUser", "Front");
|
||||||
|
this->addColor("Col", "FrontDim");
|
||||||
|
this->addValue("Rounding", "Rounding");
|
||||||
|
}
|
||||||
|
|
||||||
|
void proc(const Events& events, const RectF& areaParent, const RectF& aArea) override {
|
||||||
|
this->mArea = aArea;
|
||||||
|
this->mArea.w = 50;
|
||||||
|
this->mVisible = areaParent.isOverlap(aArea);
|
||||||
|
if (!this->mVisible) return;
|
||||||
|
mIsHover = aArea.isInside(events.getPos());
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw(Canvas& canvas) const override {
|
||||||
|
if (!this->mVisible) return;
|
||||||
|
|
||||||
|
if (mIsHover) canvas.rect(this->mArea, this->getColor("Base"), this->getValue("Rounding"));
|
||||||
|
|
||||||
|
const auto padding = this->getValue("Padding");
|
||||||
|
const auto size = this->getValue("Size");
|
||||||
|
const auto sizeUser = this->getValue("SizeUser");
|
||||||
|
const auto col = this->getColor("Col");
|
||||||
|
const auto colUser = this->getColor("ColUser");
|
||||||
|
|
||||||
|
auto userName = this->mArea;
|
||||||
|
userName.w = 25;
|
||||||
|
|
||||||
|
auto content = this->mArea;
|
||||||
|
content.y = userName.y + userName.w;
|
||||||
|
content.w = this->mArea.w - userName.w;
|
||||||
|
|
||||||
|
canvas.text(mContent.read(), content, size, Canvas::LC, padding, col);
|
||||||
|
canvas.text(mUser.read(), userName, sizeUser, Canvas::LC, padding, colUser);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
String mContent = "Message Content";
|
||||||
|
String mUser = "UserName";
|
||||||
|
bool mIsHover = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Events, typename Canvas>
|
||||||
|
class LoginWidget : public Widget<Events, Canvas> {
|
||||||
|
public:
|
||||||
|
explicit LoginWidget() {
|
||||||
|
this->createConfig("Login");
|
||||||
|
this->addColor("Back", "Background");
|
||||||
|
mPass.mId = "pass";
|
||||||
|
mUser.mId = "user";
|
||||||
|
mButton.mLabel.mLabel = "Login";
|
||||||
|
}
|
||||||
|
|
||||||
|
void proc(const Events& events, const RectF& areaParent, const RectF& aArea) override {
|
||||||
|
this->mArea = aArea;
|
||||||
|
mLogged = false;
|
||||||
|
|
||||||
|
const auto xval = aArea.z / 2 - 100;
|
||||||
|
mUser.proc(events, aArea, { xval, 10, 200, 30 });
|
||||||
|
mPass.proc(events, aArea, { xval, 50, 200, 30 });
|
||||||
|
mButton.proc(events, aArea, { xval, 90, 200, 30 });
|
||||||
|
|
||||||
|
if (mButton.mIsReleased) {
|
||||||
|
mButton.mIsReleased = false;
|
||||||
|
mLogged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw(Canvas& canvas) const override {
|
||||||
|
canvas.rect(this->mArea, this->getColor("Back"));
|
||||||
|
mButton.draw(canvas);
|
||||||
|
mUser.draw(canvas);
|
||||||
|
mPass.draw(canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
TextInputWidget<Events, Canvas> mUser;
|
||||||
|
TextInputWidget<Events, Canvas> mPass;
|
||||||
|
ButtonWidget<Events, Canvas> mButton;
|
||||||
|
bool mLogged = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Events, typename Canvas>
|
||||||
|
class ActiveChatWidget : public Widget<Events, Canvas> {
|
||||||
|
public:
|
||||||
|
ActiveChatWidget() {
|
||||||
|
this->createConfig("ActiveChat");
|
||||||
|
this->addColor("Back", "Background");
|
||||||
|
this->addValue("Padding", "Padding");
|
||||||
|
mSend.mLabel.mLabel = "Send";
|
||||||
|
mMessage.mId = "Message";
|
||||||
|
}
|
||||||
|
|
||||||
|
void proc(const Events& events, const RectF& areaParent, const RectF& aArea) override {
|
||||||
|
this->mArea = aArea;
|
||||||
|
|
||||||
|
auto history = this->mArea;
|
||||||
|
history.w -= 50;
|
||||||
|
|
||||||
|
auto input = this->mArea;
|
||||||
|
input.y = history.w + 10;
|
||||||
|
input.w = 40 - this->getValue("Padding");
|
||||||
|
input.x += this->getValue("Padding");
|
||||||
|
input.z -= this->getValue("Padding");
|
||||||
|
|
||||||
|
auto inputMessage = input;
|
||||||
|
inputMessage.z -= 100;
|
||||||
|
|
||||||
|
auto inputSend = input;
|
||||||
|
inputSend.x = inputMessage.x + inputMessage.z + this->getValue("Padding");
|
||||||
|
inputSend.z = 100 - this->getValue("Padding") * 2;
|
||||||
|
|
||||||
|
mSend.proc(events, this->mArea, inputSend);
|
||||||
|
mMessage.proc(events, this->mArea, inputMessage);
|
||||||
|
|
||||||
|
if (mSend.mIsReleased) {
|
||||||
|
mSend.mIsReleased = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
mHistoryView.proc(events, this->mArea, history);
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw(Canvas& canvas) const override {
|
||||||
|
canvas.rect(this->mArea, this->getColor("Back"));
|
||||||
|
mHistoryView.draw(canvas);
|
||||||
|
mMessage.draw(canvas);
|
||||||
|
mSend.draw(canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
Buffer<MessageWidget<Events, Canvas>> mMessages;
|
||||||
|
ScrollableWindow<Events, Canvas> mHistoryView;
|
||||||
|
TextInputWidget<Events, Canvas> mMessage;
|
||||||
|
ButtonWidget<Events, Canvas> mSend;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Events, typename Canvas>
|
||||||
|
class ChattingWidget : public Widget<Events, Canvas> {
|
||||||
|
public:
|
||||||
|
explicit ChattingWidget() {
|
||||||
|
this->createConfig("Chatting");
|
||||||
|
this->addColor("Back", "Background");
|
||||||
|
this->addValue("Padding", "Padding");
|
||||||
|
|
||||||
|
// todo : fetch code
|
||||||
|
mUsers.append(UserWidget<Events, Canvas>());
|
||||||
|
mUsers.append(UserWidget<Events, Canvas>());
|
||||||
|
mUsers.append(UserWidget<Events, Canvas>());
|
||||||
|
|
||||||
|
mUsers[0].mArea = { 0, 0, 100, 100 };
|
||||||
|
mUsers[1].mArea = { 0, 0, 100, 100 };
|
||||||
|
mUsers[2].mArea = { 0, 0, 100, 100 };
|
||||||
|
|
||||||
|
for (auto message : mUsers) {
|
||||||
|
mSideView.mContents.append(&message.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
mActive.mMessages.append(MessageWidget<Events, Canvas>());
|
||||||
|
mActive.mMessages.append(MessageWidget<Events, Canvas>());
|
||||||
|
mActive.mMessages.append(MessageWidget<Events, Canvas>());
|
||||||
|
|
||||||
|
mActive.mMessages[0].mArea = { 0, 0, 100, 100 };
|
||||||
|
mActive.mMessages[1].mArea = { 0, 0, 100, 100 };
|
||||||
|
mActive.mMessages[2].mArea = { 0, 0, 100, 100 };
|
||||||
|
|
||||||
|
for (auto message : mActive.mMessages) {
|
||||||
|
mActive.mHistoryView.mContents.append(&message.data());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void proc(const Events& events, const RectF& areaParent, const RectF& aArea) override {
|
||||||
|
this->mArea = aArea;
|
||||||
|
|
||||||
|
mSplitView.proc(events, aArea, aArea);
|
||||||
|
|
||||||
|
const auto padding = this->getValue("Padding");
|
||||||
|
mSideView.proc(events, this->mArea, mSplitView.getSecond());
|
||||||
|
|
||||||
|
mActive.proc(events, aArea, mSplitView.getFirst());
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw(Canvas& canvas) const override {
|
||||||
|
canvas.rect(this->mArea, this->getColor("Back"));
|
||||||
|
mSplitView.draw(canvas);
|
||||||
|
mSideView.draw(canvas);
|
||||||
|
mActive.draw(canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
Buffer<UserWidget<Events, Canvas>> mUsers;
|
||||||
|
ScrollableWindow<Events, Canvas> mSideView;
|
||||||
|
ActiveChatWidget<Events, Canvas> mActive;
|
||||||
|
SplitView<Events, Canvas> mSplitView;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Events, typename Canvas>
|
||||||
|
class ComplexWidget : public Widget<Events, Canvas> {
|
||||||
|
public:
|
||||||
|
ComplexWidget() {
|
||||||
|
this->createConfig("chat");
|
||||||
|
this->addColor("Back", "Background");
|
||||||
|
}
|
||||||
|
|
||||||
|
void proc(const Events& events, const RectF& areaParent, const RectF& aArea) override {
|
||||||
|
this->mArea = aArea;
|
||||||
|
if (mLogged) {
|
||||||
|
mChatting.proc(events, aArea, aArea);
|
||||||
|
} else {
|
||||||
|
mLogin.proc(events, aArea, aArea);
|
||||||
|
mLogged = mLogin.mLogged;
|
||||||
|
if (mLogged) {
|
||||||
|
mChatting.proc(events, aArea, aArea);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw(Canvas& canvas) const override {
|
||||||
|
canvas.rect(this->mArea, this->getColor("Back"));
|
||||||
|
if (mLogged) mChatting.draw(canvas);
|
||||||
|
else mLogin.draw(canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool mLogged = false;
|
||||||
|
LoginWidget<Events, Canvas> mLogin;
|
||||||
|
ChattingWidget<Events, Canvas> mChatting;
|
||||||
|
};
|
||||||
|
}
|
||||||
BIN
Widgets/examples/Font.ttf
Normal file
BIN
Widgets/examples/Font.ttf
Normal file
Binary file not shown.
9
Widgets/private/WidgetConfig.cpp
Normal file
9
Widgets/private/WidgetConfig.cpp
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
|
||||||
|
#include "WidgetBase.hpp"
|
||||||
|
|
||||||
|
namespace tp {
|
||||||
|
GlobalGUIConfig* gGlobalGUIConfig = nullptr;
|
||||||
|
|
||||||
|
ModuleManifest* deps[] = { &gModuleMath, &gModuleStrings, nullptr };
|
||||||
|
ModuleManifest gModuleWidgets = ModuleManifest("Widgets", nullptr, nullptr, deps);
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "GraphicsCommon.hpp"
|
#include "Color.hpp"
|
||||||
|
#include "Rect.hpp"
|
||||||
|
#include "Timing.hpp"
|
||||||
|
|
||||||
namespace tp {
|
namespace tp {
|
||||||
|
|
||||||
61
Widgets/public/ButtonWidget.hpp
Normal file
61
Widgets/public/ButtonWidget.hpp
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "LabelWidget.hpp"
|
||||||
|
|
||||||
|
namespace tp {
|
||||||
|
|
||||||
|
template <typename Events, typename Canvas>
|
||||||
|
class ButtonWidget : public Widget<Events, Canvas> {
|
||||||
|
public:
|
||||||
|
ButtonWidget() {
|
||||||
|
this->createConfig("Button");
|
||||||
|
this->addColor("Pressed", "Action");
|
||||||
|
this->addColor("Hovered", "Interaction");
|
||||||
|
this->addColor("Default", "Accent");
|
||||||
|
this->addValue("Rounding", "Rounding");
|
||||||
|
}
|
||||||
|
|
||||||
|
void proc(const Events& events, const tp::RectF& areaParent, const tp::RectF& aArea) override {
|
||||||
|
this->mArea = aArea;
|
||||||
|
mIsHover = false;
|
||||||
|
|
||||||
|
if (!areaParent.isOverlap(aArea)) {
|
||||||
|
mIsReleased = false;
|
||||||
|
mIsPressed = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mIsHover = aArea.isInside(events.getPos());
|
||||||
|
|
||||||
|
if (events.isPressed() && mIsHover) {
|
||||||
|
mIsPressed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mIsPressed && mIsHover && events.isReleased()) {
|
||||||
|
mIsReleased = true;
|
||||||
|
mIsPressed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mIsHover) mIsPressed = false;
|
||||||
|
|
||||||
|
mLabel.proc(events, aArea, aArea);
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw(Canvas& canvas) const override {
|
||||||
|
if (mIsPressed) {
|
||||||
|
canvas.rect(this->mArea, this->getColor("Pressed"), this->getValue("Rounding"));
|
||||||
|
} else if (mIsHover) {
|
||||||
|
canvas.rect(this->mArea, this->getColor("Hovered"), this->getValue("Rounding"));
|
||||||
|
} else {
|
||||||
|
canvas.rect(this->mArea, this->getColor("Default"), this->getValue("Rounding"));
|
||||||
|
}
|
||||||
|
mLabel.draw(canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
LabelWidget<Events, Canvas> mLabel;
|
||||||
|
bool mIsHover = false;
|
||||||
|
bool mIsPressed = false;
|
||||||
|
bool mIsReleased = false;
|
||||||
|
};
|
||||||
|
}
|
||||||
34
Widgets/public/LabelWidget.hpp
Normal file
34
Widgets/public/LabelWidget.hpp
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "WidgetBase.hpp"
|
||||||
|
|
||||||
|
namespace tp {
|
||||||
|
template <typename Events, typename Canvas>
|
||||||
|
class LabelWidget : public Widget<Events, Canvas> {
|
||||||
|
public:
|
||||||
|
LabelWidget() {
|
||||||
|
this->createConfig("Label");
|
||||||
|
this->addValue("Size", "FontSize");
|
||||||
|
this->addValue("Padding", "Padding");
|
||||||
|
this->addColor("Default", "Front");
|
||||||
|
}
|
||||||
|
|
||||||
|
void proc(const Events& events, const tp::RectF& areaParent, const tp::RectF& aArea) override {
|
||||||
|
this->mArea = aArea;
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw(Canvas& canvas) const override {
|
||||||
|
canvas.text(
|
||||||
|
mLabel.read(),
|
||||||
|
this->mArea,
|
||||||
|
this->getValue("Size"),
|
||||||
|
Canvas::CC,
|
||||||
|
this->getValue("Padding"),
|
||||||
|
this->getColor("Default")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
String mLabel = "Label";
|
||||||
|
};
|
||||||
|
}
|
||||||
218
Widgets/public/ScrollableWidget.hpp
Normal file
218
Widgets/public/ScrollableWidget.hpp
Normal file
|
|
@ -0,0 +1,218 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "WidgetBase.hpp"
|
||||||
|
|
||||||
|
namespace tp {
|
||||||
|
|
||||||
|
template <typename Events, typename Canvas>
|
||||||
|
class ScrollBarWidget : public Widget<Events, Canvas> {
|
||||||
|
public:
|
||||||
|
ScrollBarWidget() {
|
||||||
|
this->createConfig("ScrollBar");
|
||||||
|
this->addColor("Default", "Base");
|
||||||
|
this->addColor("Handle", "Accent");
|
||||||
|
this->addColor("Hovered", "Interaction");
|
||||||
|
this->addColor("Scrolling", "Action");
|
||||||
|
this->addValue("Padding", "Padding");
|
||||||
|
this->addValue("HandleSize", 20.f);
|
||||||
|
this->addValue("MinSize", 20.f);
|
||||||
|
this->addValue("Rounding", "Rounding");
|
||||||
|
}
|
||||||
|
|
||||||
|
// takes whole area
|
||||||
|
void proc(const Events& events, const tp::RectF& areaParent, const tp::RectF& aArea) override {
|
||||||
|
this->mArea = aArea;
|
||||||
|
|
||||||
|
auto area = getHandle();
|
||||||
|
mHovered = getHandleHandle().isInside(events.getPos());
|
||||||
|
|
||||||
|
if (mSizeFraction > 1.f) {
|
||||||
|
mPositionFraction = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!areaParent.isOverlap(area)) {
|
||||||
|
mIsScrolling = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (events.getScrollY() != 0 && areaParent.isInside(events.getPos())) {
|
||||||
|
auto offset = events.getScrollY() < 0 ? 1.0f : -1.0f;
|
||||||
|
if (scrollInertia * offset > 0) {
|
||||||
|
scrollInertia += offset;
|
||||||
|
} else {
|
||||||
|
scrollInertia = -scrollInertia + offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tp::abs(scrollInertia) > 0.1f) {
|
||||||
|
auto offset = scrollInertia * mScrollFactor;
|
||||||
|
mPositionFraction += offset;
|
||||||
|
mPositionFraction = tp::clamp(mPositionFraction, 0.f, 1.f - mSizeFraction);
|
||||||
|
scrollInertia *= 0.f;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (events.isPressed() && area.isInside(events.getPos())) {
|
||||||
|
mIsScrolling = true;
|
||||||
|
} else if (!events.isDown()) {
|
||||||
|
mIsScrolling = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mIsScrolling) {
|
||||||
|
tp::halnf pos = events.getPos().y;
|
||||||
|
pos = (pos - area.y - mSizeFraction * area.w / 2.f) / area.w;
|
||||||
|
mPositionFraction = tp::clamp(pos, 0.f, 1.f - mSizeFraction);
|
||||||
|
}
|
||||||
|
|
||||||
|
mPositionFraction = tp::clamp(mPositionFraction, 0.f, 1.f - mSizeFraction);
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw(Canvas& canvas) const override {
|
||||||
|
auto area = getHandle();
|
||||||
|
|
||||||
|
if (mSizeFraction > 1.f) return;
|
||||||
|
// if (!areaParent.isOverlap(getHandle())) return;
|
||||||
|
|
||||||
|
tp::RGBA col = this->getColor("Handle");
|
||||||
|
|
||||||
|
if (mIsScrolling) {
|
||||||
|
col = this->getColor("Scrolling");
|
||||||
|
} else if (mHovered) {
|
||||||
|
col = this->getColor("Hovered");
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas.rect(area, this->getColor("Default"), this->getValue("Rounding"));
|
||||||
|
|
||||||
|
canvas.rect(getHandleHandle(), col, this->getValue("Rounding"));
|
||||||
|
}
|
||||||
|
|
||||||
|
RectF getHandleHandle() const {
|
||||||
|
auto minSize = this->getValue("MinSize");
|
||||||
|
if (mIsScrolling) {
|
||||||
|
minSize = this->getValue("MinSize") * 2;
|
||||||
|
} else if (mHovered) {
|
||||||
|
minSize = this->getValue("MinSize") * 2;
|
||||||
|
}
|
||||||
|
auto area = getHandle();
|
||||||
|
auto sliderSize = tp::clamp(area.w * mSizeFraction, minSize, area.w);
|
||||||
|
auto diffSize = sliderSize - area.w * mSizeFraction;
|
||||||
|
return { area.x, area.y + (area.w - diffSize) * mPositionFraction, area.z, sliderSize };
|
||||||
|
}
|
||||||
|
|
||||||
|
RectF getViewport() const {
|
||||||
|
if (mSizeFraction > 1.f) {
|
||||||
|
return this->mArea;
|
||||||
|
}
|
||||||
|
return { this->mArea.x, this->mArea.y, this->mArea.z - this->getValue("HandleSize"), this->mArea.w };
|
||||||
|
}
|
||||||
|
|
||||||
|
RectF getHandle() const {
|
||||||
|
return { this->mArea.x + this->mArea.z - this->getValue("HandleSize") + this->getValue("Padding"),
|
||||||
|
this->mArea.y + this->getValue("Padding"),
|
||||||
|
this->getValue("HandleSize") - this->getValue("Padding") * 2,
|
||||||
|
this->mArea.w - this->getValue("Padding") * 2 };
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
halnf mScrollFactor = 0.f;
|
||||||
|
halnf scrollInertia = 0.f;
|
||||||
|
bool mIsScrolling = false;
|
||||||
|
halnf mSizeFraction = 1.f;
|
||||||
|
halnf mPositionFraction = 0.f;
|
||||||
|
bool mHovered = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Events, typename Canvas>
|
||||||
|
class ScrollableWindow : public Widget<Events, Canvas> {
|
||||||
|
public:
|
||||||
|
ScrollableWindow() {
|
||||||
|
this->createConfig("ScrollableWindow");
|
||||||
|
this->addColor("Default", "Base");
|
||||||
|
this->addValue("Padding", "Padding");
|
||||||
|
}
|
||||||
|
|
||||||
|
// takes whole area
|
||||||
|
void proc(const Events& events, const tp::RectF& areaParent, const tp::RectF& aArea) override {
|
||||||
|
this->mArea = aArea;
|
||||||
|
this->mVisible = areaParent.isOverlap(aArea);
|
||||||
|
|
||||||
|
if (!this->mVisible) return;
|
||||||
|
|
||||||
|
updateContents();
|
||||||
|
updateContentSize();
|
||||||
|
|
||||||
|
const auto padding = this->getValue("Padding");
|
||||||
|
|
||||||
|
mScroller.mSizeFraction = this->mArea.w / mContentSize;
|
||||||
|
mScroller.proc(events, this->mArea, this->mArea);
|
||||||
|
|
||||||
|
if (mScroller.mSizeFraction > 1.f) {
|
||||||
|
setOffset(0);
|
||||||
|
} else {
|
||||||
|
setOffset((-mScroller.mPositionFraction) * mContentSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto widget : mContents) {
|
||||||
|
widget->proc(
|
||||||
|
events,
|
||||||
|
this->mArea,
|
||||||
|
{ this->mArea.x + padding, widget->mArea.y, mScroller.getViewport().z - padding * 2, widget->mArea.w }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw(Canvas& canvas) const override {
|
||||||
|
if (!this->mVisible) return;
|
||||||
|
mScroller.draw(canvas);
|
||||||
|
|
||||||
|
canvas.pushClamp(this->mArea);
|
||||||
|
for (auto widget : mContents) {
|
||||||
|
widget->draw(canvas);
|
||||||
|
}
|
||||||
|
canvas.popClamp();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void updateContents() {
|
||||||
|
if (mContents.size()) {
|
||||||
|
const auto padding = this->getValue("Padding");
|
||||||
|
const halnf offset = mContents.first()->mArea.y + padding;
|
||||||
|
|
||||||
|
halnf start = 0;
|
||||||
|
for (auto widget : mContents) {
|
||||||
|
widget->mArea.y = start;
|
||||||
|
start += widget->mArea.w + padding;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto widget : mContents) {
|
||||||
|
widget->mArea.y += offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateContentSize() {
|
||||||
|
mContentSize = 0;
|
||||||
|
if (mContents.size()) {
|
||||||
|
const auto padding = this->getValue("Padding");
|
||||||
|
mContentSize = mContents.last()->mArea.y - mContents.first()->mArea.y;
|
||||||
|
mContentSize += mContents.last()->mArea.w;
|
||||||
|
mContentSize += (mContents.size()) * padding;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setOffset(const halnf offset) {
|
||||||
|
if (!mContents.size()) return;
|
||||||
|
const auto padding = this->getValue("Padding");
|
||||||
|
auto newOffset = offset - mContents.first()->mArea.y + padding;
|
||||||
|
for (auto widget : mContents) {
|
||||||
|
widget->mArea.y += newOffset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
halnf mContentSize = 0;
|
||||||
|
Buffer<Widget<Events, Canvas>*> mContents;
|
||||||
|
ScrollBarWidget<Events, Canvas> mScroller;
|
||||||
|
};
|
||||||
|
}
|
||||||
79
Widgets/public/SplitViewWidget.hpp
Normal file
79
Widgets/public/SplitViewWidget.hpp
Normal file
|
|
@ -0,0 +1,79 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "WidgetBase.hpp"
|
||||||
|
|
||||||
|
namespace tp {
|
||||||
|
template <typename Events, typename Canvas>
|
||||||
|
class SplitView : public Widget<Events, Canvas> {
|
||||||
|
public:
|
||||||
|
SplitView() {
|
||||||
|
this->createConfig("SplitView");
|
||||||
|
this->addColor("Handle", "Accent");
|
||||||
|
this->addColor("Hovered", "Interaction");
|
||||||
|
this->addColor("Resizing", "Action");
|
||||||
|
this->addValue("Min", 200.f);
|
||||||
|
this->addValue("HandleSize", 7.f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void proc(const Events& events, const tp::RectF& areaParent, const tp::RectF& aArea) override {
|
||||||
|
this->mArea = aArea;
|
||||||
|
|
||||||
|
if (!areaParent.isOverlap(aArea)) {
|
||||||
|
mResizeInProcess = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mIsHover = getHandle().isInside(events.getPos());
|
||||||
|
|
||||||
|
if (events.isPressed() && mIsHover) {
|
||||||
|
mResizeInProcess = true;
|
||||||
|
} else if (!events.isDown()) {
|
||||||
|
mResizeInProcess = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mResizeInProcess) {
|
||||||
|
halnf pos = events.getPos().x;
|
||||||
|
auto diff = pos - (this->mArea.x + mFactor * this->mArea.z);
|
||||||
|
mFactor += diff / this->mArea.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
mFactor = tp::clamp(mFactor, this->getValue("Min") / this->mArea.z, 1 - this->getValue("Min") / this->mArea.z);
|
||||||
|
|
||||||
|
if (this->getValue("Min") * 2.f > this->mArea.z) {
|
||||||
|
mFactor = 0.5f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// takes whole area
|
||||||
|
void draw(Canvas& canvas) const override {
|
||||||
|
if (mResizeInProcess) canvas.rect(getHandle(), this->getColor("Resizing"));
|
||||||
|
else if (mIsHover) canvas.rect(getHandle(), this->getColor("Hovered"));
|
||||||
|
else canvas.rect(getHandle(), this->getColor("Handle"));
|
||||||
|
}
|
||||||
|
|
||||||
|
RectF getFirst() const {
|
||||||
|
return {
|
||||||
|
this->mArea.x, this->mArea.y, mFactor * this->mArea.z - this->getValue("HandleSize") / 2.f, this->mArea.w
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
RectF getSecond() const {
|
||||||
|
return { this->mArea.x + mFactor * this->mArea.z + this->getValue("HandleSize") / 2.f,
|
||||||
|
this->mArea.y,
|
||||||
|
(1.f - mFactor) * this->mArea.z - this->getValue("HandleSize") / 2.f,
|
||||||
|
this->mArea.w };
|
||||||
|
}
|
||||||
|
|
||||||
|
RectF getHandle() const {
|
||||||
|
return { this->mArea.x + mFactor * this->mArea.z - this->getValue("HandleSize") / 2.f,
|
||||||
|
this->mArea.y,
|
||||||
|
this->getValue("HandleSize"),
|
||||||
|
this->mArea.w };
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
halnf mFactor = 0.7f;
|
||||||
|
bool mResizeInProcess = false;
|
||||||
|
bool mIsHover = false;
|
||||||
|
};
|
||||||
|
}
|
||||||
72
Widgets/public/TextInputWidget.hpp
Normal file
72
Widgets/public/TextInputWidget.hpp
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "WidgetBase.hpp"
|
||||||
|
|
||||||
|
#include "imgui.h"
|
||||||
|
#include "imgui_internal.h"
|
||||||
|
|
||||||
|
namespace tp {
|
||||||
|
template <typename Events, typename Canvas>
|
||||||
|
class TextInputWidget : public Widget<Events, Canvas> {
|
||||||
|
public:
|
||||||
|
TextInputWidget() {
|
||||||
|
this->createConfig("TextInput");
|
||||||
|
this->addColor("Accent", "Accent");
|
||||||
|
this->addColor("Base", "Base");
|
||||||
|
this->addValue("Rounding", "Rounding");
|
||||||
|
this->addColor("Hovered", "Interaction");
|
||||||
|
this->addValue("Padding", "Padding");
|
||||||
|
}
|
||||||
|
|
||||||
|
void proc(const Events& events, const tp::RectF& areaParent, const tp::RectF& aArea) override {
|
||||||
|
this->mArea = aArea;
|
||||||
|
nChanged = false;
|
||||||
|
|
||||||
|
const auto col = this->getColor("Accent");
|
||||||
|
const auto colSel = this->getColor("Hovered");
|
||||||
|
|
||||||
|
ImGui::GetStyle().Colors[ImGuiCol_FrameBg] = { col.r, col.g, col.b, col.a };
|
||||||
|
ImGui::GetStyle().Colors[ImGuiCol_TextSelectedBg] = { colSel.r, colSel.g, colSel.b, colSel.a };
|
||||||
|
|
||||||
|
ImGui::SetNextWindowPos({ this->mArea.x, this->mArea.y });
|
||||||
|
ImGui::SetNextWindowSize({ this->mArea.z, this->mArea.w });
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, { 0, 0 });
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, this->getValue("Rounding") * 1.5f);
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, { this->getValue("Padding"), this->getValue("Padding") });
|
||||||
|
|
||||||
|
// ImGui::PushID((int) alni(this));
|
||||||
|
ImGui::Begin(
|
||||||
|
mId.read(),
|
||||||
|
0,
|
||||||
|
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoBackground |
|
||||||
|
ImGuiWindowFlags_NoResize
|
||||||
|
);
|
||||||
|
|
||||||
|
if (mMultiline) {
|
||||||
|
if (ImGui::InputTextMultiline("input", mBuff, mMaxBufferSize, { this->mArea.z, this->mArea.w })) {
|
||||||
|
mValue = mBuff;
|
||||||
|
nChanged = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (ImGui::InputTextEx("input", mId.read(), mBuff, mMaxBufferSize, { this->mArea.z, this->mArea.w }, 0)) {
|
||||||
|
mValue = mBuff;
|
||||||
|
nChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::End();
|
||||||
|
ImGui::PopStyleVar(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw(Canvas& canvas) const override {
|
||||||
|
// canvas.rect(this->mArea, this->getColor("Base"));
|
||||||
|
}
|
||||||
|
|
||||||
|
enum { mMaxBufferSize = 512 };
|
||||||
|
char mBuff[mMaxBufferSize] = "";
|
||||||
|
bool nChanged = false;
|
||||||
|
String mValue;
|
||||||
|
String mId = "id";
|
||||||
|
bool mMultiline = false;
|
||||||
|
};
|
||||||
|
}
|
||||||
64
Widgets/public/WidgetBase.hpp
Normal file
64
Widgets/public/WidgetBase.hpp
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "WidgetConfig.hpp"
|
||||||
|
|
||||||
|
namespace tp {
|
||||||
|
|
||||||
|
template <typename Events, typename Canvas>
|
||||||
|
class Widget {
|
||||||
|
public:
|
||||||
|
Widget() = default;
|
||||||
|
|
||||||
|
[[nodiscard]] const RGBA& getColor(const String& name) const {
|
||||||
|
const auto& node = cfg->values.get(name);
|
||||||
|
if (node.type == WidgetConfig::Node::REF) {
|
||||||
|
return globalCfg->configs.get(node.refGroup).values.get(node.refName).color;
|
||||||
|
}
|
||||||
|
return cfg->values.get(name).color;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] halnf getValue(const String& name) const {
|
||||||
|
const auto& node = cfg->values.get(name);
|
||||||
|
if (node.type == WidgetConfig::Node::REF) {
|
||||||
|
return globalCfg->configs.get(node.refGroup).values.get(node.refName).value;
|
||||||
|
}
|
||||||
|
return cfg->values.get(name).value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void addColor(const String& name, const RGBA& val) { cfg->values.put(name, WidgetConfig::Node(val)); }
|
||||||
|
void addValue(const String& name, halnf val) { cfg->values.put(name, WidgetConfig::Node(val)); }
|
||||||
|
|
||||||
|
void addColor(const String& name, const String& presetName) {
|
||||||
|
cfg->values.put(name, WidgetConfig::Node(presetName));
|
||||||
|
}
|
||||||
|
void addValue(const String& name, const String& presetName) {
|
||||||
|
cfg->values.put(name, WidgetConfig::Node(presetName));
|
||||||
|
}
|
||||||
|
|
||||||
|
void createConfig(const String& name) {
|
||||||
|
globalCfg->configs.put(name, {});
|
||||||
|
cfg = &globalCfg->configs.get(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void proc(const Events& events, const tp::RectF& areaParent, const tp::RectF& aArea) {
|
||||||
|
mVisible = areaParent.isOverlap(aArea);
|
||||||
|
if (!mVisible) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->mArea = aArea;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void draw(Canvas& canvas) const {
|
||||||
|
if (!mVisible) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
GlobalGUIConfig* globalCfg = gGlobalGUIConfig;
|
||||||
|
WidgetConfig* cfg = nullptr;
|
||||||
|
RectF mArea;
|
||||||
|
bool mVisible = false;
|
||||||
|
};
|
||||||
|
}
|
||||||
70
Widgets/public/WidgetConfig.hpp
Normal file
70
Widgets/public/WidgetConfig.hpp
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Animations.hpp"
|
||||||
|
#include "Map.hpp"
|
||||||
|
#include "Rect.hpp"
|
||||||
|
#include "Strings.hpp"
|
||||||
|
|
||||||
|
namespace tp {
|
||||||
|
|
||||||
|
extern ModuleManifest gModuleWidgets;
|
||||||
|
|
||||||
|
struct WidgetConfig {
|
||||||
|
struct Node {
|
||||||
|
enum Type { NONE, VAL, COL, REF };
|
||||||
|
|
||||||
|
halnf value = 0.f;
|
||||||
|
RGBA color = {};
|
||||||
|
|
||||||
|
Type type = NONE;
|
||||||
|
|
||||||
|
String refGroup;
|
||||||
|
String refName;
|
||||||
|
|
||||||
|
Node() = default;
|
||||||
|
|
||||||
|
explicit Node(halnf val) {
|
||||||
|
type = VAL;
|
||||||
|
value = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit Node(const RGBA& val) {
|
||||||
|
type = COL;
|
||||||
|
color = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit Node(const String& val) {
|
||||||
|
refGroup = "Presets";
|
||||||
|
refName = val;
|
||||||
|
type = REF;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Map<String, Node> values;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GlobalGUIConfig {
|
||||||
|
GlobalGUIConfig() {
|
||||||
|
configs.put("Presets", {});
|
||||||
|
auto& presets = configs.get("Presets");
|
||||||
|
|
||||||
|
presets.values.put("FontSize", WidgetConfig::Node(15.f));
|
||||||
|
presets.values.put("FontSizeDim", WidgetConfig::Node(12.f));
|
||||||
|
presets.values.put("Rounding", WidgetConfig::Node(5.f));
|
||||||
|
presets.values.put("Padding", WidgetConfig::Node(5.f));
|
||||||
|
presets.values.put("HandleSize", WidgetConfig::Node(5.f));
|
||||||
|
|
||||||
|
presets.values.put("Background", WidgetConfig::Node({ 0.03f, 0.03f, 0.03f, 1.f }));
|
||||||
|
presets.values.put("Base", WidgetConfig::Node({ 0.07f, 0.07f, 0.07f, 1.f }));
|
||||||
|
presets.values.put("Accent", WidgetConfig::Node({ 0.13f, 0.13f, 0.13f, 1.f }));
|
||||||
|
presets.values.put("Interaction", WidgetConfig::Node({ 0.33f, 0.33f, 0.3f, 1.f }));
|
||||||
|
presets.values.put("Action", WidgetConfig::Node({ 0.44f, 0.44f, 0.4f, 1.f }));
|
||||||
|
presets.values.put("Front", WidgetConfig::Node({ 1.f, 1.f, 1.f, 1.f }));
|
||||||
|
presets.values.put("FrontDim", WidgetConfig::Node({ 0.7f, 0.7f, 0.7f, 1.f }));
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, WidgetConfig> configs;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern GlobalGUIConfig* gGlobalGUIConfig;
|
||||||
|
}
|
||||||
8
Widgets/public/Widgets.hpp
Normal file
8
Widgets/public/Widgets.hpp
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ButtonWidget.hpp"
|
||||||
|
#include "LabelWidget.hpp"
|
||||||
|
#include "ScrollableWidget.hpp"
|
||||||
|
#include "SplitViewWidget.hpp"
|
||||||
|
#include "TextInputWidget.hpp"
|
||||||
|
#include "Animations.hpp"
|
||||||
1
Widgets/tests/Tests.cpp
Normal file
1
Widgets/tests/Tests.cpp
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
int main() {}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue