Replace Old Widgets
This commit is contained in:
parent
cce8f0e1bc
commit
fcd5eb2d2a
81 changed files with 388 additions and 278 deletions
|
|
@ -5,17 +5,13 @@ 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_include_directories(${PROJECT_NAME} PUBLIC ./public/ ./public/layouts ./public/mangers ./public/widgets)
|
||||
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})
|
||||
add_executable(Widgets2Example examples/Example.cpp)
|
||||
target_link_libraries(Widgets2Example ${PROJECT_NAME} ${GLEW_LIB})
|
||||
target_include_directories(Widgets2Example PUBLIC ../Externals/glfw/include ${GLEW_INCLUDE_DIR})
|
||||
|
||||
file(COPY "examples/Font.ttf" DESTINATION "${CMAKE_BINARY_DIR}/${PROJECT_NAME}/")
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
|
||||
#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();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,256 +0,0 @@
|
|||
#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;
|
||||
};
|
||||
}
|
||||
213
Widgets/examples/Example.cpp
Normal file
213
Widgets/examples/Example.cpp
Normal file
|
|
@ -0,0 +1,213 @@
|
|||
|
||||
#include "WidgetApplication.hpp"
|
||||
|
||||
#include "RootWidget.hpp"
|
||||
#include "FloatingWidget.hpp"
|
||||
#include "DockWidget.hpp"
|
||||
|
||||
#include "ScrollableLayout.hpp"
|
||||
|
||||
using namespace tp;
|
||||
|
||||
|
||||
class Example : public WidgetApplication {
|
||||
public:
|
||||
Example() {
|
||||
exampleScrolling();
|
||||
}
|
||||
|
||||
void exampleAll() {
|
||||
static DockWidget dock;
|
||||
static LabelWidget centralWidget;
|
||||
static FloatingMenu menus[4];
|
||||
static FloatingMenu nestedMenus[4];
|
||||
static FloatingMenu buttons[10];
|
||||
|
||||
dock.setCenterWidget(¢ralWidget);
|
||||
|
||||
for (auto& menu : menus) {
|
||||
dock.addChild(&menu);
|
||||
}
|
||||
|
||||
for (auto& menu : nestedMenus) {
|
||||
menus[0].addToMenu(&menu);
|
||||
}
|
||||
|
||||
setRoot(&dock);
|
||||
}
|
||||
|
||||
void exampleBasic() {
|
||||
static Widget widget;
|
||||
static LabelWidget label;
|
||||
static SliderWidget slider;
|
||||
static ButtonWidget button;
|
||||
|
||||
widget.addChild(&label);
|
||||
widget.addChild(&button);
|
||||
widget.addChild(&slider);
|
||||
|
||||
setRoot(&widget);
|
||||
}
|
||||
|
||||
void exampleScrolling() {
|
||||
static Widget widget;
|
||||
static Widget content;
|
||||
static ButtonWidget buttons[10];
|
||||
static ScrollableBarWidget scrollBar;
|
||||
|
||||
setRoot(&widget);
|
||||
|
||||
widget.addChild(&scrollBar);
|
||||
widget.addChild(&content);
|
||||
|
||||
for (auto& button : buttons) {
|
||||
content.addChild(&button);
|
||||
}
|
||||
|
||||
widget.setLayout(new ScrollableLayout(&widget));
|
||||
|
||||
RootWidget::setWidgetArea(content, { 333, 333, 522, 522 });
|
||||
}
|
||||
|
||||
void examplePopup() {
|
||||
static Widget root;
|
||||
static ButtonWidget closeButton;
|
||||
static ButtonWidget openButton;
|
||||
|
||||
root.addChild(&openButton);
|
||||
((BasicLayout*)root.getLayout())->setLayoutPolicy(LayoutPolicy::Passive);
|
||||
|
||||
openButton.setAction([&](){
|
||||
closeButton.setArea({ 50, 50, 100, 100 });
|
||||
openButton.openPopup(&closeButton);
|
||||
});
|
||||
|
||||
closeButton.setAction([&](){
|
||||
closeButton.closePopup(&closeButton);
|
||||
});
|
||||
|
||||
openButton.setText("open");
|
||||
closeButton.setText("close");
|
||||
|
||||
RootWidget::setWidgetArea(openButton, { 333, 333, 222, 222 });
|
||||
|
||||
setRoot(&root);
|
||||
}
|
||||
|
||||
void exampleNestedMenus() {
|
||||
static DockWidget dock;
|
||||
static FloatingMenu menu1;
|
||||
static FloatingMenu menu2;
|
||||
static ButtonWidget buttons[15];
|
||||
|
||||
setRoot(&dock);
|
||||
|
||||
dock.addChild(&menu1);
|
||||
dock.addChild(&menu2);
|
||||
|
||||
dock.setCenterWidget(&buttons[5]);
|
||||
dock.dockWidget(&buttons[6], DockLayout::RIGHT);
|
||||
|
||||
menu1.addToMenu(&buttons[0]);
|
||||
|
||||
menu2.addToMenu(&buttons[2]);
|
||||
menu2.addToMenu(&buttons[3]);
|
||||
menu2.addToMenu(&buttons[4]);
|
||||
|
||||
menu2.addToMenu(&menu1);
|
||||
|
||||
// popup
|
||||
{
|
||||
buttons[0].setAction([&](){
|
||||
buttons[10].setArea({ 30, 30, 100, 100 });
|
||||
buttons[0].openPopup(&buttons[10]);
|
||||
});
|
||||
|
||||
buttons[0].setText("open popup");
|
||||
|
||||
buttons[10].setAction([&](){
|
||||
buttons[10].closePopup(&buttons[10]);
|
||||
});
|
||||
|
||||
buttons[10].setText("close popup");
|
||||
}
|
||||
|
||||
RootWidget::setWidgetArea(menu1, { 300, 100, 150, 500 });
|
||||
RootWidget::setWidgetArea(menu2, { 100, 100, 150, 300 });
|
||||
}
|
||||
|
||||
void exampleLayouts() {
|
||||
static Widget widgets[15];
|
||||
static ButtonWidget buttons[15];
|
||||
|
||||
setRoot(&widgets[0]);
|
||||
|
||||
widgets[0].addChild(&widgets[1]);
|
||||
|
||||
widgets[1].addChild(&buttons[1]);
|
||||
widgets[1].addChild(&buttons[2]);
|
||||
widgets[1].addChild(&widgets[2]);
|
||||
|
||||
RootWidget::setWidgetArea(buttons[1], { 300, 100, 350, 800 });
|
||||
RootWidget::setWidgetArea(buttons[2], { 100, 100, 150, 300 });
|
||||
|
||||
widgets[2].addChild(&buttons[3]);
|
||||
widgets[2].addChild(&buttons[4]);
|
||||
widgets[2].addChild(&buttons[5]);
|
||||
widgets[2].addChild(&buttons[6]);
|
||||
|
||||
buttons[1].setSizePolicy(SizePolicy::Expanding, SizePolicy::Expanding);
|
||||
buttons[2].setSizePolicy(SizePolicy::Expanding, SizePolicy::Expanding);
|
||||
buttons[3].setSizePolicy(SizePolicy::Expanding, SizePolicy::Expanding);
|
||||
buttons[4].setSizePolicy(SizePolicy::Expanding, SizePolicy::Expanding);
|
||||
buttons[5].setSizePolicy(SizePolicy::Expanding, SizePolicy::Expanding);
|
||||
buttons[6].setSizePolicy(SizePolicy::Expanding, SizePolicy::Expanding);
|
||||
|
||||
widgets[2].setSizePolicy(SizePolicy::Expanding, SizePolicy::Expanding);
|
||||
((BasicLayout*)widgets[1].getLayout())->setLayoutPolicy(LayoutPolicy::Horizontal);
|
||||
}
|
||||
|
||||
void exampleDock() {
|
||||
static DockWidget dock;
|
||||
static FloatingMenu menu1;
|
||||
static FloatingMenu menu2;
|
||||
static ButtonWidget buttons[15];
|
||||
|
||||
setRoot(&dock);
|
||||
|
||||
dock.addChild(&menu2);
|
||||
dock.addChild(&menu1);
|
||||
|
||||
dock.setCenterWidget(&buttons[5]);
|
||||
dock.dockWidget(&buttons[6], DockLayout::RIGHT);
|
||||
|
||||
menu1.addToMenu(&buttons[0]);
|
||||
menu2.addToMenu(&buttons[2]);
|
||||
//menu2.addToMenu(&menu1);
|
||||
menu2.addToMenu(&buttons[3]);
|
||||
menu2.addToMenu(&buttons[4]);
|
||||
|
||||
buttons[0].setAction([&]() { buttons[2].setColor(RGBA::random()); });
|
||||
buttons[2].setAction([&]() { buttons[0].setColor(RGBA::random()); });
|
||||
|
||||
buttons[5].setAction([&]() { dock.dockWidget(&menu1, DockLayout::LEFT); });
|
||||
buttons[6].setAction([&]() { dock.undockWidget(DockLayout::LEFT); });
|
||||
|
||||
buttons[3].setAction([&]() { dock.toggleWidgetVisibility(DockLayout::LEFT); });
|
||||
// buttons[4].setAction([this]() { mDockLayout.undockWidget(DockLayout::LEFT); });
|
||||
|
||||
buttons[3].setText("toggle");
|
||||
buttons[5].setText("dock");
|
||||
buttons[6].setText("undock");
|
||||
|
||||
RootWidget::setWidgetArea(menu2, { 300, 100, 150, 500 });
|
||||
RootWidget::setWidgetArea(menu1, { 100, 100, 150, 300 });
|
||||
}
|
||||
};
|
||||
|
||||
int main() {
|
||||
{
|
||||
Example gui;
|
||||
gui.run();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
|
||||
#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();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
#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;
|
||||
};
|
||||
}
|
||||
|
|
@ -1,103 +0,0 @@
|
|||
|
||||
#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)); }
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
|
||||
#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");
|
||||
}
|
||||
|
|
@ -1,96 +0,0 @@
|
|||
|
||||
#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");
|
||||
}
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
#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; }
|
||||
|
|
@ -1,89 +0,0 @@
|
|||
|
||||
#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;
|
||||
}
|
||||
|
|
@ -1,328 +0,0 @@
|
|||
#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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
#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");
|
||||
}
|
||||
96
Widgets/private/Layout.cpp
Normal file
96
Widgets/private/Layout.cpp
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
#include <algorithm>
|
||||
#include "Layout.hpp"
|
||||
|
||||
#include "Widget.hpp"
|
||||
|
||||
using namespace tp;
|
||||
|
||||
const RectF& WidgetLayout::getArea() const {
|
||||
return mWidget->mAreaCache;
|
||||
}
|
||||
|
||||
RectF WidgetLayout::getAnimatedArea() const {
|
||||
return mWidget->getArea();
|
||||
}
|
||||
|
||||
void WidgetLayout::setArea(const RectF& area) {
|
||||
mWidget->setAreaCache(area);
|
||||
}
|
||||
|
||||
Widget* WidgetLayout::parent() const { return mWidget->mParent; }
|
||||
|
||||
const std::vector<Widget*>& WidgetLayout::children() const { return mWidget->mChildren; }
|
||||
|
||||
const Vec2F& WidgetLayout::getMinSize() { return mMinSize; }
|
||||
|
||||
void WidgetLayout::setMinSize(const Vec2F& size) { mMinSize = size; }
|
||||
|
||||
const Vec2<SizePolicy>& WidgetLayout::getSizePolicy() const { return mSizePolicy; }
|
||||
|
||||
void WidgetLayout::setSizePolicy(SizePolicy x, SizePolicy y) { mSizePolicy = { x, y }; }
|
||||
|
||||
RangeF WidgetLayout::pickRange(const RangeF& current, const RangeF& children, const RangeF& parent, bool vertical) const {
|
||||
RangeF out;
|
||||
|
||||
switch (mSizePolicy[vertical]) {
|
||||
case SizePolicy::Fixed: out = current; break;
|
||||
case SizePolicy::Expanding: out = parent; break;
|
||||
case SizePolicy::Minimal: out = children; break;
|
||||
}
|
||||
|
||||
out = clampRange(out, children, parent, vertical);
|
||||
return out;
|
||||
}
|
||||
|
||||
void WidgetLayout::clampMinMaxSize() {
|
||||
auto current = getArea();
|
||||
current.size.clamp(mMinSize, mMaxSize);
|
||||
setArea(current);
|
||||
}
|
||||
|
||||
RangeF WidgetLayout::clampRange(const RangeF& current, const RangeF& child, const RangeF& parent, bool vertical) const {
|
||||
auto out = current;
|
||||
|
||||
if (!children().empty()) {
|
||||
auto clampedChild = child;
|
||||
clampedChild.clamp(parent);
|
||||
out.clamp(clampedChild, parent);
|
||||
} else {
|
||||
out.clamp(parent);
|
||||
}
|
||||
|
||||
// clamp min max sizes
|
||||
auto len = clamp(current.size(), mMinSize[vertical], mMaxSize[vertical]);
|
||||
if (len != current.size()) {
|
||||
out.resizeFromCenter(len);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
RectF WidgetLayout::getChildrenEnclosure() const {
|
||||
RectF out;
|
||||
|
||||
if (children().empty()) {
|
||||
out = { getArea().center(), { 0, 0 } };
|
||||
} else {
|
||||
out = children().front()->getLayout()->getArea();
|
||||
for (auto child : children()) {
|
||||
out.expand(child->getLayout()->getArea());
|
||||
}
|
||||
out.pos += getArea().pos;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
RectF WidgetLayout::getAvailableChildArea() const {
|
||||
return getArea().relative();
|
||||
}
|
||||
|
||||
RectF WidgetLayout::getParentEnclosure() const {
|
||||
DEBUG_ASSERT(parent())
|
||||
if (!parent()) return { { 0, 0 }, mMaxSize };
|
||||
auto out = parent()->getLayout()->getAvailableChildArea();
|
||||
return out;
|
||||
}
|
||||
155
Widgets/private/RootWidget.cpp
Normal file
155
Widgets/private/RootWidget.cpp
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
|
||||
#include "RootWidget.hpp"
|
||||
|
||||
#include "BasicLayout.hpp"
|
||||
#include "OverlayLayout.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
using namespace tp;
|
||||
|
||||
WidgetLayout* WidgetManagerInterface::defaultLayout(Widget* widget) {
|
||||
return new BasicLayout(widget);
|
||||
}
|
||||
|
||||
RootWidget::RootWidget() {
|
||||
setDebug("root", RGBA(1));
|
||||
|
||||
addChild(&mRoot);
|
||||
|
||||
mRoot.addChild(&mPopups);
|
||||
mRoot.setLayout(new OverlayLayout(&mRoot));
|
||||
|
||||
dynamic_cast<BasicLayout*>(mPopups.getLayout())->setLayoutPolicy(LayoutPolicy::Passive);
|
||||
}
|
||||
|
||||
void RootWidget::setRootWidget(Widget* widget) {
|
||||
mRoot.removeChild(mUserRoot);
|
||||
mRoot.addChild(widget);
|
||||
|
||||
widget->bringToBack();
|
||||
|
||||
mUserRoot = widget;
|
||||
}
|
||||
|
||||
void RootWidget::processFrame(EventHandler* events, const RectF& screenArea) {
|
||||
mRoot.setArea(screenArea);
|
||||
|
||||
if (!gDebugWidget.update(this, *events)) return;
|
||||
|
||||
// construct hierarchy tree of widgets to process
|
||||
mUpdateManager.updateTreeToProcess(&mRoot);
|
||||
|
||||
updateAnimations();
|
||||
updateAreaCache(&mRoot, true);
|
||||
|
||||
// update all events and call all event processing callbacks
|
||||
mUpdateManager.processWidgets(&mRoot, *events);
|
||||
|
||||
updateAreaCache(&mRoot, true);
|
||||
|
||||
// update widget sizes base on individual size policies
|
||||
mLayoutManager.adjust(&mRoot);
|
||||
|
||||
updateAreaCache(&mRoot, false);
|
||||
|
||||
// trigger some widgets by moise pointer
|
||||
mUpdateManager.handleFocusChanges(&mRoot, *events);
|
||||
|
||||
// check triggered widgets for removal
|
||||
mUpdateManager.clean();
|
||||
}
|
||||
|
||||
bool RootWidget::needsUpdate() const {
|
||||
if (gDebugWidget.isRedrawAlways()) return true;
|
||||
return mUpdateManager.isPendingUpdates();
|
||||
}
|
||||
|
||||
void RootWidget::drawFrame(Canvas& canvas) {
|
||||
canvas.rect(mRoot.getAreaT(), RGBA(0, 0, 0, 1));
|
||||
|
||||
drawRecursion(canvas, &mRoot, { 0, 0 });
|
||||
|
||||
gDebugWidget.drawDebug(this, canvas);
|
||||
}
|
||||
|
||||
|
||||
void RootWidget::drawRecursion(Canvas& canvas, Widget* active, const Vec2F& pos) {
|
||||
if (!active->mFlags.get(ENABLED)) return;
|
||||
|
||||
canvas.setOrigin(pos);
|
||||
canvas.pushClamp({ pos, active->getArea().size });
|
||||
|
||||
if (canvas.getClampedArea().size.length2() > EPSILON) {
|
||||
active->draw(canvas);
|
||||
|
||||
for (auto child = active->mDepthOrder.lastNode(); child; child = child->prev) {
|
||||
drawRecursion(canvas, child->data, pos + child->data->getArea().pos);
|
||||
}
|
||||
}
|
||||
|
||||
canvas.setOrigin(pos);
|
||||
canvas.popClamp();
|
||||
|
||||
active->drawOverlay(canvas);
|
||||
}
|
||||
|
||||
void RootWidget::setWidgetArea(Widget& widget, const RectF& rect) {
|
||||
widget.mArea.setTargetRect(rect);
|
||||
widget.mArea.endAnimation();
|
||||
}
|
||||
|
||||
void RootWidget::updateAnimations() {
|
||||
dfs(&mRoot, [](Widget* widget) {
|
||||
widget->updateAnimations();
|
||||
});
|
||||
}
|
||||
|
||||
void RootWidget::updateAreaCache(Widget* iter, bool read) {
|
||||
if (!iter || !iter->isUpdate()) return;
|
||||
|
||||
if (read) {
|
||||
iter->mAreaCache = iter->getAreaT();
|
||||
} else {
|
||||
iter->setArea(iter->mAreaCache);
|
||||
iter->mArea.updateCurrentRect();
|
||||
}
|
||||
|
||||
for (auto child : iter->mChildren) {
|
||||
if (read) {
|
||||
child->mAreaCache = child->getAreaT();
|
||||
} else {
|
||||
child->setArea(child->mAreaCache);
|
||||
child->mArea.updateCurrentRect();
|
||||
}
|
||||
}
|
||||
|
||||
for (auto child : iter->mDepthOrder) {
|
||||
updateAreaCache(child.data(), read);
|
||||
}
|
||||
}
|
||||
|
||||
void RootWidget::updateWidget(Widget* widget, const char* reason) {
|
||||
DEBUG_ASSERT(reason)
|
||||
mUpdateManager.scheduleUpdate(widget, reason);
|
||||
}
|
||||
|
||||
void RootWidget::openPopup(Widget* widget) {
|
||||
auto relativeArea = widget->getAreaT();
|
||||
for (auto iter = widget->mParent; iter && iter->mParent; iter = iter->mParent) {
|
||||
relativeArea.pos += iter->getAreaT().pos;
|
||||
}
|
||||
setWidgetArea(*widget, relativeArea);
|
||||
|
||||
mPopups.addChild(widget);
|
||||
mUpdateManager.lockFocus(widget);
|
||||
}
|
||||
|
||||
void RootWidget::closePopup(Widget* widget) {
|
||||
mPopups.removeChild(widget);
|
||||
mUpdateManager.freeFocus(widget);
|
||||
}
|
||||
|
||||
void RootWidget::lockFocus(Widget* widget) { mUpdateManager.lockFocus(widget); }
|
||||
void RootWidget::freeFocus(Widget* widget) { mUpdateManager.freeFocus(widget); }
|
||||
bool RootWidget::isDebug() const { return gDebugWidget.isDebug(); }
|
||||
|
|
@ -1,179 +0,0 @@
|
|||
#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;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
#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);
|
||||
}
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
#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");
|
||||
}
|
||||
152
Widgets/private/Widget.cpp
Normal file
152
Widgets/private/Widget.cpp
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
#include "Widget.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
using namespace tp;
|
||||
|
||||
Widget::Widget() {
|
||||
mArea.setTargetRect({ 100, 100, 10, 10 });
|
||||
setLayout(WidgetManagerInterface::defaultLayout(this));
|
||||
mArea.endAnimation();
|
||||
|
||||
mFlags.set(ENABLED, true);
|
||||
}
|
||||
|
||||
Widget::~Widget() {
|
||||
delete mLayout;
|
||||
}
|
||||
|
||||
void Widget::addChild(Widget* child, bool front) {
|
||||
if (auto node = mDepthOrder.find(child)) {
|
||||
node->data->bringToFront();
|
||||
return;
|
||||
}
|
||||
|
||||
if (child->mParent) {
|
||||
child->mParent->removeChild(child);
|
||||
}
|
||||
|
||||
mChildren.push_back(child);
|
||||
|
||||
if (front) {
|
||||
mDepthOrder.pushFront(child);
|
||||
} else {
|
||||
mDepthOrder.pushBack(child);
|
||||
}
|
||||
|
||||
child->mParent = this;
|
||||
|
||||
triggerWidgetUpdate("add child");
|
||||
child->triggerWidgetUpdate("new parent");
|
||||
}
|
||||
|
||||
void Widget::removeChild(Widget* child) {
|
||||
auto node = mDepthOrder.find(child);
|
||||
if (!node) return;
|
||||
|
||||
mDepthOrder.removeNode(node);
|
||||
mChildren.erase(std::remove(mChildren.begin(), mChildren.end(), child), mChildren.end());
|
||||
|
||||
triggerWidgetUpdate("removed child");
|
||||
child->triggerWidgetUpdate("parent changed");
|
||||
}
|
||||
|
||||
void Widget::clear() {
|
||||
mChildren.clear();
|
||||
mDepthOrder.removeAll();
|
||||
}
|
||||
|
||||
void Widget::endAnimations() { mArea.endAnimation(); }
|
||||
bool Widget::processesEvents() const { return false; }
|
||||
void Widget::updateAnimations() { mArea.updateCurrentRect(); }
|
||||
bool Widget::needsNextFrame() const { return !mArea.shouldEndTransition(); }
|
||||
|
||||
void Widget::bringToFront() {
|
||||
if (!mParent) return;
|
||||
auto& order = mParent->mDepthOrder;
|
||||
auto node = order.find(this);
|
||||
DEBUG_ASSERT(node)
|
||||
order.detach(node);
|
||||
order.pushFront(node);
|
||||
}
|
||||
|
||||
void Widget::bringToBack() {
|
||||
if (!mParent) return;
|
||||
auto& order = mParent->mDepthOrder;
|
||||
auto node = order.find(this);
|
||||
DEBUG_ASSERT(node)
|
||||
order.detach(node);
|
||||
order.pushBack(node);
|
||||
}
|
||||
|
||||
void Widget::mouseEnter() { mFlags.set(IN_FOCUS, true); }
|
||||
void Widget::mouseLeave() { mFlags.set(IN_FOCUS, false); }
|
||||
|
||||
bool Widget::propagateEventsToChildren() const { return true; }
|
||||
|
||||
void Widget::setLayout(tp::WidgetLayout* layout) {
|
||||
delete mLayout;
|
||||
mLayout = layout;
|
||||
triggerWidgetUpdate("chane layout");
|
||||
}
|
||||
|
||||
WidgetLayout* Widget::getLayout() { return mLayout; }
|
||||
|
||||
const WidgetLayout* Widget::getLayout() const { return mLayout; }
|
||||
|
||||
WidgetManagerInterface* Widget::getRoot() {
|
||||
Widget* iter = mParent;
|
||||
while (iter && iter->mParent) {
|
||||
iter = iter->mParent;
|
||||
}
|
||||
return dynamic_cast<WidgetManagerInterface*>(iter);
|
||||
}
|
||||
|
||||
void Widget::setAreaCache(const tp::RectF& area) { mAreaCache = area; }
|
||||
|
||||
void Widget::setArea(const RectF& area) {
|
||||
if (mArea.getTargetRect() == area) return;
|
||||
|
||||
mArea.setTargetRect(area);
|
||||
triggerWidgetUpdate("new area");
|
||||
}
|
||||
|
||||
RectF Widget::getArea() const { return mArea.getCurrentRect(); }
|
||||
|
||||
RectF Widget::getAreaT() const { return mArea.getTargetRect(); }
|
||||
|
||||
RectF Widget::getRelativeArea() const { return { {}, mArea.getCurrentRect().size }; }
|
||||
|
||||
RectF Widget::getRelativeAreaT() const { return { {}, mArea.getTargetRect().size }; }
|
||||
|
||||
void Widget::setDebug(const char* name, RGBA col) {
|
||||
mDebug.id = name;
|
||||
mDebug.col = col;
|
||||
}
|
||||
|
||||
void Widget::setSizePolicy(SizePolicy x, SizePolicy y) {
|
||||
getLayout()->setSizePolicy(x, y);
|
||||
}
|
||||
|
||||
void Widget::triggerWidgetUpdate(const char* reason) {
|
||||
if (auto root = getRoot()) {
|
||||
root->updateWidget(this, reason);
|
||||
}
|
||||
}
|
||||
|
||||
void Widget::openPopup(Widget* widget) {
|
||||
addChild(widget);
|
||||
getRoot()->openPopup(widget);
|
||||
}
|
||||
|
||||
void Widget::closePopup(Widget* widget) {
|
||||
getRoot()->closePopup(widget);
|
||||
}
|
||||
|
||||
void Widget::lockFocus() {
|
||||
getRoot()->lockFocus(this);
|
||||
}
|
||||
|
||||
void Widget::freeFocus() {
|
||||
getRoot()->freeFocus(this);
|
||||
}
|
||||
10
Widgets/private/WidgetApplication.cpp
Normal file
10
Widgets/private/WidgetApplication.cpp
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
#include "WidgetApplication.hpp"
|
||||
|
||||
#include "imgui.h"
|
||||
|
||||
using namespace tp;
|
||||
|
||||
void WidgetApplication::debugUI() {
|
||||
ImGui::Text("Proc ms : %i", (int) mProcTime);
|
||||
ImGui::SameLine(); ImGui::Text("Draw ms : %i", (int) mDrawTime);
|
||||
}
|
||||
|
|
@ -1,140 +0,0 @@
|
|||
#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;
|
||||
}
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
#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 }));
|
||||
}
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
#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;
|
||||
}
|
||||
}
|
||||
149
Widgets/private/layouts/BasicLayout.cpp
Normal file
149
Widgets/private/layouts/BasicLayout.cpp
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
|
||||
#include "BasicLayout.hpp"
|
||||
#include "Widget.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
using namespace tp;
|
||||
|
||||
|
||||
void BasicLayout::pickRect(bool vertical) {
|
||||
auto current = getArea();
|
||||
auto children = getChildrenEnclosure();
|
||||
auto parent = getParentEnclosure();
|
||||
|
||||
children.shrinkFromCenter(-mLayoutMargin, true);
|
||||
|
||||
RectF newArea;
|
||||
if (vertical) {
|
||||
auto rangeY = pickRange(current.getRangeY(), children.getRangeY(), parent.getRangeY(), true);
|
||||
newArea = RectF(current.getRangeX(), rangeY);
|
||||
} else {
|
||||
auto rangeX = pickRange(current.getRangeX(), children.getRangeX(), parent.getRangeX(), false);
|
||||
newArea = RectF(rangeX, current.getRangeY());
|
||||
}
|
||||
|
||||
setArea(newArea);
|
||||
}
|
||||
|
||||
void BasicLayout::clampRect() {
|
||||
DEBUG_ASSERT(0)
|
||||
|
||||
auto current = getArea();
|
||||
auto children = getChildrenEnclosure();
|
||||
auto parent = getParentEnclosure();
|
||||
|
||||
auto rangeX = clampRange(current.getRangeX(), children.getRangeX(), parent.getRangeX(), false);
|
||||
auto rangeY = clampRange(current.getRangeY(), children.getRangeY(), parent.getRangeY(), true);
|
||||
|
||||
setArea(RectF(rangeX, rangeY));
|
||||
}
|
||||
|
||||
void BasicLayout::updateLayout(bool vertical) {
|
||||
if (children().empty()) return;
|
||||
|
||||
if (vertical) {
|
||||
switch (mLayoutPolicy) {
|
||||
case LayoutPolicy::Vertical: adjustLayout(true); break;
|
||||
case LayoutPolicy::Passive:
|
||||
case LayoutPolicy::Horizontal: break;
|
||||
}
|
||||
} else {
|
||||
switch (mLayoutPolicy) {
|
||||
case LayoutPolicy::Horizontal: adjustLayout(false); break;
|
||||
case LayoutPolicy::Passive:
|
||||
case LayoutPolicy::Vertical: break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
halnf BasicLayout::changeChildSize(tp::Widget* widget, halnf diff, bool vertical) {
|
||||
auto prevSize = widget->getLayout()->getArea().size[vertical];
|
||||
{
|
||||
auto area = widget->getLayout()->getArea();
|
||||
area.size[vertical] += diff;
|
||||
widget->setAreaCache(area);
|
||||
|
||||
widget->getLayout()->clampMinMaxSize();
|
||||
}
|
||||
auto newSize = widget->getLayout()->getArea().size[vertical];
|
||||
return newSize - prevSize;
|
||||
}
|
||||
|
||||
void BasicLayout::adjustLayout(bool vertical) {
|
||||
std::vector<std::pair<Widget*, bool>> contributors;
|
||||
Vec2F contentSize = { 0, 0 };
|
||||
Vec2F availableSize = getArea().size;
|
||||
|
||||
for (auto child : children()) {
|
||||
child->getLayout()->pickRect(vertical);
|
||||
|
||||
if (child->getLayout()->getSizePolicy()[vertical] == SizePolicy::Expanding) {
|
||||
contributors.emplace_back( child, true );
|
||||
|
||||
auto area = child->getLayout()->getArea();
|
||||
area.size[vertical] = 0;
|
||||
child->setAreaCache(area);
|
||||
child->getLayout()->clampMinMaxSize();
|
||||
}
|
||||
|
||||
contentSize += child->getLayout()->getArea().size;
|
||||
}
|
||||
|
||||
availableSize -= mLayoutGap * ((halnf) children().size() - 1) + mLayoutMargin * 2;
|
||||
|
||||
auto diff = availableSize - contentSize;
|
||||
|
||||
// expand or contract as much as possible
|
||||
while (!contributors.empty() && (diff[vertical] != 0)) {
|
||||
auto quota = diff / (halnf) contributors.size();
|
||||
|
||||
for (auto& contributor : contributors) {
|
||||
if (!contributor.second) continue;
|
||||
|
||||
// contributor.first->endAnimations();
|
||||
auto contribution = changeChildSize(contributor.first, quota[vertical], vertical);
|
||||
|
||||
if (contribution == 0) {
|
||||
contributor.second = false;
|
||||
}
|
||||
|
||||
diff[vertical] -= contribution;
|
||||
}
|
||||
|
||||
contributors.erase(
|
||||
std::remove_if(contributors.begin(), contributors.end(), [](auto node) { return !node.second; }),
|
||||
contributors.end()
|
||||
);
|
||||
}
|
||||
|
||||
// set opposite direction size
|
||||
for (auto child : children()) {
|
||||
// if (child->getLayout()->getSizePolicy()[!vertical] != SizePolicy::Minimal) {
|
||||
auto area = child->getLayout()->getArea();
|
||||
area.size[!vertical] = getArea().size[!vertical] - mLayoutMargin * 2;
|
||||
child->setAreaCache(area);
|
||||
// }
|
||||
}
|
||||
|
||||
// set pos
|
||||
halnf iterPos = mLayoutMargin;
|
||||
for (auto child : children()) {
|
||||
auto area = child->getLayout()->getArea();
|
||||
|
||||
area.pos[vertical] = iterPos;
|
||||
area.pos[!vertical] = mLayoutMargin;
|
||||
|
||||
iterPos += area.size[vertical] + mLayoutGap;
|
||||
child->setAreaCache(area);
|
||||
|
||||
// child->updateAnimations();
|
||||
// child->triggerWidgetUpdate("layout changed");
|
||||
}
|
||||
}
|
||||
|
||||
RectF BasicLayout::getAvailableChildArea() const {
|
||||
auto out = WidgetLayout::getAvailableChildArea();
|
||||
return out.shrinkFromCenter(mLayoutMargin, true);
|
||||
}
|
||||
290
Widgets/private/layouts/DockLayout.cpp
Normal file
290
Widgets/private/layouts/DockLayout.cpp
Normal file
|
|
@ -0,0 +1,290 @@
|
|||
#include "DockLayout.hpp"
|
||||
|
||||
using namespace tp;
|
||||
|
||||
DockLayout::DockLayout(Widget* widget) :
|
||||
WidgetLayout(widget) {
|
||||
mSideWidgets[0].side = LEFT;
|
||||
mSideWidgets[1].side = TOP;
|
||||
mSideWidgets[2].side = RIGHT;
|
||||
mSideWidgets[3].side = BOTTOM;
|
||||
}
|
||||
|
||||
bool DockLayout::setCenterWidget(Widget* widget) {
|
||||
if (mCenterWidget) return false;
|
||||
mCenterWidget = widget;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DockLayout::dockWidget(Widget* widget, Side side) {
|
||||
if (sideExists(side) || side == Side::NONE) return false;
|
||||
|
||||
auto& sideWidget = mSideWidgets[side];
|
||||
sideWidget.widget = widget;
|
||||
|
||||
for (auto& order : mSideWidgets) {
|
||||
if (order.order == -1) {
|
||||
order.order = side;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sideWidget.hidden = false;
|
||||
sideWidget.areaBeforeDocking = widget->getArea();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DockLayout::undockWidget(Side side) {
|
||||
if (!sideExists(side) || (side == Side::NONE)) return false;
|
||||
|
||||
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;
|
||||
mSideWidgets[side].widget = nullptr;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DockLayout::toggleWidgetVisibility(Side side) {
|
||||
DEBUG_ASSERT(sideExists(side))
|
||||
mSideWidgets[side].hidden = !mSideWidgets[side].hidden;
|
||||
}
|
||||
|
||||
void DockLayout::calculateSideAreas() {
|
||||
auto startArea = getArea().relative();
|
||||
|
||||
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 DockLayout::calculateResizeHandles() {
|
||||
RectF rec;
|
||||
RectF area = getArea().relative();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
if (isSideVisible(LEFT)) {
|
||||
auto& side = mSideWidgets[LEFT];
|
||||
rec = { side.area.p4(), { mPadding * 2, side.area.size.y } };
|
||||
side.resizeHandle.area = rec;
|
||||
side.resizeHandle.start = 0;
|
||||
side.resizeHandle.end = 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.area = rec;
|
||||
side.resizeHandle.start = 0;
|
||||
side.resizeHandle.end = (area.p3() - mCenterArea.p1()).x;
|
||||
}
|
||||
if (isSideVisible(TOP)) {
|
||||
auto& side = mSideWidgets[TOP];
|
||||
rec = { side.area.p2(), { side.area.size.x, mPadding * 2 } };
|
||||
side.resizeHandle.area = rec;
|
||||
side.resizeHandle.start = 0;
|
||||
side.resizeHandle.end = 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.area = rec;
|
||||
side.resizeHandle.start = 0;
|
||||
side.resizeHandle.end = (area.p3() - mCenterArea.p1()).y;
|
||||
}
|
||||
}
|
||||
|
||||
void DockLayout::updateChildSideWidgets() {
|
||||
for (ualni i = 0; i < 4; i++) {
|
||||
if (!sideExists(Side(i))) continue;
|
||||
auto widget = mSideWidgets[i].widget;
|
||||
|
||||
if (!isSideVisible(Side(i))) {
|
||||
// FIXME : widget->mEnable = false;
|
||||
} else {
|
||||
widget->setAreaCache(mSideWidgets[i].area);
|
||||
// FIXME : widget->mEnable = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (mCenterWidget) mCenterWidget->setAreaCache(mCenterArea);
|
||||
}
|
||||
|
||||
void DockLayout::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;
|
||||
}
|
||||
}
|
||||
|
||||
ualni DockLayout::getVisibleSidesSize() const {
|
||||
ualni out = 0;
|
||||
for (ualni i = 0; i < 4; i++) {
|
||||
if (isSideVisible(Side(i))) out++;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
auto DockLayout::getSideFromWidget(Widget* widget) -> Side {
|
||||
for (auto& sideWidget : mSideWidgets) {
|
||||
if (sideWidget.widget == widget) return sideWidget.side;
|
||||
}
|
||||
return DockLayout::NONE;
|
||||
}
|
||||
|
||||
void DockLayout::updateLayout(bool vertical) {
|
||||
for (auto child : children()) {
|
||||
child->getLayout()->pickRect(vertical);
|
||||
}
|
||||
|
||||
if (vertical) return;
|
||||
adjustChildrenRect();
|
||||
}
|
||||
|
||||
void DockLayout::adjustChildrenRect() {
|
||||
calculateSideAreas();
|
||||
calculateResizeHandles();
|
||||
updateChildSideWidgets();
|
||||
}
|
||||
|
||||
void DockLayout::updatePreviewSide(const Vec2F& pointer) {
|
||||
const auto handleSize = min(mCenterArea.size.x, mCenterArea.size.y) * mHandleFactor;
|
||||
|
||||
for (auto& sideWidget : mSideWidgets) {
|
||||
if (sideWidget.widget) continue;
|
||||
|
||||
switch (sideWidget.side) {
|
||||
case TOP: sideWidget.area = mCenterArea.splitByFactorVT(mHandleSplitFactor); break;
|
||||
case BOTTOM: sideWidget.area = mCenterArea.splitByFactorVB(1 - mHandleSplitFactor); break;
|
||||
case LEFT: sideWidget.area = mCenterArea.splitByFactorHL(mHandleSplitFactor); break;
|
||||
case RIGHT: sideWidget.area = mCenterArea.splitByFactorHR(1 - mHandleSplitFactor); 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(pointer)) {
|
||||
mPreviewArea = sideWidget.area;
|
||||
mPreviewSide = sideWidget.side;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DockLayout::Side DockLayout::getPreviewSide() {
|
||||
return mPreviewSide;
|
||||
}
|
||||
|
||||
void DockLayout::updateResizeHover(const Vec2F& pointer) {
|
||||
for (auto& sideWidget : mSideWidgets) {
|
||||
sideWidget.resizeHandle.hover = sideWidget.resizeHandle.area.isInside(pointer);
|
||||
}
|
||||
}
|
||||
|
||||
void DockLayout::updateResize(const Vec2F& pointerDelta) {
|
||||
if (!mResizing) return;
|
||||
|
||||
// do the resizing
|
||||
for (auto& sideWidget : mSideWidgets) {
|
||||
if (!sideWidget.resizeHandle.active) continue;
|
||||
halnf delta = pointerDelta[(sideWidget.side == TOP || sideWidget.side == BOTTOM)];
|
||||
if (sideWidget.side == BOTTOM || sideWidget.side == RIGHT) {
|
||||
delta *= -1;
|
||||
}
|
||||
sideWidget.absoluteSize += delta;
|
||||
}
|
||||
}
|
||||
|
||||
bool DockLayout::startResize(const Vec2F& pointer) {
|
||||
for (auto& sideWidget : mSideWidgets) {
|
||||
if (sideWidget.resizeHandle.hover) {
|
||||
sideWidget.resizeHandle.active = true;
|
||||
mResizing = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void DockLayout::endResize() {
|
||||
for (auto& sideWidget : mSideWidgets) {
|
||||
sideWidget.resizeHandle.active = false;
|
||||
}
|
||||
mResizing = false;
|
||||
}
|
||||
|
||||
bool DockLayout::isSideVisible(DockLayout::Side side) const {
|
||||
return sideExists(side) && !mSideWidgets[side].hidden;
|
||||
}
|
||||
|
||||
bool DockLayout::sideExists(DockLayout::Side side) const {
|
||||
return mSideWidgets[side].widget;
|
||||
}
|
||||
|
||||
const std::vector<Widget*>& DockLayout::getDockedWidgets() {
|
||||
mDockedWidgets.resize(5);
|
||||
for (auto i = 0; i < 4; i++) {
|
||||
mDockedWidgets[i] = mSideWidgets[i].widget;
|
||||
}
|
||||
mDockedWidgets[4] = mCenterWidget;
|
||||
return mDockedWidgets;
|
||||
}
|
||||
52
Widgets/private/layouts/FloatingLayout.cpp
Normal file
52
Widgets/private/layouts/FloatingLayout.cpp
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
#include "FloatingLayout.hpp"
|
||||
#include "Widget.hpp"
|
||||
|
||||
using namespace tp;
|
||||
|
||||
void FloatingLayout::pickRect(bool vertical) {
|
||||
if (mIsFloating) {
|
||||
auto area = getArea();
|
||||
|
||||
if (mIsResizing) {
|
||||
mPointerCurrent.clamp(mMinSize, mMaxSize);
|
||||
|
||||
area.size[vertical] = (mPointerCurrent + mHandleSize / 2.f)[vertical];
|
||||
|
||||
for (auto child : children()) {
|
||||
child->triggerWidgetUpdate("floating menu resized");
|
||||
}
|
||||
|
||||
} else if (mIsFloating) {
|
||||
area.pos[vertical] += (mPointerCurrent - mPointerStart)[vertical];
|
||||
}
|
||||
|
||||
setArea(area);
|
||||
}
|
||||
|
||||
clampMinMaxSize();
|
||||
}
|
||||
|
||||
RectF FloatingLayout::resizeHandleRect() {
|
||||
auto area = getAnimatedArea().relative();
|
||||
area.pos = area.p3() - mHandleSize;
|
||||
area.size = mHandleSize;
|
||||
area.shrink(mHandlePadding);
|
||||
return area;
|
||||
}
|
||||
|
||||
void FloatingLayout::startAction(const Vec2F& pointer) {
|
||||
mPointerStart = pointer;
|
||||
|
||||
mIsFloating = true;
|
||||
if (resizeHandleRect().isInside(mPointerStart)) {
|
||||
mIsResizing = true;
|
||||
}
|
||||
}
|
||||
|
||||
void FloatingLayout::updateAction(const Vec2F& pointer) {
|
||||
mPointerCurrent = pointer;
|
||||
}
|
||||
|
||||
void FloatingLayout::endAction() {
|
||||
mIsResizing = mIsFloating = false;
|
||||
}
|
||||
29
Widgets/private/layouts/OverlayLayout.cpp
Normal file
29
Widgets/private/layouts/OverlayLayout.cpp
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
|
||||
#include "OverlayLayout.hpp"
|
||||
#include "Widget.hpp"
|
||||
|
||||
|
||||
using namespace tp;
|
||||
|
||||
void OverlayLayout::updateLayout(bool vertical) {
|
||||
if (vertical) return;
|
||||
|
||||
if (children().empty()) return;
|
||||
|
||||
for (auto child : children()) {
|
||||
child->getLayout()->setArea(getArea().relative());
|
||||
}
|
||||
|
||||
/*
|
||||
if (children().empty()) return;
|
||||
|
||||
auto first = children().front();
|
||||
|
||||
first->getLayout()->setArea(getArea().relative());
|
||||
|
||||
for (auto child : children()) {
|
||||
if (child == first) continue;
|
||||
child->getLayout()->setArea(getArea().relative());
|
||||
}
|
||||
*/
|
||||
}
|
||||
53
Widgets/private/layouts/ScrollableLayout.cpp
Normal file
53
Widgets/private/layouts/ScrollableLayout.cpp
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
#include "ScrollableLayout.hpp"
|
||||
|
||||
// fixme : this dependency should be removed
|
||||
#include "ScrollableWidget.hpp"
|
||||
|
||||
using namespace tp;
|
||||
|
||||
void ScrollableLayout::updateLayout(bool vertical) {
|
||||
if (vertical) return;
|
||||
|
||||
// TODO : make better interface to get scroller and content widget
|
||||
if (children().size() != 2) return;
|
||||
|
||||
auto scroller = dynamic_cast<ScrollableBarWidget*>(children().front());
|
||||
auto content = children().back();
|
||||
|
||||
if (!scroller || !content) return;
|
||||
|
||||
updateWidgetRects(getArea().relative(), content, scroller);
|
||||
}
|
||||
|
||||
void ScrollableLayout::updateWidgetRects(const RectF& area, Widget* content, ScrollableBarWidget* scroller) const {
|
||||
bool dir = scroller->getDirection();
|
||||
|
||||
// update content
|
||||
// auto contentItems = content->getLayout()->getChildrenEnclosure();
|
||||
// content->getLayout()->setArea(contentItems.shrinkFromCenter(-10, true));
|
||||
content->getLayout()->pickRect(dir);
|
||||
|
||||
auto sizeFactor = (area.size[dir] / content->getLayout()->getArea().size[dir]);
|
||||
auto splitFactor = 1 - ((sizeFactor > 1.f) ? 0 : mScrollerSize / area.size[!dir]);
|
||||
|
||||
auto holderArea = area.splitByFactorHL(splitFactor);
|
||||
auto contentArea = content->getLayout()->getArea();
|
||||
auto scrollerArea = area.splitByFactorHR(splitFactor);
|
||||
|
||||
scroller->updateSizeFactor(sizeFactor);
|
||||
|
||||
contentArea.pos[dir] = -scroller->getPosFactor() * contentArea.size[dir];
|
||||
contentArea.size[!dir] = holderArea.size[!dir];
|
||||
contentArea.pos[!dir] = 0;
|
||||
|
||||
scroller->getLayout()->setArea(scrollerArea);
|
||||
content->getLayout()->setArea(contentArea);
|
||||
}
|
||||
|
||||
RectF ScrollableLayout::getAvailableChildArea() const {
|
||||
auto out = getArea();
|
||||
// dont constrain on scroll axis
|
||||
out.pos = -FLT_MAX / 4;
|
||||
out.size = FLT_MAX / 2;
|
||||
return out;
|
||||
}
|
||||
177
Widgets/private/managers/DebugManager.cpp
Normal file
177
Widgets/private/managers/DebugManager.cpp
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
#include "DebugManager.hpp"
|
||||
#include "RootWidget.hpp"
|
||||
#include "BasicLayout.hpp"
|
||||
|
||||
#include "imgui.h"
|
||||
#include <sstream>
|
||||
|
||||
using namespace tp;
|
||||
|
||||
DebugManager tp::gDebugWidget;
|
||||
|
||||
#define LIST_SIZE { -FLT_MIN, 150 }
|
||||
|
||||
bool DebugManager::update(RootWidget* rootWidget, EventHandler& events) {
|
||||
mRootWidget = rootWidget;
|
||||
|
||||
events.setEnableKeyEvents(true);
|
||||
if (events.isPressed(InputID::D)) mDebug = !mDebug;
|
||||
|
||||
if (mDebug) {
|
||||
auto area = rootWidget->mRoot.getAreaT();
|
||||
area.size -= { 400, 0 };
|
||||
rootWidget->mRoot.setArea(area);
|
||||
|
||||
events.setEnableKeyEvents(true);
|
||||
if (events.isPressed(InputID::K)) mDebugStopProcessing = !mDebugStopProcessing;
|
||||
if (mDebugStopProcessing) return false;
|
||||
|
||||
if (auto widget = rootWidget->mUpdateManager.mInFocusWidget) {
|
||||
if (auto lay = dynamic_cast<BasicLayout*>(widget->getLayout())) {
|
||||
if (events.isPressed(InputID::V)) lay->setLayoutPolicy(LayoutPolicy::Vertical);
|
||||
if (events.isPressed(InputID::H)) lay->setLayoutPolicy(LayoutPolicy::Horizontal);
|
||||
|
||||
auto sizing = lay->getSizePolicy();
|
||||
|
||||
if (events.isDown(InputID::X)) {
|
||||
if (events.isPressed(InputID::S)) lay->setSizePolicy(SizePolicy::Minimal, sizing.y);
|
||||
if (events.isPressed(InputID::E)) lay->setSizePolicy(SizePolicy::Expanding, sizing.y);
|
||||
}
|
||||
if (events.isDown(InputID::Y)) {
|
||||
if (events.isPressed(InputID::S)) lay->setSizePolicy(sizing.x, SizePolicy::Minimal);
|
||||
if (events.isPressed(InputID::E)) lay->setSizePolicy(sizing.x, SizePolicy::Expanding);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (auto breakWidget = rootWidget->mUpdateManager.mInFocusWidget) {
|
||||
if (events.isPressed(InputID::P)) mProcBreakpoints.insert(breakWidget);
|
||||
if (events.isPressed(InputID::L)) mLayBreakpoints.insert(breakWidget);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DebugManager::drawDebug(RootWidget* rootWidget, Canvas& canvas) {
|
||||
mRootWidget = rootWidget;
|
||||
|
||||
if (!mDebug) {
|
||||
return;
|
||||
}
|
||||
|
||||
// ImGui::Checkbox("Draw debug", &mDebug);
|
||||
|
||||
ImGui::SameLine(); ImGui::Text("To Toggle processing press k");
|
||||
|
||||
auto& upd = rootWidget->mUpdateManager;
|
||||
|
||||
recursiveDraw(canvas, &rootWidget->mRoot, { 0, 0 }, 0);
|
||||
|
||||
ImGui::Text("Triggered: %i", (int) upd.mTriggeredWidgets.size());
|
||||
ImGui::SameLine(); ImGui::Text("Processing: %i", upd.mDebugWidgetsToProcess);
|
||||
|
||||
ImGui::Checkbox("Stop processing", &mDebugStopProcessing);
|
||||
ImGui::SameLine(); ImGui::Checkbox("Force new frames", &mDebugRedrawAlways);
|
||||
ImGui::SameLine(); ImGui::Checkbox("Slow-mo", &mSlowMotion);
|
||||
|
||||
if (upd.mInFocusWidget) {
|
||||
ImGui::Text("Under cursor");
|
||||
{
|
||||
if (ImGui::BeginListBox("##under_cursor", LIST_SIZE)) {
|
||||
for (auto widget = upd.mInFocusWidget; widget && widget->mParent; widget = widget->mParent) {
|
||||
widgetMenu(widget);
|
||||
}
|
||||
ImGui::EndListBox();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Text("Triggered");
|
||||
{
|
||||
if (ImGui::BeginListBox("##triggered", LIST_SIZE)) {
|
||||
for (auto widget : upd.mTriggeredWidgets) {
|
||||
widgetMenu(widget.first);
|
||||
}
|
||||
ImGui::EndListBox();
|
||||
}
|
||||
}
|
||||
|
||||
drawLayoutOrder();
|
||||
}
|
||||
|
||||
void DebugManager::recursiveDraw(Canvas& canvas, Widget* active, const Vec2F& pos, int depthOrder) {
|
||||
auto area = RectF{ pos, active->getAreaT().size };
|
||||
|
||||
if (active->isUpdate()) {
|
||||
RGBA color = { 1, 0, 0, 1 };
|
||||
canvas.frame(area, color);
|
||||
canvas.text((active->mDebug.id + ":" + std::to_string(depthOrder)).c_str(), area, 22, Canvas::Align::LC, 2, color);
|
||||
}
|
||||
|
||||
if (active->mFlags.get(Widget::IN_FOCUS)) {
|
||||
if (active->isUpdate()) {
|
||||
canvas.circle(pos + active->mDebug.pLocal, 5, active->mDebug.col);
|
||||
canvas.circle(active->mDebug.pGlobal, 15, active->mDebug.col);
|
||||
}
|
||||
|
||||
RGBA color = { 1, 0, 0, 0.3f };
|
||||
canvas.debugCross(area, color);
|
||||
}
|
||||
|
||||
int orderIdx = 0;
|
||||
for (auto child = active->mDepthOrder.lastNode(); child; child = child->prev) {
|
||||
recursiveDraw(canvas, child->data, pos + child->data->getAreaT().pos, orderIdx++);
|
||||
}
|
||||
}
|
||||
|
||||
void DebugManager::widgetMenu(Widget* widget) {
|
||||
|
||||
ImGui::PushID(widget);
|
||||
|
||||
if (ImGui::CollapsingHeader((widget->mDebug.id + std::to_string((long) widget)).c_str())) {
|
||||
|
||||
ImGui::Text("trigger reason: %s", widget->mDebug.triggerReason.c_str());
|
||||
|
||||
auto area = widget->getAreaT();
|
||||
if (ImGui::InputFloat4("rect", &area.x)) {
|
||||
widget->setArea(area);
|
||||
}
|
||||
|
||||
auto layout = widget->getLayout();
|
||||
|
||||
ImGui::InputFloat2("min size", &layout->mMinSize.x);
|
||||
ImGui::InputFloat2("max size", &layout->mMaxSize.x);
|
||||
|
||||
{
|
||||
int sizePolicyX = int(widget->getLayout()->getSizePolicy().x);
|
||||
int sizePolicyY = int(widget->getLayout()->getSizePolicy().y);
|
||||
|
||||
if (ImGui::Combo("Size Policy X", &sizePolicyX, "Fixed\0Expanding\0Minimal\0")) {
|
||||
widget->setSizePolicy(SizePolicy(sizePolicyX), SizePolicy(sizePolicyY));
|
||||
}
|
||||
|
||||
if (ImGui::Combo("Size Policy Y", &sizePolicyY, "Fixed\0Expanding\0Minimal\0")) {
|
||||
widget->setSizePolicy(SizePolicy(sizePolicyX), SizePolicy(sizePolicyY));
|
||||
}
|
||||
}
|
||||
|
||||
if (auto pBasicLayout = dynamic_cast<BasicLayout*>(widget->getLayout())) {
|
||||
int policy = int(pBasicLayout->mLayoutPolicy);
|
||||
if (ImGui::Combo("Layout", &policy, "Passive\0Vertical\0Horizontal\0")) {
|
||||
pBasicLayout->mLayoutPolicy = LayoutPolicy(policy);
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
void DebugManager::drawLayoutOrder() {
|
||||
ImGui::Text("Layout Processing Order");
|
||||
if (ImGui::BeginListBox("##layout_order", LIST_SIZE)) {
|
||||
for (auto iter : mRootWidget->mLayoutManager.mLayOrder) {
|
||||
ImGui::Text("%i %s (%s)", iter.second, iter.first->mDebug.id.c_str(), std::to_string((long) iter.first).c_str());
|
||||
}
|
||||
ImGui::EndListBox();
|
||||
}
|
||||
}
|
||||
98
Widgets/private/managers/LayoutManager.cpp
Normal file
98
Widgets/private/managers/LayoutManager.cpp
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
#include "LayoutManager.hpp"
|
||||
#include "DebugManager.hpp"
|
||||
|
||||
#include "DockLayout.hpp"
|
||||
#include "ScrollableLayout.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
using namespace tp;
|
||||
|
||||
void LayoutManager::adjust(Widget* root) {
|
||||
for (auto i : IterRange(2)) {
|
||||
mVertical = bool(i);
|
||||
|
||||
mDepGraph.clear();
|
||||
mLayOrder.clear();
|
||||
mRoots.clear();
|
||||
|
||||
findDependencies(root);
|
||||
|
||||
// TODO : implement better topological-sort
|
||||
for (auto iterRoot : mRoots) {
|
||||
topologicalSort(iterRoot);
|
||||
}
|
||||
|
||||
adjustLayouts();
|
||||
}
|
||||
}
|
||||
|
||||
void LayoutManager::findDependencies(Widget* root) {
|
||||
mDepGraph.insert({ root, {} });
|
||||
|
||||
Widget::dfs(root, [this](Widget* widget) { mDepGraph.insert({ widget, {} }); });
|
||||
|
||||
for (auto& [widget, deps] : mDepGraph) {
|
||||
for (auto child : widget->mChildren) {
|
||||
mDepGraph.insert({ child, {} });
|
||||
|
||||
if (auto depOrder = getLayoutOrder(widget->getLayout(), child->getLayout())) {
|
||||
if (depOrder > 0) {
|
||||
deps.depends.push_back(child);
|
||||
mDepGraph[child].references++;
|
||||
} else {
|
||||
mDepGraph[child].depends.push_back(widget);
|
||||
mDepGraph[widget].references++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// find root
|
||||
for (auto& node : mDepGraph) {
|
||||
if (node.second.references == 0) {
|
||||
mRoots.push_back(node.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LayoutManager::topologicalSort(Widget* root, int depth) {
|
||||
mDepGraph.at(root).depth = max(mDepGraph.at(root).depth, depth);
|
||||
|
||||
for (auto& dep : mDepGraph.at(root).depends) {
|
||||
topologicalSort(dep, depth + 1);
|
||||
}
|
||||
}
|
||||
|
||||
void LayoutManager::adjustLayouts() {
|
||||
for (auto& iter : mDepGraph) {
|
||||
mLayOrder.emplace_back(iter.first, iter.second.depth);
|
||||
}
|
||||
|
||||
std::sort(mLayOrder.begin(), mLayOrder.end(), [](auto first, auto second){
|
||||
return first.second > second.second;
|
||||
});
|
||||
|
||||
for (auto& [iter, _] : mLayOrder) {
|
||||
iter->getLayout()->updateLayout(mVertical);
|
||||
}
|
||||
}
|
||||
|
||||
static int sizePolicyDep[3][3] = {
|
||||
// f s e child
|
||||
{ 1, 1, 1 }, // parent fixed
|
||||
{ 1, 1, 1 }, // parent shrink
|
||||
{ 1, 1, 1 }, // parent expand
|
||||
};
|
||||
|
||||
int LayoutManager::getLayoutOrder(WidgetLayout* parent, WidgetLayout* child) const {
|
||||
if (!parent || !child) return 0;
|
||||
|
||||
auto policyParent = parent->getSizePolicy()[mVertical];
|
||||
auto policyChild = parent->getSizePolicy()[mVertical];
|
||||
|
||||
if (dynamic_cast<DockLayout*>(parent)) return -1;
|
||||
// if (dynamic_cast<ScrollableLayout*>(parent)) return -1;
|
||||
|
||||
return sizePolicyDep[int(policyParent)][int(policyChild)];
|
||||
}
|
||||
211
Widgets/private/managers/UpdateManager.cpp
Normal file
211
Widgets/private/managers/UpdateManager.cpp
Normal file
|
|
@ -0,0 +1,211 @@
|
|||
#include "UpdateManager.hpp"
|
||||
#include "DebugManager.hpp"
|
||||
|
||||
using namespace tp;
|
||||
|
||||
void UpdateManager::processWidgets(Widget* root, EventHandler& events) {
|
||||
events.setEnableKeyEvents(true);
|
||||
events.setCursorOrigin({ 0, 0 });
|
||||
|
||||
processFocusItems(events);
|
||||
|
||||
events.setEnableKeyEvents(false);
|
||||
events.setCursorOrigin({ 0, 0 });
|
||||
|
||||
processActiveTree(root, events, { 0, 0 });
|
||||
}
|
||||
|
||||
void UpdateManager::clean() {
|
||||
erase_if(mTriggeredWidgets, [](auto iter) {
|
||||
auto widget = iter.first;
|
||||
auto flag = iter.second;
|
||||
|
||||
if (!flag) return false;
|
||||
|
||||
// if (mWidgetsToProcess.find(widget) == mWidgetsToProcess.end()) return false;
|
||||
widget->updateAnimations();
|
||||
auto end = !widget->needsNextFrame();
|
||||
if (end) {
|
||||
widget->endAnimations();
|
||||
widget->mDebug.triggerReason = "del";
|
||||
}
|
||||
return end;
|
||||
});
|
||||
}
|
||||
|
||||
void UpdateManager::scheduleUpdate(Widget* widget, const char* reason) {
|
||||
widget->mDebug.triggerReason = reason;
|
||||
mTriggeredWidgets.insert({ widget, false });
|
||||
}
|
||||
|
||||
void UpdateManager::updateTreeToProcess(Widget* root) {
|
||||
Widget::dfs(root, [](Widget* widget) {
|
||||
widget->mFlags.set(Widget::NEEDS_UPDATE, false);
|
||||
});
|
||||
|
||||
for (auto& [widget, flag] : mTriggeredWidgets) {
|
||||
flag = true;
|
||||
|
||||
for (auto iter = widget; iter && iter->mParent; iter = iter->mParent) {
|
||||
iter->mFlags.set(Widget::NEEDS_UPDATE, true);
|
||||
}
|
||||
}
|
||||
|
||||
mDebugWidgetsToProcess = 0;
|
||||
Widget::dfs(root, [this](Widget*) {
|
||||
mDebugWidgetsToProcess++;
|
||||
});
|
||||
}
|
||||
|
||||
void UpdateManager::getWidgetPath(Widget* widget, std::vector<Widget*>& out) {
|
||||
if (!widget) return;
|
||||
|
||||
for (auto iter = widget; iter && iter->mParent; iter = iter->mParent) {
|
||||
out.push_back(iter);
|
||||
}
|
||||
|
||||
for (auto i = 0; i < out.size() / 2; i++) {
|
||||
swapV(out[i], out[out.size() - i - 1]);
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateManager::handleFocusChanges(Widget* root, EventHandler& events) {
|
||||
auto prevFocus = mInFocusWidget;
|
||||
|
||||
events.setCursorOrigin({ 0, 0 });
|
||||
|
||||
mInFocusWidget = nullptr;
|
||||
|
||||
if (!mFocusLockWidget) {
|
||||
findFocusWidget(root, &mInFocusWidget, events.getPointer());
|
||||
} else {
|
||||
mInFocusWidget = mFocusLockWidget;
|
||||
}
|
||||
|
||||
// if (mInFocusWidget == prevFocus) return;
|
||||
if (mInFocusWidget) scheduleUpdate(mInFocusWidget, "focus entered");
|
||||
|
||||
if (!mInFocusWidget && !prevFocus) return;
|
||||
|
||||
std::vector<Widget*> path2;
|
||||
getWidgetPath(mInFocusWidget, path2);
|
||||
size_t propLen2 = path2.size();
|
||||
for (auto i = 0; i < path2.size(); i++) {
|
||||
if (!path2[i]->propagateEventsToChildren()) {
|
||||
propLen2 = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Widget*> path1;
|
||||
getWidgetPath(prevFocus, path1);
|
||||
size_t propLen1 = path1.size();
|
||||
for (auto i = 0; i < path1.size(); i++) {
|
||||
if (!path1[i]->propagateEventsToChildren()) {
|
||||
propLen1 = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int mostCommonIdx = 0;
|
||||
if (!(path1.empty() || path2.empty())) {
|
||||
while (path1[mostCommonIdx] == path2[mostCommonIdx] && mostCommonIdx < min(path1.size(), path2.size())) {
|
||||
mostCommonIdx++;
|
||||
}
|
||||
}
|
||||
mostCommonIdx--;
|
||||
|
||||
for (auto i = 0; i < path1.size(); i++) {
|
||||
path1[i]->mFlags.set(Widget::IN_FOCUS, false);
|
||||
if (i > mostCommonIdx && i < propLen1) path1[i]->mouseLeave();
|
||||
}
|
||||
|
||||
for (auto i = 0; i < path2.size(); i++) {
|
||||
path2[i]->mFlags.set(Widget::IN_FOCUS, true);
|
||||
if (i > mostCommonIdx && i < propLen2) path2[i]->mouseEnter();
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateManager::findFocusWidget(Widget* iter, Widget** focus, const Vec2F& pointer) {
|
||||
if (!iter->mArea.getTargetRect().isInside(pointer) || !iter->mFlags.get(Widget::ENABLED)) return;
|
||||
|
||||
if (iter->processesEvents()) {
|
||||
*focus = iter;
|
||||
}
|
||||
|
||||
for (auto child = iter->mDepthOrder.lastNode(); child; child = child->prev) {
|
||||
findFocusWidget(child->data, focus, pointer - iter->mArea.getTargetRect().pos);
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateManager::processActiveTree(Widget* iter, EventHandler& events, Vec2F parent) {
|
||||
if (!iter || !iter->isUpdate()) return;
|
||||
|
||||
auto current = parent + iter->getAreaT().pos;
|
||||
|
||||
if (!iter->mFlags.get(Widget::IN_FOCUS)) {
|
||||
iter->mDebug.pGlobal = current;
|
||||
|
||||
events.setCursorOrigin(current);
|
||||
procWidget(iter, events, false);
|
||||
}
|
||||
|
||||
for (auto child : iter->mDepthOrder) {
|
||||
processActiveTree(child.data(), events, current);
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateManager::processFocusItems(EventHandler& events) {
|
||||
if (!mInFocusWidget) return;
|
||||
|
||||
std::vector<Widget*> path;
|
||||
getWidgetPath(mInFocusWidget, path);
|
||||
|
||||
size_t len = path.size();
|
||||
for (auto i = 0; i < len; i++) {
|
||||
if (!path[i]->propagateEventsToChildren()) {
|
||||
len = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Vec2F> widgetGlobalPos(path.size());
|
||||
|
||||
widgetGlobalPos[0] = 0;
|
||||
for (auto widget = 0; widget < path.size() - 1; widget++) {
|
||||
widgetGlobalPos[widget + 1] = widgetGlobalPos[widget] + path[widget + 1]->getAreaT().pos;
|
||||
// path[widget]->mGlobalPoint = widgetGlobalPos[widget];
|
||||
}
|
||||
|
||||
bool eventsProcessed = false;
|
||||
|
||||
for (int iter = (int) len - 1; iter >= 0; iter--) {
|
||||
auto widget = path[iter];
|
||||
|
||||
events.setCursorOrigin(widgetGlobalPos[iter]);
|
||||
|
||||
widget->mDebug.pGlobal = widgetGlobalPos[iter];
|
||||
|
||||
if (!eventsProcessed && widget->processesEvents()) {
|
||||
procWidget(widget, events, true);
|
||||
eventsProcessed = true;
|
||||
} else {
|
||||
procWidget(widget, events, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateManager::procWidget(Widget* widget, EventHandler& events, bool withEvents) {
|
||||
events.setEnableKeyEvents(withEvents);
|
||||
gDebugWidget.checkProcBreakPoints(widget);
|
||||
widget->process(events);
|
||||
}
|
||||
|
||||
void UpdateManager::lockFocus(tp::Widget* widget) {
|
||||
mFocusLockWidget = widget;
|
||||
}
|
||||
|
||||
void UpdateManager::freeFocus(tp::Widget* widget) {
|
||||
// DEBUG_ASSERT(mFocusLockWidget == widget)
|
||||
mFocusLockWidget = nullptr;
|
||||
}
|
||||
28
Widgets/private/widgets/AnimationTestWidget.cpp
Normal file
28
Widgets/private/widgets/AnimationTestWidget.cpp
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
#include "AnimationTestWidget.hpp"
|
||||
|
||||
using namespace tp;
|
||||
|
||||
void AnimationTestWidget::process(const EventHandler& events) {
|
||||
if (events.isDown(tp::InputID::MOUSE1)) {
|
||||
mTestSpring.getStart().setTargetPosition(events.getPointer());
|
||||
}
|
||||
|
||||
if (events.isDown(tp::InputID::MOUSE2)) {
|
||||
mTestSpring.getEnd().setTargetPosition(events.getPointer());
|
||||
}
|
||||
|
||||
mTestSpring.updateCurrentRect();
|
||||
|
||||
if (mTestSpring.shouldEndTransition()) {
|
||||
mTestSpring.endAnimation();
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationTestWidget::draw(Canvas& canvas) {
|
||||
canvas.rect(getRelativeArea(), { 0, 0, 0, 1 }, 10);
|
||||
canvas.rect(mTestSpring.getCurrentRect(), RGBA(1.f), 10);
|
||||
}
|
||||
|
||||
bool AnimationTestWidget::needsNextFrame() const {
|
||||
return Widget::needsNextFrame() || !mTestSpring.shouldEndTransition();
|
||||
}
|
||||
138
Widgets/private/widgets/DockWidget.cpp
Normal file
138
Widgets/private/widgets/DockWidget.cpp
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
#include "DockWidget.hpp"
|
||||
#include "FloatingWidget.hpp"
|
||||
|
||||
using namespace tp;
|
||||
|
||||
DockWidget::DockWidget() : Widget() {
|
||||
setDebug("dock", { 1, 1, 1, 1 });
|
||||
setLayout(new DockLayout(this));
|
||||
}
|
||||
|
||||
void DockWidget::dockWidget(Widget* widget, DockLayout::Side side) {
|
||||
if (layout()->dockWidget(widget, side)) {
|
||||
addChild(widget);
|
||||
|
||||
for (auto child : layout()->getDockedWidgets()) {
|
||||
if (child) child->bringToBack();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DockWidget::undockWidget(DockLayout::Side side, bool restoreArea) {
|
||||
if (layout()->sideExists(side) && side != DockLayout::NONE) {
|
||||
auto widget = layout()->getSideWidget(side);
|
||||
|
||||
widget->bringToFront();
|
||||
if (restoreArea) {
|
||||
widget->setArea(layout()->getRectBeforeDocked(side));
|
||||
} else {
|
||||
// FIXME : widget->endAnimations();
|
||||
}
|
||||
|
||||
layout()->undockWidget(side);
|
||||
}
|
||||
}
|
||||
|
||||
void DockWidget::setCenterWidget(Widget* widget) {
|
||||
if (layout()->setCenterWidget(widget)) {
|
||||
addChild(widget);
|
||||
}
|
||||
}
|
||||
|
||||
void DockWidget::toggleWidgetVisibility(DockLayout::Side side) {
|
||||
if (layout()->sideExists(side)) {
|
||||
auto widget = layout()->getSideWidget(side);
|
||||
widget->setEnabled(!layout()->isSideVisible(side));
|
||||
widget->bringToBack();
|
||||
|
||||
layout()->toggleWidgetVisibility(side);
|
||||
}
|
||||
}
|
||||
|
||||
void DockWidget::process(const EventHandler& events) {
|
||||
// calculateHeaderAreas();
|
||||
|
||||
Widget* floater = nullptr;
|
||||
for (auto child : mChildren) {
|
||||
if (auto iter = dynamic_cast<FloatingWidget*>(child)) {
|
||||
if (iter->isFloating()) {
|
||||
floater = iter;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (floater) undockWidget(layout()->getSideFromWidget(floater), false);
|
||||
|
||||
if (floater) {
|
||||
layout()->updatePreviewSide(events.getPointer());
|
||||
}
|
||||
|
||||
if (mPreviewWidget && !floater) {
|
||||
dockWidget(mPreviewWidget, layout()->getPreviewSide());
|
||||
}
|
||||
|
||||
mPreviewWidget = floater;
|
||||
|
||||
if (!mPreviewWidget) {
|
||||
layout()->updateResizeHover(events.getPointer());
|
||||
|
||||
if (events.isPressed(InputID::MOUSE1)) {
|
||||
if (layout()->startResize(events.getPointer())) {
|
||||
triggerWidgetUpdate("dock layout resizing");
|
||||
}
|
||||
} else if (events.isReleased(InputID::MOUSE1)) {
|
||||
layout()->endResize();
|
||||
}
|
||||
|
||||
if (layout()->isResizing()) {
|
||||
layout()->updateResize(events.getPointerDelta());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool DockWidget::propagateEventsToChildren() const {
|
||||
return !layout()->isResizing();
|
||||
}
|
||||
|
||||
bool DockWidget::needsNextFrame() const {
|
||||
return Widget::needsNextFrame() || layout()->isResizing();
|
||||
}
|
||||
|
||||
void DockWidget::draw(Canvas& canvas) {
|
||||
canvas.rect(getRelativeArea(), mBackgroundColor, 0);
|
||||
}
|
||||
|
||||
void DockWidget::drawSide(DockLayout::Side side, tp::Canvas& canvas) {
|
||||
auto lay = layout();
|
||||
|
||||
if (lay->isSideVisible(side)) {
|
||||
// header
|
||||
canvas.rect(lay->getHeaderArea(side), mResizeHandleColorActive, 0);
|
||||
|
||||
// resize
|
||||
if (lay->isResizing(side)) {
|
||||
canvas.rect(lay->getResizeHandleArea(side).shrink(mPadding / 1.5f), mResizeHandleColorActive, 0);
|
||||
} else if (lay->isResizeHandleHover(side)) {
|
||||
canvas.rect(lay->getResizeHandleArea(side).shrink(mPadding / 1.5f), mResizeHandleColorHovered, 0);
|
||||
}
|
||||
|
||||
} else {
|
||||
// preview handles
|
||||
if (mPreviewWidget) {
|
||||
// preview active
|
||||
if (lay->getPreviewSide() == side) {
|
||||
canvas.rect(lay->getPreviewArea().shrink(mPadding * 2), mPreviewColor, mRounding);
|
||||
} else {
|
||||
canvas.rect(lay->getPreviewHandleArea(side), mPreviewColor, mRounding);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DockWidget::drawOverlay(Canvas& canvas) {
|
||||
drawSide(DockLayout::Side::LEFT, canvas);
|
||||
drawSide(DockLayout::Side::RIGHT, canvas);
|
||||
drawSide(DockLayout::Side::TOP, canvas);
|
||||
drawSide(DockLayout::Side::BOTTOM, canvas);
|
||||
}
|
||||
76
Widgets/private/widgets/FloatingWidget.cpp
Normal file
76
Widgets/private/widgets/FloatingWidget.cpp
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
#include "FloatingWidget.hpp"
|
||||
#include "ScrollableLayout.hpp"
|
||||
|
||||
using namespace tp;
|
||||
|
||||
void FloatingWidget::process(const EventHandler& events) {
|
||||
const auto pointer = events.getPointer();
|
||||
|
||||
if (getRelativeAreaT().isInside(pointer) && events.isPressed(InputID::MOUSE1)) {
|
||||
layout()->startAction(pointer);
|
||||
|
||||
bringToFront();
|
||||
lockFocus();
|
||||
}
|
||||
|
||||
if (layout()->isFloating() && events.isReleased(InputID::MOUSE1)) {
|
||||
layout()->endAction();
|
||||
freeFocus();
|
||||
}
|
||||
|
||||
layout()->updateAction(pointer);
|
||||
}
|
||||
|
||||
void FloatingWidget::draw(Canvas& canvas) {
|
||||
canvas.rect(layout()->resizeHandleRect(), RGBA(0.7f), 2);
|
||||
canvas.rect(getRelativeArea(), RGBA(0.5f), 10);
|
||||
}
|
||||
|
||||
bool FloatingWidget::processesEvents() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FloatingWidget::propagateEventsToChildren() const {
|
||||
return !layout()->isFloating();
|
||||
}
|
||||
|
||||
bool FloatingWidget::isFloating() const {
|
||||
return layout()->isFloating();
|
||||
}
|
||||
|
||||
bool FloatingWidget::needsNextFrame() const {
|
||||
return Widget::needsNextFrame() || isFloating();
|
||||
}
|
||||
|
||||
FloatingLayout* FloatingWidget::layout() { return dynamic_cast<FloatingLayout*>(Widget::getLayout()); }
|
||||
|
||||
const FloatingLayout* FloatingWidget::layout() const {
|
||||
return dynamic_cast<const FloatingLayout*>(Widget::getLayout());
|
||||
}
|
||||
|
||||
FloatingMenu::FloatingMenu() : FloatingWidget() {
|
||||
setDebug("float menu", { 0.0, 0.9, 0.1, 0.7 });
|
||||
|
||||
// addChild(&mMenuLayout);
|
||||
|
||||
addChild(&mHeader);
|
||||
addChild(&mBodyLayout);
|
||||
|
||||
mHeader.setText("Menu");
|
||||
|
||||
mHeader.setSizePolicy(SizePolicy::Expanding, SizePolicy::Minimal);
|
||||
mBodyLayout.setSizePolicy(SizePolicy::Expanding, SizePolicy::Expanding);
|
||||
|
||||
mBodyLayout.setLayout(new ScrollableLayout(&mBodyLayout));
|
||||
mBodyLayout.addChild(&mScrollBar);
|
||||
mBodyLayout.addChild(&mContentWidget);
|
||||
|
||||
mContentWidget.setSizePolicy(SizePolicy::Minimal, SizePolicy::Minimal);
|
||||
|
||||
// getLayout()->setLayoutPolicy(LayoutPolicy::Vertical);
|
||||
// mBodyLayout.getLayout()->setLayoutPolicy(LayoutPolicy::Vertical);
|
||||
}
|
||||
|
||||
void FloatingMenu::setText(const std::string& text) {
|
||||
mHeader.setText(text);
|
||||
}
|
||||
87
Widgets/private/widgets/ScrollableWidget.cpp
Normal file
87
Widgets/private/widgets/ScrollableWidget.cpp
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
#include "ScrollableWidget.hpp"
|
||||
|
||||
using namespace tp;
|
||||
|
||||
void ScrollableBarWidget::process(const EventHandler& events) {
|
||||
// all content is visible no need to process anything
|
||||
if (mSizeFactor >= 1) {
|
||||
mScrolling = false;
|
||||
mPosFactor = mSizeFactor / 2.f;
|
||||
freeFocus();
|
||||
return;
|
||||
}
|
||||
|
||||
updateHandleRect();
|
||||
|
||||
auto pointer = events.getPointer();
|
||||
auto size = getArea().size;
|
||||
|
||||
mHandleHovered = getHandleRect().isInside(pointer);
|
||||
|
||||
if (getRelativeAreaT().isInside(pointer) && events.isPressed(InputID::MOUSE1)) {
|
||||
if (!mHandleHovered) {
|
||||
jumpTo((pointer / size)[mVertical]);
|
||||
mStartPos = { 0, 0 };
|
||||
} else {
|
||||
mStartPos = pointer - getHandleRect().center();
|
||||
}
|
||||
mScrolling = true;
|
||||
lockFocus();
|
||||
}
|
||||
|
||||
if (mScrolling && events.isReleased(InputID::MOUSE1)) {
|
||||
mScrolling = false;
|
||||
freeFocus();
|
||||
}
|
||||
|
||||
if (mScrolling) {
|
||||
updateHandleRect();
|
||||
auto dragDelta = (pointer - getHandleRect().center()) - mStartPos;
|
||||
moveBy((dragDelta / size)[mVertical]);
|
||||
}
|
||||
}
|
||||
|
||||
void ScrollableBarWidget::jumpTo(halnf pos) {
|
||||
mPosFactor = pos;
|
||||
clamp();
|
||||
}
|
||||
|
||||
void ScrollableBarWidget::moveBy(halnf delta) {
|
||||
mPosFactor += delta;
|
||||
clamp();
|
||||
}
|
||||
|
||||
void ScrollableBarWidget::updateSizeFactor(halnf factor) {
|
||||
mSizeFactor = factor;
|
||||
clamp();
|
||||
}
|
||||
|
||||
void ScrollableBarWidget::clamp() {
|
||||
mSizeFactor = ::clamp(mSizeFactor, 0.f, 1.f);
|
||||
mPosFactor = ::clamp(mPosFactor, mSizeFactor / 2, 1.f - mSizeFactor / 2);
|
||||
}
|
||||
|
||||
void ScrollableBarWidget::draw(Canvas& canvas) {
|
||||
updateHandleRect();
|
||||
|
||||
canvas.rect(getArea().relative(), mBGColor, mRounding);
|
||||
|
||||
auto& handleColor = mScrolling ? mHandleSlideColor : (mHandleHovered ? mHandleHoverColor : mHandleColor);
|
||||
canvas.rect(getHandleRect(), handleColor, mRounding);
|
||||
}
|
||||
|
||||
void ScrollableBarWidget::updateHandleRect() {
|
||||
auto area = getArea().relative();
|
||||
|
||||
mHandleRect = area;
|
||||
mHandleRect.size[mVertical] = area.size[mVertical] * mSizeFactor;
|
||||
mHandleRect.pos[mVertical] = area.size[mVertical] * mPosFactor;
|
||||
|
||||
mHandleRect.pos[mVertical] -= mHandleRect.size[mVertical] / 2;
|
||||
|
||||
mHandleRect.shrinkFromCenter(mHandlePadding, true);
|
||||
}
|
||||
|
||||
const RectF& ScrollableBarWidget::getHandleRect() const {
|
||||
return mHandleRect;
|
||||
}
|
||||
130
Widgets/private/widgets/SimpleWidgets.cpp
Normal file
130
Widgets/private/widgets/SimpleWidgets.cpp
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
|
||||
#include "SimpleWidgets.hpp"
|
||||
|
||||
using namespace tp;
|
||||
|
||||
void LabelWidget::setText(const std::string& text) {
|
||||
mText = text;
|
||||
}
|
||||
|
||||
const std::string& LabelWidget::getText() const {
|
||||
return mText;
|
||||
}
|
||||
|
||||
void LabelWidget::draw(Canvas& canvas) {
|
||||
canvas.text(mText.c_str(), getRelativeArea(), mSize, Canvas::LC, mPadding, mColor);
|
||||
}
|
||||
|
||||
ButtonWidget::ButtonWidget() {
|
||||
mAction = []() {
|
||||
printf("Button Pressed!\n");
|
||||
};
|
||||
|
||||
setDebug("button", { 0.1, 0.1, 0.7, 0.7 });
|
||||
|
||||
mColorAnimated.setTargetColor(mColor);
|
||||
mColorAnimated.endAnimation();
|
||||
}
|
||||
|
||||
void ButtonWidget::setAction(const std::function<void()>& action) {
|
||||
mAction = action;
|
||||
}
|
||||
|
||||
void ButtonWidget::setColor(const RGBA& in) {
|
||||
mColor = in;
|
||||
mColorAnimated.setTargetColor(mColor);
|
||||
triggerWidgetUpdate("color changed");
|
||||
}
|
||||
|
||||
void ButtonWidget::process(const EventHandler& eventHandler) {
|
||||
if (getRelativeArea().isInside(eventHandler.getPointer())) {
|
||||
if (eventHandler.isPressed(InputID::MOUSE1)) {
|
||||
mAction();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ButtonWidget::draw(Canvas& canvas) {
|
||||
canvas.rect(getRelativeArea(), mColorAnimated.getCurrentColor(), mRounding);
|
||||
LabelWidget::draw(canvas);
|
||||
}
|
||||
|
||||
bool ButtonWidget::needsNextFrame() const {
|
||||
return LabelWidget::needsNextFrame() || !mColorAnimated.shouldEndTransition();
|
||||
}
|
||||
|
||||
void ButtonWidget::endAnimations() {
|
||||
mColorAnimated.endAnimation();
|
||||
LabelWidget::endAnimations();
|
||||
}
|
||||
|
||||
void ButtonWidget::updateAnimations() {
|
||||
mColorAnimated.updateCurrentRect();
|
||||
LabelWidget::updateAnimations();
|
||||
}
|
||||
|
||||
void ButtonWidget::mouseEnter() {
|
||||
mColorAnimated.setTargetColor(mColor);
|
||||
mColorAnimated.endAnimation();
|
||||
|
||||
mColorAnimated.setTargetColor(mColorHovered);
|
||||
|
||||
// mColorAnimated.updateCurrentRect();
|
||||
triggerWidgetUpdate("button hovered");
|
||||
}
|
||||
|
||||
void ButtonWidget::mouseLeave() {
|
||||
mColorAnimated.setTargetColor(mColor);
|
||||
//mColorAnimated.updateCurrentRect();
|
||||
|
||||
triggerWidgetUpdate("button out of focus");
|
||||
}
|
||||
|
||||
|
||||
void SliderWidget::process(const EventHandler& events) {
|
||||
const auto pointer = events.getPointer();
|
||||
|
||||
switch (mState) {
|
||||
case SLIDING:
|
||||
if (events.isReleased(InputID::MOUSE1)) {
|
||||
mState = IDLE;
|
||||
freeFocus();
|
||||
}
|
||||
|
||||
mFactor = ((pointer - mHandleSize / 2) / (getArea().relative().size - mHandleSize)).x;
|
||||
mFactor = clamp(mFactor, 0.f, 1.f);
|
||||
break;
|
||||
|
||||
case IDLE:
|
||||
case HOVER:
|
||||
mState = getHandleArea().isInside(pointer) ? HOVER : IDLE;
|
||||
if (getRelativeAreaT().isInside(pointer) && events.isPressed(InputID::MOUSE1)) {
|
||||
mState = SLIDING;
|
||||
lockFocus();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void SliderWidget::draw(Canvas& canvas) {
|
||||
canvas.rect(getArea().relative(), mColorBG, mRounding);
|
||||
|
||||
switch (mState) {
|
||||
case IDLE: canvas.rect(getHandleArea(), mColorIdle, mRounding); break;
|
||||
case HOVER: canvas.rect(getHandleArea(), mColorHovered, mRounding); break;
|
||||
case SLIDING: canvas.rect(getHandleArea(), mColorActive, mRounding); break;
|
||||
}
|
||||
}
|
||||
|
||||
RectF SliderWidget::getHandleArea() const {
|
||||
auto area = getArea().relative();
|
||||
|
||||
const auto& size = area.size;
|
||||
auto center = area.center();
|
||||
auto tilt = -(mFactor - 0.5f) * 2;
|
||||
|
||||
area.pos.x = center.x - (tilt * ((size.x - mHandleSize) / 2)) - mHandleSize / 2;
|
||||
area.size.x = mHandleSize;
|
||||
|
||||
return area;
|
||||
}
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
#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&);
|
||||
};
|
||||
};
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
#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 = [](){};
|
||||
};
|
||||
}
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
#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;
|
||||
};
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
#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;
|
||||
};
|
||||
}
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
#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;
|
||||
};
|
||||
}
|
||||
|
|
@ -1,88 +0,0 @@
|
|||
#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;
|
||||
};
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
#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 };
|
||||
};
|
||||
}
|
||||
70
Widgets/public/Layout.hpp
Normal file
70
Widgets/public/Layout.hpp
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
#pragma once
|
||||
|
||||
#include "Rect.hpp"
|
||||
#include <vector>
|
||||
|
||||
namespace tp {
|
||||
class Widget;
|
||||
|
||||
enum class SizePolicy {
|
||||
Fixed,
|
||||
Expanding,
|
||||
Minimal,
|
||||
};
|
||||
|
||||
enum class LayoutPolicy {
|
||||
Passive,
|
||||
Vertical,
|
||||
Horizontal,
|
||||
};
|
||||
|
||||
class WidgetLayout {
|
||||
friend class DebugManager;
|
||||
friend class LayoutManager;
|
||||
|
||||
public:
|
||||
explicit WidgetLayout(Widget* widget) { mWidget = widget; }
|
||||
virtual ~WidgetLayout() = default;
|
||||
|
||||
virtual void updateLayout(bool vertical) {}
|
||||
|
||||
virtual void pickRect(bool vertical) {}
|
||||
virtual void clampRect() {}
|
||||
[[nodiscard]] virtual RectF getAvailableChildArea() const;
|
||||
|
||||
public:
|
||||
const Vec2F& getMinSize();
|
||||
void setMinSize(const Vec2F& size);
|
||||
|
||||
[[nodiscard]] const Vec2<SizePolicy>& getSizePolicy() const;
|
||||
void setSizePolicy(SizePolicy x, SizePolicy y);
|
||||
|
||||
public:
|
||||
[[nodiscard]] const RectF& getArea() const;
|
||||
[[nodiscard]] RectF getAnimatedArea() const;
|
||||
|
||||
void setArea(const RectF& area);
|
||||
[[nodiscard]] Widget* parent() const;
|
||||
[[nodiscard]] const std::vector<Widget*>& children() const;
|
||||
|
||||
public:
|
||||
void clampMinMaxSize();
|
||||
|
||||
[[nodiscard]] RangeF pickRange(const RangeF& current, const RangeF& child, const RangeF& parent, bool v) const;
|
||||
[[nodiscard]] RangeF clampRange(const RangeF& current, const RangeF& child, const RangeF& parent, bool v) const;
|
||||
|
||||
[[nodiscard]] RectF getChildrenEnclosure() const;
|
||||
[[nodiscard]] RectF getParentEnclosure() const;
|
||||
|
||||
private:
|
||||
Widget* mWidget = nullptr;
|
||||
|
||||
protected:
|
||||
Vec2<SizePolicy> mSizePolicy = { SizePolicy::Fixed, SizePolicy::Fixed };
|
||||
Vec2F mMinSize = { 50, 50 };
|
||||
Vec2F mMaxSize = { FLT_MAX / 2, FLT_MAX / 2 };
|
||||
|
||||
private:
|
||||
// RectF mPrevArea{};
|
||||
};
|
||||
}
|
||||
53
Widgets/public/RootWidget.hpp
Normal file
53
Widgets/public/RootWidget.hpp
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
#pragma once
|
||||
|
||||
#include "Widget.hpp"
|
||||
|
||||
#include "UpdateManager.hpp"
|
||||
#include "LayoutManager.hpp"
|
||||
#include "DebugManager.hpp"
|
||||
|
||||
namespace tp {
|
||||
class RootWidget : public WidgetManagerInterface {
|
||||
friend DebugManager;
|
||||
|
||||
public:
|
||||
RootWidget();
|
||||
|
||||
// User Interface
|
||||
public:
|
||||
void setRootWidget(Widget* widget);
|
||||
static void setWidgetArea(Widget& widget, const RectF& rect);
|
||||
|
||||
[[nodiscard]] bool isDebug() const;
|
||||
|
||||
// Graphic Application Interface
|
||||
public:
|
||||
void processFrame(EventHandler* events, const RectF& screenArea);
|
||||
void drawFrame(Canvas& canvas);
|
||||
[[nodiscard]] bool needsUpdate() const;
|
||||
|
||||
// Internals
|
||||
private:
|
||||
void drawRecursion(Canvas& canvas, Widget* active, const Vec2F& pos);
|
||||
|
||||
void updateWidget(Widget*, const char* reason = nullptr) override;
|
||||
|
||||
void openPopup(Widget*) override;
|
||||
void closePopup(Widget*) override;
|
||||
|
||||
void lockFocus(Widget*) override;
|
||||
void freeFocus(Widget*) override;
|
||||
|
||||
void updateAnimations();
|
||||
void updateAreaCache(Widget* iter, bool read);
|
||||
|
||||
private:
|
||||
Widget mRoot;
|
||||
Widget mPopups;
|
||||
|
||||
Widget* mUserRoot = nullptr;
|
||||
|
||||
LayoutManager mLayoutManager;
|
||||
UpdateManager mUpdateManager;
|
||||
};
|
||||
}
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
#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;
|
||||
};
|
||||
}
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
#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;
|
||||
};
|
||||
}
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
#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;
|
||||
};
|
||||
}
|
||||
155
Widgets/public/Widget.hpp
Normal file
155
Widgets/public/Widget.hpp
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
#pragma once
|
||||
|
||||
#include "SpringAnimations.hpp"
|
||||
|
||||
#include "Layout.hpp"
|
||||
|
||||
#include "EventHandler.hpp"
|
||||
#include "Graphics.hpp"
|
||||
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
namespace tp {
|
||||
class WidgetLayout;
|
||||
|
||||
class LayoutManager;
|
||||
class UpdateManager;
|
||||
class DebugManager;
|
||||
|
||||
class WidgetManagerInterface;
|
||||
class RootWidget;
|
||||
|
||||
class Widget {
|
||||
friend RootWidget;
|
||||
|
||||
friend LayoutManager;
|
||||
friend UpdateManager;
|
||||
friend DebugManager;
|
||||
|
||||
using BitField = Bits<ualni>;
|
||||
|
||||
using DFSAction = std::function<void(Widget*)>;
|
||||
|
||||
enum Flags : int1 {
|
||||
ENABLED = 0,
|
||||
NEEDS_UPDATE,
|
||||
IN_FOCUS,
|
||||
TRIGGERED,
|
||||
};
|
||||
|
||||
public:
|
||||
Widget(const Widget&&) = delete;
|
||||
Widget(const Widget&) = delete;
|
||||
void operator=(const Widget&) = delete;
|
||||
|
||||
Widget();
|
||||
virtual ~Widget();
|
||||
|
||||
void addChild(Widget* child, bool front = false);
|
||||
void removeChild(Widget* child);
|
||||
void clear();
|
||||
|
||||
const List<Widget*>& getChildren() { return mDepthOrder; }
|
||||
|
||||
void bringToFront();
|
||||
void bringToBack();
|
||||
|
||||
void setLayout(WidgetLayout* layout);
|
||||
void setSizePolicy(SizePolicy x, SizePolicy y);
|
||||
|
||||
void setEnabled(bool val) { mFlags.set(ENABLED, val); }
|
||||
|
||||
WidgetLayout* getLayout();
|
||||
[[nodiscard]] const WidgetLayout* getLayout() const;
|
||||
|
||||
void triggerWidgetUpdate(const char* reason = nullptr);
|
||||
|
||||
void openPopup(Widget*);
|
||||
void closePopup(Widget*);
|
||||
|
||||
void lockFocus();
|
||||
void freeFocus();
|
||||
|
||||
protected:
|
||||
virtual void process(const EventHandler& events) {}
|
||||
virtual void draw(Canvas& canvas) {}
|
||||
virtual void drawOverlay(Canvas& canvas) {}
|
||||
virtual void endAnimations();
|
||||
virtual void updateAnimations();
|
||||
|
||||
[[nodiscard]] virtual bool processesEvents() const;
|
||||
[[nodiscard]] virtual bool propagateEventsToChildren() const;
|
||||
[[nodiscard]] virtual bool needsNextFrame() const;
|
||||
|
||||
virtual void mouseEnter();
|
||||
virtual void mouseLeave();
|
||||
|
||||
protected:
|
||||
void setDebug(const char* name, RGBA col);
|
||||
WidgetManagerInterface* getRoot();
|
||||
|
||||
public:
|
||||
[[nodiscard]] RectF getArea() const;
|
||||
[[nodiscard]] RectF getAreaT() const;
|
||||
|
||||
[[nodiscard]] RectF getRelativeArea() const;
|
||||
[[nodiscard]] RectF getRelativeAreaT() const;
|
||||
|
||||
void setArea(const RectF& area);
|
||||
void setAreaCache(const RectF& area);
|
||||
|
||||
private:
|
||||
[[nodiscard]] bool isUpdate() const { return mFlags.get(ENABLED) && mFlags.get(NEEDS_UPDATE); }
|
||||
[[nodiscard]] bool isDraw() const { return mFlags.get(ENABLED); }
|
||||
|
||||
static void dfs(Widget* iter, const DFSAction& before, const DFSAction& after = [](auto){}) {
|
||||
if (!iter->isUpdate()) return;
|
||||
|
||||
before(iter);
|
||||
|
||||
for (auto child : iter->mDepthOrder) {
|
||||
dfs(child.data(), before, after);
|
||||
}
|
||||
|
||||
after(iter);
|
||||
}
|
||||
|
||||
protected:
|
||||
friend WidgetLayout;
|
||||
|
||||
Widget* mParent = nullptr;
|
||||
|
||||
std::vector<Widget*> mChildren;
|
||||
List<Widget*> mDepthOrder;
|
||||
|
||||
// relative to the parent
|
||||
SpringRect mArea;
|
||||
RectF mAreaCache;
|
||||
|
||||
WidgetLayout* mLayout = nullptr;
|
||||
|
||||
BitField mFlags;
|
||||
|
||||
// debug
|
||||
struct {
|
||||
std::string id = "widget base";
|
||||
RGBA col = { 1, 1, 1, 0.3 };
|
||||
std::string triggerReason = "none";
|
||||
Vec2F pLocal;
|
||||
Vec2F pGlobal;
|
||||
} mDebug;
|
||||
};
|
||||
|
||||
struct WidgetManagerInterface : public Widget {
|
||||
virtual void updateWidget(Widget*, const char* reason) = 0;
|
||||
|
||||
virtual void openPopup(Widget*) = 0;
|
||||
virtual void closePopup(Widget*) = 0;
|
||||
|
||||
virtual void lockFocus(Widget*) = 0;
|
||||
virtual void freeFocus(Widget*) = 0;
|
||||
|
||||
static WidgetLayout* defaultLayout(Widget* widget);
|
||||
};
|
||||
}
|
||||
49
Widgets/public/WidgetApplication.hpp
Normal file
49
Widgets/public/WidgetApplication.hpp
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
#pragma once
|
||||
|
||||
#include "GraphicApplication.hpp"
|
||||
#include "RootWidget.hpp"
|
||||
#include "Timing.hpp"
|
||||
|
||||
namespace tp {
|
||||
class WidgetApplication : public Application {
|
||||
public:
|
||||
WidgetApplication() = default;
|
||||
|
||||
void setRoot(Widget* widget) {
|
||||
mRootWidget.setRootWidget(widget);
|
||||
}
|
||||
|
||||
private:
|
||||
void processFrame(EventHandler* eventHandler, halnf deltaTime) override {
|
||||
const auto rec = RectF({ 0, 0 }, mWindow->getSize());
|
||||
|
||||
Timer timer;
|
||||
mRootWidget.processFrame(eventHandler, rec);
|
||||
mProcTime = timer.timePassed();
|
||||
}
|
||||
|
||||
void drawFrame(Canvas* canvas) override {
|
||||
Timer timer;
|
||||
mRootWidget.drawFrame(*canvas);
|
||||
mDrawTime = timer.timePassed();
|
||||
|
||||
if (mRootWidget.isDebug()) {
|
||||
drawDebug();
|
||||
debugUI();
|
||||
}
|
||||
}
|
||||
|
||||
bool forceNewFrame() override {
|
||||
return mRootWidget.needsUpdate();
|
||||
}
|
||||
|
||||
private:
|
||||
void debugUI();
|
||||
|
||||
private:
|
||||
time_ms mProcTime = 0;
|
||||
time_ms mDrawTime = 0;
|
||||
|
||||
RootWidget mRootWidget;
|
||||
};
|
||||
}
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
#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;
|
||||
};
|
||||
}
|
||||
|
|
@ -1,89 +0,0 @@
|
|||
#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;
|
||||
};
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
#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"
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
#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 };
|
||||
};
|
||||
}
|
||||
30
Widgets/public/layouts/BasicLayout.hpp
Normal file
30
Widgets/public/layouts/BasicLayout.hpp
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
#pragma once
|
||||
|
||||
#include "Layout.hpp"
|
||||
|
||||
namespace tp {
|
||||
|
||||
class BasicLayout : public WidgetLayout {
|
||||
friend class DebugManager;
|
||||
|
||||
public:
|
||||
explicit BasicLayout(Widget* widget) : WidgetLayout(widget) {}
|
||||
|
||||
void updateLayout(bool vertical) override;
|
||||
|
||||
void pickRect(bool vertical) override;
|
||||
void clampRect() override;
|
||||
[[nodiscard]] RectF getAvailableChildArea() const override;
|
||||
|
||||
void setLayoutPolicy(LayoutPolicy layout) { mLayoutPolicy = layout; }
|
||||
|
||||
private:
|
||||
void adjustLayout(bool vertical);
|
||||
static halnf changeChildSize(Widget*, halnf diff, bool vertical);
|
||||
|
||||
private:
|
||||
LayoutPolicy mLayoutPolicy = LayoutPolicy::Vertical;
|
||||
halnf mLayoutGap = 5;
|
||||
halnf mLayoutMargin = 9;
|
||||
};
|
||||
}
|
||||
109
Widgets/public/layouts/DockLayout.hpp
Normal file
109
Widgets/public/layouts/DockLayout.hpp
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
#include "Widget.hpp"
|
||||
|
||||
namespace tp {
|
||||
class DockLayout : public WidgetLayout {
|
||||
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 = 300;
|
||||
alni order = -1;
|
||||
|
||||
Side side = { TOP };
|
||||
|
||||
RectF area = {};
|
||||
RectF headerArea = {};
|
||||
RectF previewHandleArea = {};
|
||||
|
||||
ResizeHandle resizeHandle;
|
||||
|
||||
RectF areaBeforeDocking = {};
|
||||
};
|
||||
|
||||
public:
|
||||
explicit DockLayout(Widget* widget);
|
||||
|
||||
void updateResizeHover(const Vec2F& pointer);
|
||||
bool startResize(const Vec2F& pointer);
|
||||
void updateResize(const Vec2F& pointerDelta);
|
||||
void endResize();
|
||||
|
||||
void updatePreviewSide(const Vec2F& pointer);
|
||||
|
||||
public:
|
||||
void pickRect(bool vertical) override {}
|
||||
void clampRect() override {};
|
||||
void updateLayout(bool vertical) override;
|
||||
void adjustChildrenRect();
|
||||
|
||||
public:
|
||||
bool setCenterWidget(Widget* widget);
|
||||
|
||||
bool dockWidget(Widget* widget, Side side);
|
||||
bool undockWidget(Side side);
|
||||
void toggleWidgetVisibility(Side side);
|
||||
|
||||
public:
|
||||
[[nodiscard]] bool isSideVisible(Side side) const;
|
||||
[[nodiscard]] bool sideExists(Side side) const;
|
||||
|
||||
[[nodiscard]] bool isResizing() const { return mResizing; }
|
||||
[[nodiscard]] bool isResizing(Side side) const { return mSideWidgets[side].resizeHandle.active; }
|
||||
[[nodiscard]] bool isResizeHandleHover(Side side) const { return mSideWidgets[side].resizeHandle.hover; }
|
||||
|
||||
Side getSideFromWidget(Widget*);
|
||||
Side getPreviewSide();
|
||||
|
||||
[[nodiscard]] RectF getRectBeforeDocked(Side side) const { return mSideWidgets[side].areaBeforeDocking; }
|
||||
[[nodiscard]] RectF getResizeHandleArea(Side side) const { return mSideWidgets[side].resizeHandle.area; }
|
||||
[[nodiscard]] RectF getHeaderArea(Side side) const { return mSideWidgets[side].headerArea; }
|
||||
[[nodiscard]] RectF getPreviewHandleArea(Side side) const { return mSideWidgets[side].previewHandleArea; }
|
||||
[[nodiscard]] RectF getPreviewArea() const { return mPreviewArea; }
|
||||
|
||||
Widget* getSideWidget(Side side) { return mSideWidgets[side].widget; }
|
||||
|
||||
const std::vector<Widget*>& getDockedWidgets();
|
||||
|
||||
private:
|
||||
ualni getVisibleSidesSize() const;
|
||||
|
||||
void calculateSideAreas();
|
||||
void calculateResizeHandles();
|
||||
void updateChildSideWidgets();
|
||||
void calculateHeaderAreas();
|
||||
|
||||
private:
|
||||
RectF mPreviewArea = {};
|
||||
Side mPreviewSide = NONE;
|
||||
int resizeType[2] = { 0, 0 };
|
||||
|
||||
SideWidgetData mSideWidgets[4];
|
||||
|
||||
std::vector<Widget*> mDockedWidgets;
|
||||
|
||||
private:
|
||||
bool mResizing = false;
|
||||
|
||||
Widget* mCenterWidget = nullptr;
|
||||
RectF mCenterArea {};
|
||||
|
||||
// Parameters
|
||||
const halnf mHandleSplitFactor = 0.3;
|
||||
const halnf mHandleFactor = 0.1;
|
||||
|
||||
halnf mSideSizePadding = 150.f;
|
||||
halnf mPadding = 4;
|
||||
halnf mHeaderSize = 27;
|
||||
};
|
||||
}
|
||||
34
Widgets/public/layouts/FloatingLayout.hpp
Normal file
34
Widgets/public/layouts/FloatingLayout.hpp
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
#pragma once
|
||||
|
||||
#include "BasicLayout.hpp"
|
||||
|
||||
namespace tp {
|
||||
class FloatingLayout : public BasicLayout {
|
||||
public:
|
||||
explicit FloatingLayout(Widget* widget) :
|
||||
BasicLayout(widget) {}
|
||||
|
||||
public:
|
||||
void startAction(const Vec2F& pointer);
|
||||
void updateAction(const Vec2F& pointer);
|
||||
void endAction();
|
||||
|
||||
bool isFloating() const { return mIsFloating; }
|
||||
|
||||
RectF resizeHandleRect();
|
||||
|
||||
public:
|
||||
void pickRect(bool vertical) override;
|
||||
|
||||
private:
|
||||
bool mIsFloating = false;
|
||||
bool mIsResizing = false;
|
||||
|
||||
Vec2F mPointerStart;
|
||||
Vec2F mPointerCurrent;
|
||||
|
||||
// TODO : remove?
|
||||
halnf mHandleSize = 10;
|
||||
halnf mHandlePadding = 2;
|
||||
};
|
||||
}
|
||||
18
Widgets/public/layouts/OverlayLayout.hpp
Normal file
18
Widgets/public/layouts/OverlayLayout.hpp
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
#pragma once
|
||||
|
||||
#include "Layout.hpp"
|
||||
|
||||
namespace tp {
|
||||
|
||||
class OverlayLayout : public WidgetLayout {
|
||||
friend class DebugManager;
|
||||
|
||||
public:
|
||||
explicit OverlayLayout(Widget* widget) : WidgetLayout(widget) {}
|
||||
|
||||
void updateLayout(bool vertical) override;
|
||||
|
||||
void pickRect(bool vertical) override {}
|
||||
void clampRect() override {}
|
||||
};
|
||||
}
|
||||
24
Widgets/public/layouts/ScrollableLayout.hpp
Normal file
24
Widgets/public/layouts/ScrollableLayout.hpp
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
#pragma once
|
||||
|
||||
#include "Layout.hpp"
|
||||
|
||||
namespace tp {
|
||||
class ScrollableBarWidget;
|
||||
|
||||
class ScrollableLayout : public WidgetLayout {
|
||||
public:
|
||||
explicit ScrollableLayout(Widget* widget) :
|
||||
WidgetLayout(widget) {
|
||||
setSizePolicy(SizePolicy::Expanding, SizePolicy::Expanding);
|
||||
}
|
||||
|
||||
void updateLayout(bool vertical) override;
|
||||
[[nodiscard]] RectF getAvailableChildArea() const override;
|
||||
|
||||
private:
|
||||
void updateWidgetRects(const RectF& area, Widget* content, ScrollableBarWidget* scroller) const;
|
||||
|
||||
private:
|
||||
halnf mScrollerSize = 15;
|
||||
};
|
||||
}
|
||||
52
Widgets/public/mangers/DebugManager.hpp
Normal file
52
Widgets/public/mangers/DebugManager.hpp
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
#pragma once
|
||||
|
||||
#include "Widget.hpp"
|
||||
|
||||
#include <set>
|
||||
|
||||
namespace tp {
|
||||
class DebugManager {
|
||||
public:
|
||||
DebugManager() = default;
|
||||
|
||||
bool isRedrawAlways() const { return mDebugRedrawAlways; }
|
||||
bool update(RootWidget* rootWidget, EventHandler& events);
|
||||
void drawDebug(RootWidget* rootWidget, Canvas& canvas);
|
||||
|
||||
void checkProcBreakPoints(Widget* widget) {
|
||||
if (mProcBreakpoints.find(widget) != mProcBreakpoints.end()) {
|
||||
mProcBreakpoints.erase(widget);
|
||||
DEBUG_BREAK(1)
|
||||
}
|
||||
}
|
||||
|
||||
void checkLayoutBreakpoints(Widget* widget) {
|
||||
if (mLayBreakpoints.find(widget) != mLayBreakpoints.end()) {
|
||||
mLayBreakpoints.erase(widget);
|
||||
DEBUG_BREAK(1)
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] bool isDebug() const { return mDebug; }
|
||||
|
||||
private:
|
||||
void recursiveDraw(Canvas& canvas, Widget* active, const Vec2F& pos, int depthOrder);
|
||||
|
||||
static void widgetMenu(Widget*);
|
||||
void drawLayoutOrder();
|
||||
|
||||
private:
|
||||
RootWidget* mRootWidget = nullptr;
|
||||
|
||||
// debug
|
||||
bool mDebug = false;
|
||||
bool mDebugStopProcessing = false;
|
||||
bool mDebugRedrawAlways = false;
|
||||
bool mSlowMotion = false;
|
||||
|
||||
std::set<Widget*> mProcBreakpoints;
|
||||
std::set<Widget*> mLayBreakpoints;
|
||||
};
|
||||
|
||||
extern DebugManager gDebugWidget;
|
||||
}
|
||||
39
Widgets/public/mangers/LayoutManager.hpp
Normal file
39
Widgets/public/mangers/LayoutManager.hpp
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
#pragma once
|
||||
|
||||
#include "Layout.hpp"
|
||||
#include "DebugManager.hpp"
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
namespace tp {
|
||||
class LayoutManager {
|
||||
friend DebugManager;
|
||||
|
||||
struct DepNode {
|
||||
std::vector<Widget*> depends;
|
||||
int references = 0;
|
||||
int depth = 0;
|
||||
};
|
||||
|
||||
public:
|
||||
LayoutManager() = default;
|
||||
|
||||
void adjust(Widget* root);
|
||||
|
||||
private:
|
||||
void findDependencies(Widget* root);
|
||||
void topologicalSort(Widget* root, int depth = 0);
|
||||
void adjustLayouts();
|
||||
|
||||
private:
|
||||
int getLayoutOrder(WidgetLayout* parent, WidgetLayout* child) const;
|
||||
|
||||
private:
|
||||
std::map<Widget*, DepNode> mDepGraph;
|
||||
std::vector<Widget*> mRoots;
|
||||
|
||||
std::vector<std::pair<Widget*, int>> mLayOrder;
|
||||
bool mVertical = false;
|
||||
};
|
||||
}
|
||||
46
Widgets/public/mangers/UpdateManager.hpp
Normal file
46
Widgets/public/mangers/UpdateManager.hpp
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
#pragma once
|
||||
|
||||
#include "Widget.hpp"
|
||||
|
||||
#include <map>
|
||||
|
||||
namespace tp {
|
||||
// FIXME : desperately needs refactor
|
||||
class UpdateManager {
|
||||
friend DebugManager;
|
||||
|
||||
public:
|
||||
UpdateManager() = default;
|
||||
|
||||
void scheduleUpdate(Widget* widget, const char* reason);
|
||||
|
||||
void lockFocus(Widget* widget);
|
||||
void freeFocus(Widget* widget);
|
||||
|
||||
[[nodiscard]] bool isPendingUpdates() const {
|
||||
return !mTriggeredWidgets.empty();
|
||||
}
|
||||
|
||||
void processWidgets(Widget* root, EventHandler& eventHandler);
|
||||
void clean();
|
||||
|
||||
void updateTreeToProcess(Widget* root);
|
||||
void handleFocusChanges(Widget* root, EventHandler& events);
|
||||
|
||||
void findFocusWidget(Widget* iter, Widget** focus, const Vec2F& pointer);
|
||||
static void getWidgetPath(Widget* widget, std::vector<Widget*>& out);
|
||||
void processActiveTree(Widget* iter, EventHandler& events, Vec2F pos);
|
||||
void processFocusItems(EventHandler& events);
|
||||
|
||||
private:
|
||||
static void procWidget(Widget* widget, EventHandler& events, bool withEvents = false);
|
||||
|
||||
private:
|
||||
std::map<Widget*, bool> mTriggeredWidgets;
|
||||
Widget* mInFocusWidget = nullptr;
|
||||
Widget* mFocusLockWidget = nullptr;
|
||||
|
||||
private:
|
||||
int mDebugWidgetsToProcess = 0;
|
||||
};
|
||||
}
|
||||
20
Widgets/public/widgets/AnimationTestWidget.hpp
Normal file
20
Widgets/public/widgets/AnimationTestWidget.hpp
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
#pragma once
|
||||
|
||||
#include "Widget.hpp"
|
||||
|
||||
namespace tp {
|
||||
class AnimationTestWidget : public Widget {
|
||||
|
||||
public:
|
||||
AnimationTestWidget() = default;
|
||||
~AnimationTestWidget() override = default;
|
||||
|
||||
void draw(Canvas& canvas) override;
|
||||
void process(const EventHandler& events) override;
|
||||
|
||||
[[nodiscard]] bool needsNextFrame() const override;
|
||||
|
||||
private:
|
||||
mutable SpringRect mTestSpring;
|
||||
};
|
||||
}
|
||||
42
Widgets/public/widgets/DockWidget.hpp
Normal file
42
Widgets/public/widgets/DockWidget.hpp
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
#include "DockLayout.hpp"
|
||||
|
||||
namespace tp {
|
||||
class DockWidget : public Widget {
|
||||
public:
|
||||
DockWidget();
|
||||
|
||||
public:
|
||||
void process(const EventHandler& events) override;
|
||||
void draw(Canvas& canvas) override;
|
||||
void drawOverlay(Canvas& canvas) override;
|
||||
|
||||
[[nodiscard]] bool propagateEventsToChildren() const override;
|
||||
[[nodiscard]] bool needsNextFrame() const override;
|
||||
[[nodiscard]]bool processesEvents() const override { return true; }
|
||||
|
||||
void drawSide(DockLayout::Side side, Canvas& canvas);
|
||||
|
||||
public:
|
||||
void setCenterWidget(Widget* widget);
|
||||
|
||||
void dockWidget(Widget* widget, DockLayout::Side side);
|
||||
void undockWidget(DockLayout::Side side, bool restoreArea = true);
|
||||
void toggleWidgetVisibility(DockLayout::Side side);
|
||||
|
||||
private:
|
||||
DockLayout* layout() { return dynamic_cast<DockLayout*>(mLayout); }
|
||||
const DockLayout* layout() const { return dynamic_cast<const DockLayout*>(mLayout); }
|
||||
|
||||
private:
|
||||
Widget* mPreviewWidget = nullptr;
|
||||
|
||||
private:
|
||||
halnf mRounding = 10;
|
||||
halnf mPadding = 4;
|
||||
|
||||
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, 0.2);
|
||||
};
|
||||
}
|
||||
62
Widgets/public/widgets/FloatingWidget.hpp
Normal file
62
Widgets/public/widgets/FloatingWidget.hpp
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
#pragma once
|
||||
|
||||
#include "SimpleWidgets.hpp"
|
||||
#include "FloatingLayout.hpp"
|
||||
#include "ScrollableWidget.hpp"
|
||||
|
||||
namespace tp {
|
||||
class FloatingWidget : public Widget {
|
||||
public:
|
||||
FloatingWidget() : Widget() {
|
||||
setDebug("float", { 0.0, 0.9, 0.1, 1 });
|
||||
setLayout(new FloatingLayout(this));
|
||||
}
|
||||
|
||||
void process(const EventHandler& events) override;
|
||||
|
||||
void draw(Canvas& canvas) override;
|
||||
|
||||
|
||||
[[nodiscard]] bool needsNextFrame() const override;
|
||||
|
||||
[[nodiscard]] bool propagateEventsToChildren() const override;
|
||||
[[nodiscard]] bool processesEvents() const override;
|
||||
|
||||
[[nodiscard]] bool isFloating() const;
|
||||
|
||||
private:
|
||||
FloatingLayout* layout();
|
||||
[[nodiscard]] const FloatingLayout* layout() const;
|
||||
};
|
||||
|
||||
class FloatingMenu : public FloatingWidget {
|
||||
public:
|
||||
FloatingMenu();
|
||||
|
||||
public:
|
||||
void addToMenu(Widget* widget) {
|
||||
widget->setSizePolicy(SizePolicy::Expanding, SizePolicy::Minimal);
|
||||
mContentWidget.addChild(widget);
|
||||
}
|
||||
|
||||
const List<Widget*>& getContent() {
|
||||
return mContentWidget.getChildren();
|
||||
}
|
||||
|
||||
void clearChildren() {
|
||||
mContentWidget.clear();
|
||||
}
|
||||
|
||||
void setText(const std::string& text);
|
||||
|
||||
private:
|
||||
// VerticalLayout mMenuLayout;
|
||||
Widget mBodyLayout;
|
||||
Widget mContentWidget;
|
||||
ScrollableBarWidget mScrollBar;
|
||||
|
||||
LabelWidget mHeader;
|
||||
|
||||
// ButtonWidget mTestButton;
|
||||
};
|
||||
}
|
||||
57
Widgets/public/widgets/ScrollableWidget.hpp
Normal file
57
Widgets/public/widgets/ScrollableWidget.hpp
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
#pragma once
|
||||
|
||||
#include "Widget.hpp"
|
||||
|
||||
namespace tp {
|
||||
// just the scrolling bar
|
||||
class ScrollableBarWidget : public Widget {
|
||||
public:
|
||||
ScrollableBarWidget() = default;
|
||||
|
||||
[[nodiscard]] bool processesEvents() const override { return true; }
|
||||
[[nodiscard]] bool propagateEventsToChildren() const override { return false; }
|
||||
[[nodiscard]] bool needsNextFrame() const override { return mScrolling; }
|
||||
|
||||
void process(const EventHandler& events) override;
|
||||
void draw(Canvas& canvas) override;
|
||||
|
||||
void updateSizeFactor(halnf factor); // holder-size divided by content-size
|
||||
|
||||
[[nodiscard]] bool getDirection() const { return mVertical; }
|
||||
[[nodiscard]] halnf getPosFactor() const { return mPosFactor - mSizeFactor / 2; }
|
||||
|
||||
private:
|
||||
[[nodiscard]] const RectF& getHandleRect() const;
|
||||
void updateHandleRect();
|
||||
void jumpTo(halnf);
|
||||
void moveBy(halnf);
|
||||
void clamp();
|
||||
|
||||
private: // state
|
||||
bool mVertical = true;
|
||||
bool mScrolling = false;
|
||||
bool mHandleHovered = false;
|
||||
|
||||
Vec2F mStartPos = {};
|
||||
|
||||
halnf mPosFactor = 0.2f; // (center-of-holder - content-start) / content-size
|
||||
halnf mSizeFactor = 0.3f;
|
||||
|
||||
private: // TODO : make params static
|
||||
halnf mRounding = 5;
|
||||
halnf mHandlePadding = 0;
|
||||
|
||||
RGBA mBGColor = { 0.1, 0.1, 0.1, 0.0 };
|
||||
RGBA mHandleColor = { 0.2, 0.2, 0.2, 1 };
|
||||
RGBA mHandleHoverColor = { 0.3, 0.3, 0.3, 1 };
|
||||
RGBA mHandleSlideColor = { 0.5, 0.5, 0.5, 1 };
|
||||
|
||||
private: // cache
|
||||
RectF mHandleRect;
|
||||
};
|
||||
|
||||
class ScrollableWidget : public Widget {
|
||||
public:
|
||||
ScrollableWidget();
|
||||
};
|
||||
}
|
||||
94
Widgets/public/widgets/SimpleWidgets.hpp
Normal file
94
Widgets/public/widgets/SimpleWidgets.hpp
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
#pragma once
|
||||
|
||||
#include "Widget.hpp"
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace tp {
|
||||
class LabelWidget : public Widget {
|
||||
public:
|
||||
LabelWidget() : Widget() {
|
||||
setDebug("label", { 0.1, 0.1, 0.1, 0.1 });
|
||||
}
|
||||
|
||||
void setText(const std::string& text);
|
||||
|
||||
[[nodiscard]] const std::string& getText() const;
|
||||
|
||||
void draw(Canvas& canvas) override;
|
||||
|
||||
[[nodiscard]] bool processesEvents() const override { return false; }
|
||||
|
||||
private:
|
||||
std::string mText = "Text";
|
||||
|
||||
halnf mPadding = 5;
|
||||
RGBA mColor = 1.f;
|
||||
halnf mSize = 17.f;
|
||||
};
|
||||
|
||||
class ButtonWidget : public LabelWidget {
|
||||
public:
|
||||
ButtonWidget();
|
||||
|
||||
void setAction(const std::function<void()>& action);
|
||||
|
||||
void process(const EventHandler& eventHandler) override;
|
||||
void draw(Canvas& canvas) override;
|
||||
|
||||
void mouseEnter() override;
|
||||
void mouseLeave() override;
|
||||
|
||||
[[nodiscard]] bool processesEvents() const override { return true; }
|
||||
[[nodiscard]] bool needsNextFrame() const override;
|
||||
|
||||
void endAnimations() override;
|
||||
void updateAnimations() override;
|
||||
|
||||
void setColor(const RGBA& in);
|
||||
|
||||
private:
|
||||
std::function<void()> mAction;
|
||||
|
||||
SpringRect mColorAnimated;
|
||||
|
||||
halnf mRounding = 5;
|
||||
RGBA mColorHovered = { 0.0f, 0.4f, 0.4f, 1.f };
|
||||
RGBA mColor = { 0.13f, 0.13f, 0.13f, 1.f };
|
||||
};
|
||||
|
||||
class SliderWidget : public Widget {
|
||||
enum State {
|
||||
IDLE,
|
||||
HOVER,
|
||||
SLIDING,
|
||||
};
|
||||
|
||||
public:
|
||||
SliderWidget() = default;
|
||||
|
||||
void process(const EventHandler& eventHandler) override;
|
||||
void draw(Canvas& canvas) override;
|
||||
|
||||
[[nodiscard]] bool processesEvents() const override { return true; }
|
||||
[[nodiscard]] bool needsNextFrame() const override { return mState != SLIDING; }
|
||||
|
||||
[[nodiscard]] halnf val() const { return mFactor; }
|
||||
|
||||
private:
|
||||
[[nodiscard]] RectF getHandleArea() const;
|
||||
|
||||
private:
|
||||
halnf mFactor = 0;
|
||||
State mState = IDLE;
|
||||
|
||||
private:
|
||||
halnf mHandleSize = 20;
|
||||
halnf mRounding = 5;
|
||||
|
||||
RGBA mColorActive = { 0.5f, 0.5f, 0.5f, 1.f };
|
||||
RGBA mColorHovered = { 0.3f, 0.3f, 0.3f, 1.f };
|
||||
RGBA mColorIdle = { 0.2f, 0.2f, 0.2f, 1.f };
|
||||
RGBA mColorBG = { 0.08f, 0.08f, 0.08f, 1.0f };
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue