Replace Old Widgets

This commit is contained in:
IlyaShurupov 2024-10-18 10:56:42 +03:00 committed by Ilya Shurupov
parent 03da5e41d6
commit c41dc20132
81 changed files with 388 additions and 278 deletions

View file

@ -0,0 +1,21 @@
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 Graphics Imgui)
### -------------------------- Applications -------------------------- ###
add_executable(SimpleGui examples/SimpleGUI.cpp)
target_link_libraries(SimpleGui ${PROJECT_NAME} ${GLEW_LIB})
target_include_directories(SimpleGui PUBLIC ../Externals/glfw/include ${GLEW_INCLUDE_DIR})
add_executable(ChatGui examples/ChatGUI.cpp)
target_link_libraries(ChatGui ${PROJECT_NAME} ${GLEW_LIB})
target_include_directories(ChatGui PUBLIC ../Externals/glfw/include ${GLEW_INCLUDE_DIR})
file(COPY "examples/Font.ttf" DESTINATION "${CMAKE_BINARY_DIR}/${PROJECT_NAME}/")

View file

@ -0,0 +1,34 @@
#include "ChatGUI.hpp"
#include "GraphicApplication.hpp"
using namespace tp;
class ExampleGUI : public Application {
public:
ExampleGUI() = default;
void processFrame(EventHandler* eventHandler, halnf delta) override {
auto rec = RectF({ 0, 0 }, mWindow->getSize());
mGui.updateConfigWrapper(mWidgetManager);
mGui.setArea(rec);
mGui.setVisible(true);
mGui.procWrapper(*eventHandler, rec);
}
void drawFrame(Canvas* canvas) override { mGui.drawWrapper(*canvas); }
private:
WidgetManager mWidgetManager;
ComplexWidget mGui;
};
int main() {
{
ExampleGUI gui;
gui.run();
}
}

View file

@ -0,0 +1,256 @@
#pragma once
#include "Widgets.hpp"
namespace tp {
class UserWidget : public Widget {
public:
UserWidget() = default;
void eventDraw(Canvas& canvas) override {
if (this->isFocus()) canvas.rect(this->mArea, mAccentColor, mRounding);
else canvas.rect(this->mArea, mBaseColor, mRounding);
canvas.text(mUser.c_str(), this->mArea, mFontSize, Canvas::CC, mPadding, mUserColor);
}
public:
void eventUpdateConfiguration(WidgetManager& wm) override {
wm.setActiveId("UserWidget");
mBaseColor = wm.getColor("Base", "Base");
mFontSize = wm.getNumber("Size", "FontSize");
mPadding = wm.getNumber("Padding", "Padding");
mUserColor = wm.getColor("ColUser", "Front");
mAccentColor = wm.getColor("Accent", "Accent");
mRounding = wm.getNumber("Rounding", "Rounding");
}
public:
std::string mUser = "UserName";
bool mIsHover = false;
RGBA mBaseColor;
RGBA mUserColor;
RGBA mAccentColor;
halnf mPadding = 0;
halnf mFontSize = 0;
halnf mRounding = 0;
};
class MessageWidget : public Widget {
public:
MessageWidget() = default;
void eventDraw(Canvas& canvas) override {
if (this->isFocus()) canvas.rect(this->mArea, mBaseColor, mRounding);
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.c_str(), content, mFontSize, Canvas::LC, mPadding, mUserColorDim);
canvas.text(mUser.c_str(), userName, mFontSizeDim, Canvas::LC, mPadding, mUserColor);
}
void eventUpdateConfiguration(WidgetManager& wm) override {
wm.setActiveId("MessageWidget");
mBaseColor = wm.getColor("Base", "Base");
mFontSize = wm.getNumber("Size", "FontSize");
mFontSizeDim = wm.getNumber("SizeUser", "FontSizeDim");
mPadding = wm.getNumber("Padding", "Padding");
mUserColor = wm.getColor("UserColor", "Front");
mUserColorDim = wm.getColor("UserColorDim", "FrontDim");
mRounding = wm.getNumber("Rounding", "Rounding");
}
public:
std::string mContent = "Message Content";
std::string mUser = "UserName";
bool mIsHover = false;
RGBA mBaseColor;
RGBA mUserColor;
RGBA mUserColorDim;
halnf mPadding = 0;
halnf mFontSize = 0;
halnf mFontSizeDim = 0;
halnf mRounding = 0;
};
class LoginWidget : public Widget {
public:
explicit LoginWidget() {
mPass.mId = "pass";
mUser.mId = "user";
mButton.mLabel.mLabel = "Login";
this->mChildWidgets.pushBack(&mPass);
this->mChildWidgets.pushBack(&mUser);
this->mChildWidgets.pushBack(&mButton);
}
void eventProcess(const Events& events) override {
mLogged = false;
const auto xval = this->mArea.z / 2 - 100;
mUser.setArea({ xval, 10, 200, 30 });
mPass.setArea({ xval, 50, 200, 30 });
mButton.setArea({ xval, 90, 200, 30 });
if (mButton.isFired()) {
mLogged = true;
}
}
void eventDraw(Canvas& canvas) override { canvas.rect(this->mArea, mBGColor); }
public:
void eventUpdateConfiguration(WidgetManager& wm) override {
wm.setActiveId("LoginWidget");
mBGColor = wm.getColor("Back", "Base");
}
public:
TextInputWidget mUser;
TextInputWidget mPass;
ButtonWidget mButton;
bool mLogged = false;
RGBA mBGColor;
};
class ActiveChatWidget : public Widget {
public:
ActiveChatWidget() {
mSend.mLabel.mLabel = "Send";
mMessage.mId = "Message";
this->mChildWidgets.pushBack(&mHistoryView);
this->mChildWidgets.pushBack(&mMessage);
this->mChildWidgets.pushBack(&mSend);
}
void eventProcess(const Events& events) override {
auto history = this->mArea;
history.w -= 50;
auto input = this->mArea;
input.y = history.w + 10;
input.w = 40 - mPadding;
input.x += mPadding;
input.z -= mPadding;
auto inputMessage = input;
inputMessage.z -= 100;
auto inputSend = input;
inputSend.x = inputMessage.x + inputMessage.z + mPadding;
inputSend.z = 100 - mPadding * 2;
mSend.setArea(inputSend);
mMessage.setArea(inputMessage);
mHistoryView.setArea(history);
}
void eventDraw(Canvas& canvas) override { canvas.rect(this->mArea, mBGColor); }
void eventUpdateConfiguration(WidgetManager& wm) override {
wm.setActiveId("ActiveChat");
mBGColor = wm.getColor("Back", "Background");
mPadding = wm.getNumber("Padding", "Padding");
}
public:
Buffer<MessageWidget> mMessages;
ScrollableWindow mHistoryView;
TextInputWidget mMessage;
ButtonWidget mSend;
RGBA mBGColor;
halnf mPadding = 0;
};
class ChattingWidget : public DockWidget {
public:
ChattingWidget() {
// todo : fetch code
mUsers.append(UserWidget());
mUsers.append(UserWidget());
mUsers.append(UserWidget());
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.addWidget(&message.data());
}
mActive.mMessages.append(MessageWidget());
mActive.mMessages.append(MessageWidget());
mActive.mMessages.append(MessageWidget());
mActive.mMessages[0].mArea = { 0, 0, 100, 50 };
mActive.mMessages[1].mArea = { 0, 0, 100, 50 };
mActive.mMessages[2].mArea = { 0, 0, 100, 50 };
for (auto message : mActive.mMessages) {
mActive.mHistoryView.addWidget(&message.data());
}
addSideWidget(&mSideView, DockWidget::RIGHT);
setCenterWidget(&mActive);
}
void eventUpdateConfiguration(WidgetManager& wm) override {
wm.setActiveId("ChattingWidget");
mBGColor = wm.getColor("Back", "Background");
}
public:
Buffer<UserWidget> mUsers;
ScrollableWindow mSideView;
ActiveChatWidget mActive;
RGBA mBGColor;
};
class ComplexWidget : public Widget {
public:
ComplexWidget() {
this->mChildWidgets.pushBack(&mLogin);
this->mChildWidgets.pushBack(&mChatting);
}
void eventProcess(const Events& events) override {
mLogged = mLogin.mLogged;
mLogin.setEnable(!mLogged);
mChatting.setEnable(mLogged);
mLogin.setArea(this->mArea);
mChatting.setArea(this->mArea);
}
void eventDraw(Canvas& canvas) override { canvas.rect(this->mArea, mBGColor); }
void eventUpdateConfiguration(WidgetManager& wm) override {
wm.setActiveId("ChatGui");
mBGColor = wm.getColor("Back", "Background");
}
private:
bool mLogged = false;
LoginWidget mLogin;
ChattingWidget mChatting;
RGBA mBGColor;
};
}

Binary file not shown.

View file

@ -0,0 +1,40 @@
#include "SimpleGUI.hpp"
#include "GraphicApplication.hpp"
using namespace tp;
class SimpleGUI : public Application {
public:
SimpleGUI() {
// mGui.mPreview = true;
}
void processFrame(EventHandler* eventHandler, halnf) override {
const auto rec = RectF({ 0, 0 }, mWindow->getSize());
mGui.setArea(rec);
mGui.updateConfigWrapper(mWidgetManager);
mGui.procWrapper(*eventHandler, rec);
}
void drawFrame(Canvas* canvas) override {
canvas->rect(mGui.mArea, { 0.1f, 0.1f, 0.1f, 1.f });
mGui.drawWrapper(*canvas);
}
private:
WidgetManager mWidgetManager;
// DockSpaceWidget mGui;
SimpleWidget3 mGui;
};
int main() {
{
SimpleGUI gui;
gui.run();
}
}

View file

@ -0,0 +1,69 @@
#include "Widgets.hpp"
namespace tp {
class SimpleWidget : public CollapsableMenu {
public:
SimpleWidget() {
this->addWidgetToMenu(&mSlider);
this->addWidgetToMenu(&mInMenuButton1);
this->addWidgetToMenu(&mInMenuButton2);
this->addWidgetToMenu(&mLabel);
mInMenuButton1.mLabel.mLabel = "Button1";
mInMenuButton1.mCallback = []() {
printf("asd\n");
};
mInMenuButton2.mLabel.mLabel = "Button2";
}
private:
ButtonWidget mInMenuButton1;
ButtonWidget mInMenuButton2;
LabelWidget mLabel;
NamedSliderWidget mSlider;
};
class SimpleWidget3 : public WorkspaceWidget {
public:
SimpleWidget3() {
mDockSpace.addSideWidget(&mButtons[0], DockWidget::BOTTOM);
mDockSpace.addSideWidget(&mButtons[1], DockWidget::RIGHT);
mDockSpace.removeSideWidget(DockWidget::BOTTOM);
mDockSpace.addSideWidget(&mButtons[0], DockWidget::TOP);
mDockSpace.addSideWidget(&mButtons[2], DockWidget::LEFT);
mDockSpace.removeSideWidget(DockWidget::TOP);
mDockSpace.addSideWidget(&mButtons[0], DockWidget::BOTTOM);
mDockSpace.addSideWidget(&mButtons[3], DockWidget::TOP);
mDockSpace.setCenterWidget(&mButtons[4]);
/*
mButtons[4].mCallback = [&]() { mDockSpace.toggleHiddenState(DockSpaceWidget::BOTTOM); };
mButtons[0].mCallback = [&]() { mDockSpace.toggleHiddenState(DockSpaceWidget::TOP); };
mButtons[2].mCallback = [&]() { mDockSpace.toggleHiddenState(DockSpaceWidget::RIGHT); };
mButtons[3].mCallback = [&]() { mDockSpace.toggleHiddenState(DockSpaceWidget::LEFT); };
*/
mButtons[0].addWidgetToMenu(&mWidget);
mButtons[1].addWidgetToMenu(&mWidget2);
}
private:
FloatingWidget mButtons[5];
SimpleWidget mWidget;
SimpleWidget mWidget2;
SimpleWidget mWidget3;
};
}

View file

@ -0,0 +1,103 @@
#include "Animations.hpp"
using namespace tp;
bool AnimValue::gInTransition = false;
halnf AnimValue::interpolate() const {
if (!mTimeAnim) {
return mVal;
}
auto dt = gCurrentTime - mTimeStart;
if (dt > mTimeAnim) dt = mTimeAnim;
auto t = (halnf) dt / (halnf) mTimeAnim;
t = (halnf) (0.511 + atan((t - 0.36) * 28) / 2.9);
t = clamp(t, 0.f, 1.f);
auto out = mValPrev + (mVal - mValPrev) * t;
return out;
}
AnimValue::AnimValue() {
mTimeStart = gCurrentTime;
}
void AnimValue::setAnimTime(halni time) { mTimeAnim = time; }
AnimValue::AnimValue(halnf val) {
mVal = val;
mValPrev = mVal;
mTimeStart = gCurrentTime;
}
bool AnimValue::inTransition() const {
auto timePassed = gCurrentTime - mTimeStart >= mTimeAnim;
return !timePassed;
}
void AnimValue::set(halnf val) {
if (!inTransition()) mValPrev = mVal;
if (val == mVal) return;
mValPrev = get();
mVal = val;
if (!inTransition()) {
mTimeStart = gCurrentTime;
}
}
halnf AnimValue::getTarget() const { return mVal; }
void AnimValue::setNoTransition(halnf val) {
mValPrev = val;
mVal = val;
mTimeStart = gCurrentTime - mTimeAnim;
}
halnf AnimValue::get() const {
if (inTransition()) {
gInTransition = true;
return interpolate();
}
return mVal;
}
void AnimValue::operator=(halnf val) {
setNoTransition(val);
}
AnimValue::operator halnf() const { return get(); }
RectF AnimRect::get() const { return { x.get(), y.get(), z.get(), w.get() }; }
RectF AnimRect::getTarget() const { return { x.getTarget(), y.getTarget(), z.getTarget(), w.getTarget() }; }
void AnimRect::setNoTransition(RectF rec) {
x.setNoTransition(rec.x);
y.setNoTransition(rec.y);
z.setNoTransition(rec.z);
w.setNoTransition(rec.w);
}
void AnimRect::setAnimTime(const halni time) {
x.setAnimTime(time);
y.setAnimTime(time);
z.setAnimTime(time);
w.setAnimTime(time);
}
void AnimRect::set(const RectF& in) {
x.set(in.x);
y.set(in.y);
z.set(in.z);
w.set(in.w);
}
AnimColor::AnimColor() : mColor() {}
RGBA AnimColor::get() const {
auto col = mColor.get();
return { col.x, col.y, col.z, col.w };
}
void AnimColor::set(const RGBA& col) { mColor.set(RectF(col.r, col.g, col.b, col.a)); }

View file

@ -0,0 +1,46 @@
#include "ButtonWidget.hpp"
using namespace tp;
ButtonWidget::ButtonWidget() {
this->setArea({ 0, 0, 100, 30 });
this->mChildWidgets.pushBack(&mLabel);
}
ButtonWidget::ButtonWidget(const std::string& label, const tp::RectF& aArea) {
this->setArea(aArea);
mLabel.mLabel = label;
this->mChildWidgets.pushBack(&mLabel);
}
bool ButtonWidget::isFired() { return this->isReleased(); }
void ButtonWidget::eventProcess(const Events&) {
mLabel.setArea(this->mArea);
if (isFired()) {
mCallback();
}
}
void ButtonWidget::eventDraw(Canvas& canvas) {
if (this->isHolding()) {
canvas.rect(this->mArea, pressedColor, rounding);
} else if (this->isFocus()) {
canvas.rect(this->mArea, hoveredColor, rounding);
} else {
canvas.rect(this->mArea, accentColor, rounding);
}
}
void ButtonWidget::setLabel(const std::string& string) { mLabel.mLabel = string; }
void ButtonWidget::eventUpdateConfiguration(WidgetManager& wm) {
wm.setActiveId("Button");
pressedColor = wm.getColor("Pressed", "Action");
hoveredColor = wm.getColor("Hovered", "Interaction");
accentColor = wm.getColor("Default", "Accent");
rounding = wm.getNumber("Rounding", "Rounding");
}

View file

@ -0,0 +1,96 @@
#include "CollapsableMenu.hpp"
using namespace tp;
CollapsableMenu::CollapsableMenu() {
this->mChildWidgets.pushBack(&mHeader);
this->mChildWidgets.pushBack(&mBody);
}
void CollapsableMenu::eventProcess(const Events&) {
if (mHeader.isReleased()) {
toggleCollapsed();
}
updateGeometry();
}
void CollapsableMenu::eventDraw(Canvas& canvas) {
if (mBorders) {
canvas.rect(this->mArea, mBorderColor, rounding);
canvas.rect(this->mArea.shrink(mBorderSize), mMenuColor, rounding);
} else {
canvas.rect(this->mArea, mMenuColor, rounding);
}
}
void CollapsableMenu::addWidgetToMenu(Widget* widget) {
mBody.addWidget(widget);
EventHandler ev;
mBody.procWrapper(ev, this->mArea);
updateGeometry();
}
void CollapsableMenu::setLabel(const std::string& string) { mHeader.mLabel = string; }
void CollapsableMenu::toggleCollapsed() { setCollapsed(!getCollapsed()); }
void CollapsableMenu::setCollapsed(bool collapsed) {
if (mLocked) return;
if (collapsed && !mCollapsed) mPrevHeight = this->mArea.size.y;
if (!collapsed && mCollapsed) this->mArea.size.y = mPrevHeight;
mCollapsed = collapsed;
}
bool CollapsableMenu::getCollapsed() const { return mCollapsed; }
void CollapsableMenu::updateGeometry() {
mHeader.setArea(getHeaderRect());
mBody.mEnable = !mCollapsed;
if (mCollapsed) {
this->mArea.size.y = headerHeight;
} else {
if (mAdjustHeight) {
this->mArea.size.y = headerHeight + getBodyRect().size.y + mPadding * 2;
}
mBody.setArea(getBodyRect());
}
}
RectF CollapsableMenu::getHeaderRect() {
RectF out = { this->mArea.pos, { this->mArea.size.x, headerHeight } };
if (mBorders) out = out.shrink(mPadding);
return out;
}
RectF CollapsableMenu::getBodyRect() {
RectF out = { Vec2F{ this->mArea.pos.x, this->mArea.pos.y + headerHeight },
Vec2F{ this->mArea.size.x, this->mArea.size.y - headerHeight - mPadding } };
out.size.y -= mBorderSize * 2;
if (mAdjustHeight) out.size.y = mBody.getContentSize();
if (mBody.getContentSize() && mBorders) {
out = out.shrink(mPadding);
out.size.y += mPadding * 2 + 1;
}
return out;
}
void CollapsableMenu::eventUpdateConfiguration(WidgetManager& wm) {
wm.setActiveId("CollapsableMenu");
headerHeight = wm.getNumber("HeaderHeight", 35);
mMenuColor = wm.getColor("MenuColor", RGBA(0, 0, 0, 1.f));
rounding = wm.getNumber("Rounding", "Rounding");
mBorderColor = wm.getColor("BorderColor", RGBA(0.16, 0.16, 0.16, 1.f));
mBorderSize = wm.getNumber("BorderSize", 2.f);
mPadding = wm.getNumber("Padding", "Padding");
}

View file

@ -0,0 +1,47 @@
#include "FloatingLayoutWidget.hpp"
using namespace tp;
FloatingLayoutWidget::FloatingLayoutWidget() = default;
void FloatingLayoutWidget::eventProcess(const tp::Events& events) {
updateActiveWindow(events);
}
void FloatingLayoutWidget::updateActiveWindow(const tp::Events& events) {
mIsPassThrough = true;
for (auto childNode = this->mChildWidgets.firstNode(); childNode; childNode = childNode->next) {
auto child = childNode->data;
if (child->mArea.isInside(events.getPointer())) {
mIsPassThrough = false;
}
}
if (events.isPressed(InputID::MOUSE1)) {
Widget* activeChild = nullptr;
for (auto childNode = this->mChildWidgets.firstNode(); childNode; childNode = childNode->next) {
auto child = childNode->data;
if (child->mArea.isInside(events.getPointer())) {
mChildWidgets.detach(childNode);
mChildWidgets.pushFront(childNode);
child->mHandlesEvents = true;
activeChild = child;
break;
}
}
if (activeChild) {
for (auto child : this->mChildWidgets) {
if (activeChild != child.data() && !child->mIsDocked) child->mHandlesEvents = false;
}
}
}
}
bool FloatingLayoutWidget::handlesEvent() const { return !mIsPassThrough; }

View file

@ -0,0 +1,89 @@
#include "FloatingWidget.hpp"
using namespace tp;
FloatingWidget::FloatingWidget() {
this->mArea = { 0, 0, 300, 300 };
this->mAdjustHeight = false;
}
void FloatingWidget::eventProcess(const Events& events) {
mActionStartRelativePos = events.getPointerPrev() - this->mArea.pos;
checkFloating(events);
if (mResizable) checkResizing(events);
CollapsableMenu::eventProcess(events);
}
void FloatingWidget::eventDraw(Canvas& canvas) {
CollapsableMenu::eventDraw(canvas);
if (!this->getCollapsed() && mResizable) {
auto rect = getResizeHandle();
canvas.rect(rect, mResizeHandleColor, 0);
canvas.circle(rect.pos - this->mBorderSize, rect.w, this->mMenuColor);
}
}
void FloatingWidget::eventUpdateConfiguration(WidgetManager& wm) {
CollapsableMenu::eventUpdateConfiguration(wm);
wm.setActiveId("FloatingWidget");
mResizeHandleSize = wm.getNumber("ResizeHandleSize", 15);
mResizeHandleColor = wm.getColor("ResizeHandleColor", RGBA(0.16, 0.16, 0.16, 1.f));
}
void FloatingWidget::checkFloating(const Events& events) {
mDropped = false;
if (this->mHeader.isHolding() && events.getPointerDelta().length2() > 4) {
mFloating = true;
}
if (mFloating && this->mHeader.isReleased()) {
mFloating = false;
this->mHeader.clearEvents();
mDropped = true;
}
if (mFloating) {
auto relativePos = events.getPointer() - this->mArea.pos;
this->mArea.pos += relativePos - mActionStartRelativePos;
}
}
void FloatingWidget::checkResizing(const Events& events) {
if (this->getCollapsed()) return;
if (events.isPressed(InputID::MOUSE1) && getResizeHandle().isInside(events.getPointer())) {
mResizing = true;
}
if (events.isReleased(InputID::MOUSE1)) {
mResizing = false;
}
if (mResizing) {
auto relativePos = events.getPointer() - this->mArea.pos;
this->mArea.size += relativePos - mActionStartRelativePos;
}
if (!this->mCollapsed) {
this->mArea.size.clamp(mMinSize, { FLT_MAX, FLT_MAX });
}
}
RectF FloatingWidget::getResizeHandle() {
auto size = Vec2F(mResizeHandleSize);
auto pos = this->mArea.pos + this->mArea.size - size;
return { pos, size };
}
bool FloatingWidget::isFloating() const { return mFloating; }
void FloatingWidget::stopFloating() {
mFloating = false;
}

View file

@ -0,0 +1,328 @@
#include "GridLayoutWidget.hpp"
using namespace tp;
DockWidget::DockWidget() {
mSideWidgets[0].side = LEFT;
mSideWidgets[1].side = TOP;
mSideWidgets[2].side = RIGHT;
mSideWidgets[3].side = BOTTOM;
}
void DockWidget::addSideWidget(Widget* widget, Side side) {
if (sideExists(side)) return;
auto& sideWidget = mSideWidgets[side];
sideWidget.widget = widget;
for (auto& order : mSideWidgets) {
if (order.order == -1) {
order.order = side;
break;
}
}
sideWidget.hidden = false;
mChildWidgets.pushBack(widget);
widget->mIsDocked = true;
}
void DockWidget::removeSideWidget(Side side) {
if (!sideExists(side)) return;
bool removed = false;
for (ualni i = 0; i < 3; i++) {
if (mSideWidgets[i].order == side) {
removed = true;
}
if (removed) {
swapV(mSideWidgets[i].order, mSideWidgets[i + 1].order);
}
}
mSideWidgets[3].order = -1;
auto widget = mSideWidgets[side].widget;
widget->mIsDocked = false;
mChildWidgets.removeNode(mChildWidgets.find(widget));
mSideWidgets[side].widget = nullptr;
}
void DockWidget::setCenterWidget(Widget* widget) {
mChildWidgets.removeNode(mChildWidgets.find(mCenterWidget));
mCenterWidget = widget;
mChildWidgets.pushBack(mCenterWidget);
}
void DockWidget::toggleHiddenState(DockWidget::Side side) {
if (!sideExists(side)) return;
mSideWidgets[side].hidden = !mSideWidgets[side].hidden;
}
void DockWidget::eventProcess(const tp::Events& events) {
calculateSideAreas();
calculateResizeHandles();
// calculateHeaderAreas();
handlePreview(events);
handleResizeEvents(events);
updateChildSideWidgets();
}
void DockWidget::eventDraw(Canvas& canvas) {
canvas.rect(this->mArea, mBackgroundColor, 0);
for (auto& sideWidget : mSideWidgets) {
if (!isSideVisible(sideWidget.side)) continue;
auto& handle = sideWidget.resizeHandle;
if (handle.active) {
canvas.rect(handle.area.shrink(mPadding / 1.5f), mResizeHandleColorActive, 0);
} else if (handle.hover) {
canvas.rect(handle.area.shrink(mPadding / 1.5f), mResizeHandleColorHovered, 0);
}
}
for (auto& sideWidget : mSideWidgets) {
if (!isSideVisible(sideWidget.side)) continue;
canvas.rect(sideWidget.headerArea, mResizeHandleColorActive, 0);
}
}
void DockWidget::eventDrawOver(Canvas& canvas) {
if (!mPreview) return;
if (mPreviewSide != NONE) canvas.rect(mPreviewArea.shrink(mPadding * 2), mPreviewColor, mRounding);
for (auto& sideWidget : mSideWidgets) {
if (sideWidget.widget) continue;
canvas.rect(sideWidget.previewHandleArea, mPreviewColor, mRounding);
}
}
void DockWidget::calculateSideAreas() {
auto startArea = this->mArea;
for (auto& sideWidget : mSideWidgets) {
const auto side = sideWidget.order;
if (side == -1) break;
if (!isSideVisible(Side(side))) continue;
bool vertical = side == TOP || side == BOTTOM;
bool opposite = side == BOTTOM || side == RIGHT;
auto& sideSize = mSideWidgets[side].absoluteSize;
auto factor = sideSize / startArea.size[vertical];
if (opposite) factor = factor * -1 + 1;
auto& area = mSideWidgets[side].area;
if (!vertical) {
const auto first = startArea.splitByFactorHL(factor);
const auto second = startArea.splitByFactorHR(factor);
area = side == LEFT ? first : second;
startArea = side == LEFT ? second : first;
} else {
const auto first = startArea.splitByFactorVT(factor);
const auto second = startArea.splitByFactorVB(factor);
area = side == TOP ? first : second;
startArea = side == TOP ? second : first;
}
area = area.shrink(mPadding);
}
mCenterArea = startArea.shrink(mPadding);
}
void DockWidget::calculateResizeHandles() {
RectF rec;
if (isSideVisible(LEFT)) {
auto& side = mSideWidgets[LEFT];
rec = { side.area.p4(), { mPadding * 2, side.area.size.y } };
side.resizeHandle = { rec, 0, mCenterArea.p3().x };
}
if (isSideVisible(RIGHT)) {
auto& side = mSideWidgets[RIGHT];
rec = { side.area.p1(), { mPadding * 2, side.area.size.y } };
rec.x -= mPadding * 2;
side.resizeHandle = { rec, 0, (this->mArea.p3() - mCenterArea.p1()).x };
}
if (isSideVisible(TOP)) {
auto& side = mSideWidgets[TOP];
rec = { side.area.p2(), { side.area.size.x, mPadding * 2 } };
side.resizeHandle = { rec, 0, mCenterArea.p2().y };
}
if (isSideVisible(BOTTOM)) {
auto& side = mSideWidgets[BOTTOM];
rec = { side.area.p1(), { side.area.size.x, mPadding * 2 } };
rec.y -= mPadding * 2;
side.resizeHandle = { rec, 0, (this->mArea.p3() - mCenterArea.p1()).y };
}
}
void DockWidget::handleResizeEvents(const Events& events) {
for (auto& sideWidget : mSideWidgets) {
auto& sideSize = sideWidget.absoluteSize;
auto& resizeHandle = sideWidget.resizeHandle;
if (resizeHandle.end < mSideSizePadding * 2) {
sideSize = resizeHandle.end / 2.f;
} else {
sideSize = clamp(sideSize, resizeHandle.start + mSideSizePadding, resizeHandle.end - mSideSizePadding);
}
}
for (auto& sideWidget : mSideWidgets) {
sideWidget.resizeHandle.hover = false;
if (sideWidget.resizeHandle.area.isInside(events.getPointerPrev())) {
sideWidget.resizeHandle.hover = true;
}
}
for (auto& sideWidget : mSideWidgets) {
sideWidget.resizeHandle.active = false;
}
if (mPreview) return;
auto resizeSideWidget = [&events](SideWidgetData& sideWidget, Vec2F deltaVec) {
halnf delta = deltaVec[(sideWidget.side == TOP || sideWidget.side == BOTTOM)];
if (sideWidget.side == BOTTOM || sideWidget.side == RIGHT) delta *= -1;
sideWidget.absoluteSize += delta;
sideWidget.resizeHandle.active = true;
};
if (events.isDown(InputID::MOUSE1)) {
for (auto& sideWidget : mSideWidgets) {
if (sideWidget.resizeHandle.area.isInside(events.getPointerPrev())) {
resizeSideWidget(sideWidget, events.getPointerDelta());
}
}
}
if (events.isDown(InputID::LEFT_ALT)) {
const auto pointer = events.getPointerPrev();
if (!mCenterArea.isInside(pointer)) return;
if (events.isPressed(InputID::MOUSE1)) {
for (auto i : { 0, 1 }) {
const auto step = mCenterArea.size[i] / 3.f;
const auto rec1 = mCenterArea.pos[i];
const auto pos = pointer[i];
if (pos > rec1 && pos < rec1 + step) resizeType[i] = 0;
if (pos > rec1 + step && pos < rec1 + step * 2) resizeType[i] = 1;
if (pos > rec1 + step * 2) resizeType[i] = 2;
}
} else if (events.isDown(InputID::MOUSE1)) {
const auto deltaVec = events.getPointerDelta();
if (resizeType[0] == 0) resizeSideWidget(mSideWidgets[LEFT], deltaVec);
if (resizeType[1] == 0) resizeSideWidget(mSideWidgets[TOP], deltaVec);
if (resizeType[0] == 2) resizeSideWidget(mSideWidgets[RIGHT], deltaVec);
if (resizeType[1] == 2) resizeSideWidget(mSideWidgets[BOTTOM], deltaVec);
}
}
}
void DockWidget::updateChildSideWidgets() {
// Update Child Widgets
{
for (ualni i = 0; i < 4; i++) {
if (!sideExists(Side(i))) continue;
auto widget = mSideWidgets[i].widget;
if (!isSideVisible(Side(i))) {
widget->mEnable = false;
} else {
widget->setArea(mSideWidgets[i].area);
widget->mEnable = true;
}
}
if (mCenterWidget) mCenterWidget->setArea(mCenterArea);
}
// update depth order
/*
if (mChildWidgets.size() > 1) {
for (auto sideChild : mSideWidgets) {
if (!sideChild) continue;
for (auto childNode = mChildWidgets.firstNode(); childNode; childNode = childNode->next) {
if (childNode->data == sideChild) {
mChildWidgets.detach(childNode);
mChildWidgets.pushBack(childNode);
break;
}
}
}
}
*/
}
void DockWidget::calculateHeaderAreas() {
for (ualni i = 0; i < 4; i++) {
if (!isSideVisible(Side(i))) continue;
auto& area = mSideWidgets[i].area;
const auto factor = mHeaderSize / area.size.y;
mSideWidgets[i].headerArea = area.splitByFactorVT(factor);
area = area.splitByFactorVB(factor);
mSideWidgets[i].headerArea.size.y -= mPadding;
}
}
bool DockWidget::isSideVisible(DockWidget::Side side) {
return sideExists(side) && !mSideWidgets[side].hidden;
}
bool DockWidget::sideExists(DockWidget::Side side) { return mSideWidgets[side].widget; }
ualni DockWidget::getVisibleSidesSize() {
ualni out = 0;
for (ualni i = 0; i < 4; i++) {
if (isSideVisible(Side(i))) out++;
}
return out;
}
DockWidget::Side DockWidget::getPreviewSide() { return mPreviewSide; }
void DockWidget::handlePreview(const Events& events) {
if (!mPreview) {
mPreviewSide = NONE;
return;
}
const halnf factor = 0.3;
const halnf handleFactor = 0.1;
const auto handleSize = min(mCenterArea.size.x, mCenterArea.size.y) * handleFactor;
for (auto& sideWidget : mSideWidgets) {
if (sideWidget.widget) continue;
switch (sideWidget.side) {
case TOP: sideWidget.area = mCenterArea.splitByFactorVT(factor); break;
case BOTTOM: sideWidget.area = mCenterArea.splitByFactorVB(1 - factor); break;
case LEFT: sideWidget.area = mCenterArea.splitByFactorHL(factor); break;
case RIGHT: sideWidget.area = mCenterArea.splitByFactorHR(1 - factor); break;
default: break;
}
sideWidget.area = sideWidget.area.shrink(mPadding * 2);
sideWidget.previewHandleArea = sideWidget.area.getSizedFromCenter({ handleSize, handleSize });
}
mPreviewArea = {};
mPreviewSide = NONE;
for (auto& sideWidget : mSideWidgets) {
if (sideWidget.widget) continue;
if (sideWidget.previewHandleArea.isInside(events.getPointer())) {
mPreviewArea = sideWidget.area;
mPreviewSide = sideWidget.side;
}
}
}

View file

@ -0,0 +1,17 @@
#include "LabelWidget.hpp"
using namespace tp;
LabelWidget::LabelWidget() { this->mArea = { 0, 0, 100, 30 }; }
void LabelWidget::eventDraw(Canvas& canvas) {
canvas.text(mLabel.c_str(), this->mArea, fontSize, Canvas::LC, padding, fontColor);
}
void LabelWidget::eventUpdateConfiguration(WidgetManager& wm) {
wm.setActiveId("Label");
fontSize = wm.getNumber("Size", "FontSize");
padding = wm.getNumber("Padding", "Padding");
fontColor = wm.getColor("Default", "Front");
}

View file

@ -0,0 +1,179 @@
#include "ScrollableWidget.hpp"
#include "../../WidgetsNew/public/widgets/ScrollableWidget.hpp"
using namespace tp;
ScrollBarWidget::ScrollBarWidget() = default;
void ScrollBarWidget::eventProcess(const Events& events) {
auto area = getHandle();
mHovered = getHandleHandle().isInside(events.getPointer());
if (mSizeFraction > 1.f) {
mPositionFraction = 0;
return;
}
if (events.getScrollY() != 0 && mArea.isInside(events.getPointer())) {
auto offset = events.getScrollY() < 0 ? 1.0f : -1.0f;
mPositionFraction += mSizeFraction * offset * 0.3f;
mPositionFraction = tp::clamp(mPositionFraction, 0.f, 1.f - mSizeFraction);
}
if (events.isPressed(InputID::MOUSE1) && area.isInside(events.getPointer())) {
mIsScrolling = true;
} else if (events.isReleased(InputID::MOUSE1)) {
mIsScrolling = false;
}
if (mIsScrolling) {
tp::halnf pos = events.getPointer().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 ScrollBarWidget::eventDraw(Canvas& canvas) {
auto area = getHandle();
if (mSizeFraction > 1.f) return;
// if (!areaParent.isOverlap(getHandle())) return;
tp::RGBA col = mHandleColor;
if (mIsScrolling) {
col = mScrollingColor;
} else if (mHovered) {
col = mHoveredColor;
}
canvas.rect(area, mDefaultColor, mRounding);
canvas.rect(getHandleHandle(), col, mRounding);
}
RectF ScrollBarWidget::getHandleHandle() const {
auto area = getHandle();
auto sliderSize = tp::clamp(area.w * mSizeFraction, mMinSize * 2, area.w);
auto diffSize = sliderSize - area.w * mSizeFraction;
return { area.x, area.y + (area.w - diffSize) * mPositionFraction, area.z, sliderSize };
}
RectF ScrollBarWidget::getViewport() const {
if (mSizeFraction > 1.f) {
return this->mArea;
}
return { this->mArea.x, this->mArea.y, this->mArea.z - mHandleSize, this->mArea.w };
}
RectF ScrollBarWidget::getHandle() const {
return { this->mArea.x + this->mArea.z - mHandleSize + mPadding,
this->mArea.y + mPadding,
mHandleSize - mPadding * 2,
this->mArea.w - mPadding * 2 };
}
void ScrollBarWidget::eventUpdateConfiguration(WidgetManager& wm) {
wm.setActiveId("Scrollbar");
mDefaultColor = wm.getColor("Default", "Base");
mHandleColor = wm.getColor("Handle", "Accent");
mHoveredColor = wm.getColor("Hovered", "Interaction");
mScrollingColor = wm.getColor("Scrolling", "Action");
mPadding = wm.getNumber("Padding", "Padding");
mHandleSize = wm.getNumber("HandleSize", 20.f);
mMinSize = wm.getNumber("MinSize", 20.f);
mRounding = wm.getNumber("Rounding", "Rounding");
}
ScrollableWindow::ScrollableWindow() {
this->mChildWidgets.pushBack(&mScroller);
this->mChildWidgets.pushBack(&mContentWidget);
}
ScrollableWindow::~ScrollableWindow() = default;
void ScrollableWindow::eventProcess(const Events& events) {
List<Widget*>& content = mContentWidget.mChildWidgets;
// to account all changed geometry of child widgets
for (auto widget : content) {
widget->procWrapper(events, mContentWidget.mVisibleArea);
}
updateContents(content);
updateContentSize(content);
const auto padding = mPadding;
mScroller.mSizeFraction = mContentWidget.mArea.w / mContentSize;
mScroller.setArea(this->mArea);
mContentWidget.setArea(mScroller.getViewport());
if (mScroller.mSizeFraction > 1.f) {
setOffset(content, 0);
} else {
setOffset(content, (-mScroller.mPositionFraction) * mContentSize);
}
for (auto widget : content) {
widget->setArea({ mContentWidget.mArea.x + padding,
mContentWidget.mArea.y + widget->mArea.y,
mScroller.getViewport().z - padding * 2,
widget->mArea.w });
}
}
void ScrollableWindow::addWidget(Widget* widget) {
mContentWidget.mChildWidgets.pushBack(widget);
List<Widget*>& content = mContentWidget.mChildWidgets;
updateContents(content);
updateContentSize(content);
}
void ScrollableWindow::clearContent() { mContentWidget.mChildWidgets.removeAll(); }
List<Widget*>& ScrollableWindow::getContent() { return mContentWidget.mChildWidgets; }
void ScrollableWindow::eventUpdateConfiguration(WidgetManager& wm) {
wm.setActiveId("ScrollableWidget");
mPadding = wm.getNumber("Padding", "Padding");
}
[[nodiscard]] halnf ScrollableWindow::getContentSize() const { return mContentSize; }
void ScrollableWindow::updateContents(List<Widget*>& contentWidgets) {
if (!contentWidgets.size()) {
return;
}
const halnf offset = contentWidgets.first()->mArea.y + mPadding;
halnf start = 0;
for (auto widget : contentWidgets) {
widget->mArea.y = start;
start += widget->mArea.w + mPadding;
}
for (auto widget : contentWidgets) {
widget->mArea.y += offset;
}
}
void ScrollableWindow::updateContentSize(List<Widget*>& contentWidgets) {
mContentSize = 0;
if (contentWidgets.size()) {
mContentSize = contentWidgets.last()->mArea.y - contentWidgets.first()->mArea.y;
mContentSize += contentWidgets.last()->mArea.w;
mContentSize += 2 * mPadding;
}
}
void ScrollableWindow::setOffset(List<Widget*>& contentWidgets, const halnf offset) {
if (!contentWidgets.size()) return;
auto newOffset = offset - contentWidgets.first()->mArea.y + mPadding;
for (auto widget : contentWidgets) {
widget->mArea.y += newOffset;
}
}

View file

@ -0,0 +1,62 @@
#include "SliderWidget.hpp"
using namespace tp;
SliderWidget::SliderWidget() = default;
void SliderWidget::eventProcess(const Events& events) {
if (this->isPressed()) {
mIsSliding = true;
} else if (events.isReleased(InputID::MOUSE1)) {
mIsSliding = false;
}
if (mIsSliding) {
mFactor = (events.getPointer().x - this->mArea.x - handleSize / 2.f) / (this->mArea.z - handleSize);
}
mFactor = tp::clamp(mFactor, 0.f, 1.f);
}
void SliderWidget::eventDraw(Canvas& canvas) {
canvas.rect(this->mArea, handleColor, rounding);
canvas.rect(this->mArea.shrink(borderSize), defaultColor, rounding);
canvas.rect(getHandle(), handleColor, rounding);
}
RectF SliderWidget::getHandle() const {
const auto left = this->mArea.x + (this->mArea.z - handleSize) * mFactor;
return { left, this->mArea.y, handleSize, this->mArea.w };
}
void SliderWidget::eventUpdateConfiguration(WidgetManager& wm) {
wm.setActiveId("Slider");
defaultColor = wm.getColor("Default", "Base");
handleColor = wm.getColor("Handle", RGBA(0.3f, 0.3f, 0.3f, 1.f));
borderSize = wm.getNumber("BorderSize", 2);
handleSize = wm.getNumber("HandleSize", 15.f);
rounding = wm.getNumber("Rounding", "Rounding");
}
NamedSliderWidget::NamedSliderWidget(const char* name) {
mLabel.mLabel = name;
this->mArea = { 0, 0, 100, 30 };
this->mChildWidgets.pushBack(&mSlider);
this->mChildWidgets.pushBack(&mLabel);
}
void NamedSliderWidget::eventProcess(const Events& events) {
const auto widthFirst = this->mArea.z * mFactor;
const auto widthSecond = this->mArea.z * (1.f - mFactor);
RectF rec = this->mArea;
rec.size.x = widthFirst;
mLabel.setArea(rec);
rec.pos.x += widthFirst;
rec.size.x = widthSecond;
mSlider.setArea(rec);
}

View file

@ -0,0 +1,57 @@
#include "TextInputWidget.hpp"
#include "imgui.h"
#include "imgui_internal.h"
using namespace tp;
TextInputWidget::TextInputWidget() = default;
void TextInputWidget::eventDraw(Canvas&) {
nChanged = false;
const auto col = mAccentColor;
const auto colSel = mHoveredColor;
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, mRounding * 1.5f);
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, { mPadding, mPadding });
// ImGui::PushID((int) alni(this));
ImGui::Begin(
mId.c_str(),
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.c_str(), mBuff, mMaxBufferSize, { this->mArea.z, this->mArea.w }, 0)) {
mValue = mBuff;
nChanged = true;
}
}
ImGui::End();
ImGui::PopStyleVar(3);
}
void TextInputWidget::eventUpdateConfiguration(WidgetManager& wm) {
wm.setActiveId("TextInput");
mAccentColor = wm.getColor("Accent", "Accent");
mBaseColor = wm.getColor("Base", "Base");
mRounding = wm.getNumber("Rounding", "Rounding");
mHoveredColor = wm.getColor("Hovered", "Accent");
mPadding = wm.getNumber("Padding", "Padding");
}

View file

@ -0,0 +1,140 @@
#include "WidgetBase.hpp"
using namespace tp;
Widget::Widget() { mArea = { 0, 0, 100, 100 }; }
Widget::~Widget() = default;
void Widget::procWrapper(const Events& events, const RectF& parentArea) {
if (!mEnable) return;
checkVisibility(events, parentArea);
if (!mVisible) return;
checkFocus(events);
checkClicked(events);
if (mHandlesEvents) {
for (auto child : mChildWidgets) {
child->procWrapper(events, mVisibleArea);
}
eventProcess(events);
}
}
void Widget::drawWrapper(Canvas& canvas) {
if (!mEnable || !mVisible) return;
eventDraw(canvas);
// draw child widgets
canvas.pushClamp(this->mArea);
for (auto child = mChildWidgets.lastNode(); child; child = child->prev) {
child->data->drawWrapper(canvas);
}
canvas.popClamp();
eventDrawOver(canvas);
}
void Widget::updateConfigWrapper(WidgetManager& wm) {
wm.setActiveId("Global");
eventUpdateConfiguration(wm);
for (auto child : mChildWidgets) {
child->updateConfigWrapper(wm);
}
}
void Widget::eventProcess(const Events& events) {}
void Widget::eventDraw(Canvas& canvas) {}
void Widget::eventDrawOver(Canvas& canvas) {}
void Widget::eventUpdateConfiguration(WidgetManager& wm) {}
void Widget::eventVisible(const Events& events) {}
void Widget::eventNotVisible(const Events& events) {}
void Widget::eventFocusEnter(const Events& events) {}
void Widget::eventFocusLeave(const Events& events) {}
void Widget::eventPressed(const Events& events) {}
void Widget::eventReleased(const Events& events) {}
void Widget::checkVisibility(const Events& events, const RectF& parentArea) {
const bool currentVisibility = parentArea.isOverlap(getArea());
parentArea.calcIntersection(mArea, mVisibleArea);
if (currentVisibility != mVisible) {
if (currentVisibility) eventVisible(events);
else eventNotVisible(events);
}
mVisible = currentVisibility;
if (!mVisible) {
mHolding = false;
mPressed = false;
mReleased = false;
}
}
void Widget::checkFocus(const Events& events) {
const bool currentFocus = mVisibleArea.isInside(events.getPointerPrev());
if (currentFocus != mInFocus) {
if (currentFocus) eventFocusEnter(events);
else eventFocusLeave(events);
}
mInFocus = currentFocus;
if (!mInFocus) {
mHolding = false;
mPressed = false;
mReleased = false;
}
}
void Widget::checkClicked(const Events& events) {
mPressed = false;
mReleased = false;
if (!mInFocus) return;
if (mHolding) {
if (events.isReleased(InputID::MOUSE1)) {
eventReleased(events);
mReleased = true;
mHolding = false;
}
} else {
if (events.isPressed(InputID::MOUSE1) && mVisibleArea.isInside(events.getPointer())) {
eventPressed(events);
mHolding = true;
mPressed = true;
}
}
}
void Widget::setEnable(bool enable) { mEnable = enable; }
void Widget::setVisible(bool visible) { mVisible = visible; }
void Widget::setArea(const RectF& area) { mArea = area; }
const RectF& Widget::getArea() const { return mArea; }
bool Widget::isFocus() const { return mInFocus; }
bool Widget::isPressed() const { return mPressed; }
bool Widget::isReleased() const { return mReleased; }
bool Widget::isHolding() const { return mHolding; }
void Widget::clearEvents() {
mReleased = false;
mPressed = false;
mHolding = false;
}

View file

@ -0,0 +1,74 @@
#include "WidgetManager.hpp"
using namespace tp;
WidgetManager::WidgetManager() { initGlobalParameters(); }
WidgetManager::~WidgetManager() { mConfigurations.removeAll(); }
const RGBA& WidgetManager::getColor(const std::string& parameterId, const RGBA& defaultValue) {
WidgetConfig& config = getWidgetConfig(mActiveId);
Parameter& parameter = getParameter(config, parameterId, Parameter(defaultValue));
return parameter.color;
}
const RGBA& WidgetManager::getColor(const std::string& parameterId, const char* globalRef) {
WidgetConfig& config = getWidgetConfig(mActiveId);
Parameter& parameter = getParameter(config, parameterId, Parameter(globalRef, Parameter::COL));
return parameter.color;
}
halnf WidgetManager::getNumber(const std::string& parameterId, halnf defaultValue) {
WidgetConfig& config = getWidgetConfig(mActiveId);
Parameter& parameter = getParameter(config, parameterId, Parameter(defaultValue));
return parameter.value;
}
halnf WidgetManager::getNumber(const std::string& parameterId, const char* globalRef) {
WidgetConfig& config = getWidgetConfig(mActiveId);
Parameter& parameter = getParameter(config, parameterId, Parameter(globalRef, Parameter::VAL));
return parameter.value;
}
void WidgetManager::setActiveId(const std::string& id) { mActiveId = id; }
WidgetConfig& WidgetManager::getWidgetConfig(const std::string& id) {
auto idx = mConfigurations.presents(id);
if (idx) return mConfigurations.getSlotVal(idx);
mConfigurations.put(id, {});
return mConfigurations.get(id);
}
WidgetManager::Parameter& WidgetManager::getParameter(WidgetConfig& config, const std::string& id, const Parameter& defaultValue) {
auto idx = config.mParameters.presents(id);
if (!idx) {
config.mParameters.put(id, defaultValue);
}
Parameter& parameter = config.mParameters.get(id);
if (parameter.globalReference) {
return mGlobalConfig.mParameters.get(parameter.globalId);
} else {
return parameter;
}
}
void WidgetManager::initGlobalParameters() {
auto& params = mGlobalConfig.mParameters;
params.put("FontSize", Parameter(15.f));
params.put("FontSizeDim", Parameter(12.f));
params.put("Rounding", Parameter(5.f));
params.put("Padding", Parameter(5.f));
params.put("HandleSize", Parameter(5.f));
params.put("Background", Parameter(RGBA{ 0.03f, 0.03f, 0.03f, 1.f }));
params.put("Base", Parameter(RGBA{ 0.07f, 0.07f, 0.07f, 1.f }));
params.put("Accent", Parameter(RGBA{ 0.13f, 0.13f, 0.13f, 1.f }));
params.put("Interaction", Parameter(RGBA{ 0.33f, 0.33f, 0.3f, 1.f }));
params.put("Action", Parameter(RGBA{ 0.44f, 0.44f, 0.4f, 1.f }));
params.put("Front", Parameter(RGBA{ 1.f, 1.f, 1.f, 1.f }));
params.put("FrontDim", Parameter(RGBA{ 0.7f, 0.7f, 0.7f, 1.f }));
}

View file

@ -0,0 +1,73 @@
#include "WorkspaceWidget.hpp"
using namespace tp;
WorkspaceWidget::WorkspaceWidget() {
this->mChildWidgets.pushBack(&mFloatingLayer);
this->mChildWidgets.pushBack(&mDockSpace);
}
void WorkspaceWidget::eventProcess(const Events& events) {
mDockSpace.setArea(this->mArea);
mFloatingLayer.setArea(this->mArea);
mDockSpace.mPreview = false;
mDockSpace.mHandlesEvents = !mFloatingLayer.handlesEvent();
for (auto floatingChild : mFloatingLayer.mChildWidgets) {
auto widget = dynamic_cast<FloatingWidget*>(floatingChild.data());
if (!widget) continue;
if (widget->mDropped) {
auto side = mDockSpace.getPreviewSide();
if (side != DockWidget::NONE) {
mFloatingLayer.mChildWidgets.removeNode(mFloatingLayer.mChildWidgets.find(widget));
widget->setCollapsed(false);
widget->stopFloating();
mDockSpace.addSideWidget(widget, side);
}
}
if (widget->isFloating()) {
mDockSpace.mHandlesEvents = true;
mDockSpace.mPreview = true;
}
}
for (auto& dockedChild : mDockSpace.mSideWidgets) {
auto widget = dynamic_cast<FloatingWidget*>(dockedChild.widget);
if (!widget) continue;
if (widget->isFloating()) {
mDockSpace.removeSideWidget(dockedChild.side);
mFloatingLayer.mChildWidgets.pushBack(widget);
widget->mArea.pos = events.getPointer() - 20;
widget->mArea.size = mDefaultFloatSize;
widget->mResizable = true;
// widget->mBorders = true;
}
}
// toggle hide state
if (events.isDown(InputID::LEFT_ALT)) {
if (events.isPressed(InputID::N1)) {
mDockSpace.toggleHiddenState(DockWidget::LEFT);
} else if (events.isPressed(InputID::N2)) {
mDockSpace.toggleHiddenState(DockWidget::TOP);
} else if (events.isPressed(InputID::N3)) {
mDockSpace.toggleHiddenState(DockWidget::RIGHT);
} else if (events.isPressed(InputID::N4)) {
mDockSpace.toggleHiddenState(DockWidget::BOTTOM);
}
}
for (auto& dockedChild : mDockSpace.mSideWidgets) {
auto widget = dynamic_cast<FloatingWidget*>(dockedChild.widget);
if (!widget) continue;
widget->setCollapsed(false);
widget->mResizable = false;
// widget->mBorders = false;
}
}

View file

@ -0,0 +1,57 @@
#pragma once
#include "Color.hpp"
#include "Rect.hpp"
#include "Timing.hpp"
namespace tp {
class AnimValue {
halnf mValPrev = 0;
halnf mVal = 0;
time_ms mTimeStart = 0;
halni mTimeAnim = 250;
static bool gInTransition;
private:
[[nodiscard]] halnf interpolate() const;
public:
AnimValue();
explicit AnimValue(halnf val);
[[nodiscard]] halnf prev() const { return mValPrev; }
void set(halnf val);
void setNoTransition(halnf);
void setAnimTime(halni time);
[[nodiscard]] halnf get() const;
[[nodiscard]] halnf getTarget() const;
[[nodiscard]] bool inTransition() const;
explicit operator halnf() const;
void operator=(halnf val);
};
class AnimRect : Rect<AnimValue> {
public:
AnimRect() {
setAnimTime(450);
setNoTransition({ 0, 0, 0, 0 });
}
[[nodiscard]] RectF get() const;
[[nodiscard]] RectF getTarget() const;
void setNoTransition(RectF);
void set(const RectF&);
void setAnimTime(halni time_ms);
};
class AnimColor {
public:
AnimColor();
AnimRect mColor;
[[nodiscard]] RGBA get() const;
void set(const RGBA&);
};
};

View file

@ -0,0 +1,35 @@
#pragma once
#include "LabelWidget.hpp"
#include <functional>
namespace tp {
class ButtonWidget : public Widget {
public:
// enum State { NONE, ANTICIPATION, ACTIVATED, CONFIRMED };
public:
ButtonWidget();
ButtonWidget(const std::string& label, const tp::RectF& aArea);
bool isFired();
void eventProcess(const Events&) override;
void eventDraw(Canvas& canvas) override;
void setLabel(const std::string& string);
public:
void eventUpdateConfiguration(WidgetManager& wm) override;
public:
LabelWidget mLabel;
// State mStat = NONE;
RGBA pressedColor;
RGBA hoveredColor;
RGBA accentColor;
halnf rounding = 0;
std::function<void()> mCallback = [](){};
};
}

View file

@ -0,0 +1,51 @@
#pragma once
#include "ScrollableWidget.hpp"
#include "ButtonWidget.hpp"
namespace tp {
class CollapsableMenu : public Widget {
public:
CollapsableMenu();
void eventProcess(const Events&) override;
void eventDraw(Canvas& canvas) override;
public:
void addWidgetToMenu(Widget* widget);
void setLabel(const std::string& string);
void toggleCollapsed();
void setCollapsed(bool collapsed);
[[nodiscard]] bool getCollapsed() const;
void updateGeometry();
private:
RectF getHeaderRect();
RectF getBodyRect();
public:
void eventUpdateConfiguration(WidgetManager& wm) override;
protected:
ScrollableWindow mBody;
LabelWidget mHeader;
RGBA mMenuColor;
RGBA mBorderColor;
halnf headerHeight = 30;
halnf rounding = 0;
halnf mBorderSize = 0;
halnf mPadding = 0;
halnf mPrevHeight = 200;
bool mCollapsed = true;
bool mLocked = false;
bool mAdjustHeight = true;
public:
bool mBorders = true;
};
}

View file

@ -0,0 +1,18 @@
#include "FloatingWidget.hpp"
namespace tp {
class FloatingLayoutWidget : public Widget {
public:
FloatingLayoutWidget();
void eventProcess(const Events& events) override;
[[nodiscard]] bool handlesEvent() const;
private:
void updateActiveWindow(const tp::Events& events);
private:
bool mIsPassThrough = false;
};
}

View file

@ -0,0 +1,38 @@
#pragma once
#include "CollapsableMenu.hpp"
namespace tp {
class FloatingWidget : public CollapsableMenu {
public:
FloatingWidget();
void eventProcess(const Events& events) override;
void eventDraw(Canvas& canvas) override;
void eventUpdateConfiguration(WidgetManager& wm) override;
[[nodiscard]] bool isFloating() const;
void stopFloating();
private:
void checkFloating(const Events& events);
void checkResizing(const Events& events);
RectF getResizeHandle();
private:
Vec2F mMinSize = { 70, 70 };
halnf mResizeHandleSize = 10;
RGBA mResizeHandleColor = {};
bool mFloating = false;
bool mResizing = false;
Vec2F mActionStartRelativePos = {};
public:
bool mResizable = true;
bool mDropped = false;
};
}

View file

@ -0,0 +1,88 @@
#include "WidgetBase.hpp"
namespace tp {
class DockWidget : public Widget {
public:
enum Side { LEFT, TOP, RIGHT, BOTTOM, NONE };
private:
struct ResizeHandle {
RectF area{ 0, 0, 0, 0 };
halnf start{ 0 };
halnf end{ 0 };
bool active = false;
bool hover = false;
};
struct SideWidgetData {
Widget* widget = nullptr;
bool hidden = false;
halnf absoluteSize = 200;
alni order = -1;
Side side = { TOP };
RectF area = {};
RectF headerArea = {};
RectF previewHandleArea = {};
ResizeHandle resizeHandle;
};
public:
DockWidget();
void eventProcess(const Events& events) override;
void eventDraw(Canvas& canvas) override;
void eventDrawOver(Canvas& canvas) override;
void addSideWidget(Widget* widget, Side side);
void removeSideWidget(Side side);
void toggleHiddenState(Side side);
void setCenterWidget(Widget* widget);
Side getPreviewSide();
private:
void calculateSideAreas();
void calculateResizeHandles();
void handleResizeEvents(const Events& events);
void updateChildSideWidgets();
void calculateHeaderAreas();
bool isSideVisible(Side side);
bool sideExists(DockWidget::Side side);
ualni getVisibleSidesSize();
void handlePreview(const Events& events);
private:
RectF mPreviewArea = {};
Side mPreviewSide = NONE;
int resizeType[2] = { 0, 0 };
public:
SideWidgetData mSideWidgets[4];
private:
Widget* mCenterWidget = nullptr;
RectF mCenterArea {};
// Parameters
halnf mSideSizePadding = 150.f;
halnf mPadding = 4;
halnf mHeaderSize = 27;
halnf mRounding = 10;
Vec2F mPreviewHandleSize = { 50, 50 };
RGBA mResizeHandleColorHovered = RGBA(0.3, 0.3, 0.3, 1);
RGBA mResizeHandleColorActive = RGBA(0.6, 0.6, 0.6, 1);
RGBA mBackgroundColor = RGBA(0, 0, 0, 1);
RGBA mPreviewColor = RGBA(0.6, 0.6, 0.6, 1);
public:
bool mPreview = false;
};
}

View file

@ -0,0 +1,23 @@
#pragma once
#include "WidgetBase.hpp"
namespace tp {
class LabelWidget : public Widget {
public:
LabelWidget();
void eventDraw(Canvas& canvas) override;
public:
void eventUpdateConfiguration(WidgetManager& wm) override;
public:
std::string mLabel = "Label";
halnf fontSize = 10;
halnf padding = 0;
RGBA fontColor = { 1, 1, 1, 1 };
};
}

View file

@ -0,0 +1,71 @@
#pragma once
#include "WidgetBase.hpp"
#include "Buffer.hpp"
namespace tp {
class ScrollBarWidget : public Widget {
public:
ScrollBarWidget();
// takes whole area
void eventProcess(const Events& events) override;
void eventDraw(Canvas& canvas) override;
RectF getHandleHandle() const;
RectF getViewport() const;
RectF getHandle() const;
public:
void eventUpdateConfiguration(WidgetManager& wm) override;
public:
bool mIsScrolling = false;
halnf mSizeFraction = 1.f;
halnf mPositionFraction = 0.f;
bool mHovered = false;
RGBA mDefaultColor;
RGBA mHandleColor;
RGBA mHoveredColor;
RGBA mScrollingColor;
halnf mPadding = 0;
halnf mHandleSize = 10;
halnf mMinSize = 10;
halnf mRounding = 10;
};
class ScrollableWindow : public Widget {
public:
ScrollableWindow();
virtual ~ScrollableWindow();
// takes whole area
void eventProcess(const Events& events) override;
void addWidget(Widget* widget);
void clearContent();
List<Widget*>& getContent();
void eventUpdateConfiguration(WidgetManager& wm) override;
[[nodiscard]] halnf getContentSize() const;
private:
void updateContents(List<Widget*>& contentWidgets);
// ready content size
void updateContentSize(List<Widget*>& contentWidgets);
void setOffset(List<Widget*>& contentWidgets, const halnf offset);
private:
Widget mContentWidget;
ScrollBarWidget mScroller;
halnf mContentSize = 0;
halnf mPadding = 0;
};
}

View file

@ -0,0 +1,41 @@
#pragma once
#include "LabelWidget.hpp"
namespace tp {
class SliderWidget : public Widget {
public:
SliderWidget();
void eventProcess(const Events& events) override;
void eventDraw(Canvas& canvas) override;
RectF getHandle() const;
public:
void eventUpdateConfiguration(WidgetManager& wm) override;
public:
halnf mFactor = 0.f;
bool mIsSliding = false;
RGBA defaultColor;
RGBA handleColor;
halnf handleSize = 0;
halnf rounding = 0;
halnf borderSize = 2;
};
class NamedSliderWidget : public Widget {
public:
explicit NamedSliderWidget(const char* name = "Value");
void eventProcess(const Events& events) override;
public:
SliderWidget mSlider;
LabelWidget mLabel;
halnf mFactor = 0.5f;
};
}

View file

@ -0,0 +1,29 @@
#pragma once
#include "WidgetBase.hpp"
namespace tp {
class TextInputWidget : public Widget {
public:
TextInputWidget();
void eventDraw(Canvas&) override;
public:
void eventUpdateConfiguration(WidgetManager& wm) override;
public:
enum { mMaxBufferSize = 512 };
char mBuff[mMaxBufferSize] = "";
bool nChanged = false;
std::string mValue;
std::string mId = "id";
bool mMultiline = false;
RGBA mAccentColor;
RGBA mHoveredColor;
RGBA mBaseColor;
halnf mRounding = 0;
halnf mPadding = 0;
};
}

View file

@ -0,0 +1,78 @@
#pragma once
#include "Graphics.hpp"
#include "EventHandler.hpp"
#include "WidgetManager.hpp"
#include "List.hpp"
namespace tp {
using Events = EventHandler;
class Widget {
public:
Widget();
virtual ~Widget();
void procWrapper(const Events& events, const RectF& parentArea);
void drawWrapper(Canvas& canvas);
void updateConfigWrapper(WidgetManager& wm);
virtual void eventProcess(const Events& events);
// draws before child widgets
virtual void eventDraw(Canvas& canvas);
// draws overlay after child widgets
virtual void eventDrawOver(Canvas& canvas);
virtual void eventUpdateConfiguration(WidgetManager& wm);
virtual void eventVisible(const Events& events);
virtual void eventNotVisible(const Events& events);
virtual void eventFocusEnter(const Events& events);
virtual void eventFocusLeave(const Events& events);
virtual void eventPressed(const Events& events);
virtual void eventReleased(const Events& events);
public:
void setEnable(bool enable);
void setVisible(bool visible);
void setArea(const RectF& area);
[[nodiscard]] const RectF& getArea() const;
[[nodiscard]] bool isFocus() const;
[[nodiscard]] bool isPressed() const;
[[nodiscard]] bool isReleased() const;
[[nodiscard]] bool isHolding() const;
void clearEvents();
private:
void checkVisibility(const Events& events, const RectF& parentArea);
void checkFocus(const Events& events);
void checkClicked(const Events& events);
public:
RectF mArea;
RectF mVisibleArea;
List<Widget*> mChildWidgets;
bool mVisible = false;
bool mEnable = true;
bool mHandlesEvents = true;
bool mInFocus = false;
bool mHolding = false;
bool mPressed = false;
bool mReleased = false;
// docking
bool mIsDocked = false;
};
}

View file

@ -0,0 +1,89 @@
#pragma once
#include "Animations.hpp"
#include "Map.hpp"
#include "Rect.hpp"
#include "InputCodes.hpp"
#include "Buffer.hpp"
namespace tp {
struct WidgetConfig {
struct Shortcut {
struct Condition {
std::string name;
std::string state;
};
std::string callbackName;
Shortcut() = default;
Shortcut(const InitialierList<Condition>&) {}
};
struct Parameter {
enum Type { NONE, VAL, COL };
halnf value = 0.f;
RGBA color = {};
Type type = NONE;
bool globalReference = false;
std::string globalId;
Parameter() = default;
explicit Parameter(halnf val) {
type = VAL;
value = val;
}
explicit Parameter(const RGBA& val) {
type = COL;
color = val;
}
explicit Parameter(const std::string& id, const Type& referenceType) {
type = referenceType;
globalId = id;
globalReference = true;
}
};
Map<std::string, Parameter> mParameters;
Buffer<Shortcut> mShortcuts;
};
class WidgetManager {
public:
using Parameter = WidgetConfig::Parameter;
public:
WidgetManager();
~WidgetManager();
const RGBA& getColor(const std::string& parameterId, const RGBA& defaultValue);
const RGBA& getColor(const std::string& parameterId, const char* globalRef);
halnf getNumber(const std::string& parameterId, halnf defaultValue);
halnf getNumber(const std::string& parameterId, const char* globalRef);
void setActiveId(const std::string& id);
private:
WidgetConfig& getWidgetConfig(const std::string& id);
Parameter& getParameter(WidgetConfig& config, const std::string& id, const Parameter& defaultValue);
void initGlobalParameters();
private:
Map<std::string, WidgetConfig> mConfigurations;
WidgetConfig mGlobalConfig;
RGBA mErrorColor = { 0, 0, 0, 1 };
halnf mErrorNumber = 0;
std::string mActiveId;
};
}

View file

@ -0,0 +1,13 @@
#pragma once
#include "Animations.hpp"
#include "ButtonWidget.hpp"
#include "LabelWidget.hpp"
#include "ScrollableWidget.hpp"
#include "TextInputWidget.hpp"
#include "SliderWidget.hpp"
#include "CollapsableMenu.hpp"
#include "FloatingWidget.hpp"
#include "FloatingLayoutWidget.hpp"
#include "GridLayoutWidget.hpp"
#include "WorkspaceWidget.hpp"

View file

@ -0,0 +1,19 @@
#pragma once
#include "Widgets.hpp"
namespace tp {
class WorkspaceWidget : public Widget {
public:
WorkspaceWidget();
void eventProcess(const Events& events) override;
protected:
DockWidget mDockSpace;
FloatingLayoutWidget mFloatingLayer;
// Parameters
Vec2F mDefaultFloatSize = { 200, 200 };
};
}