New DockspaceWidget Stable
This commit is contained in:
parent
7158be5890
commit
9e7704a446
8 changed files with 339 additions and 7 deletions
|
|
@ -207,6 +207,7 @@ namespace tp {
|
||||||
void insert(TypeArg data, Index idx) { insert(newNode(data), idx); }
|
void insert(TypeArg data, Index idx) { insert(newNode(data), idx); }
|
||||||
|
|
||||||
void removeNode(Node* node) {
|
void removeNode(Node* node) {
|
||||||
|
if (!node) return;
|
||||||
detach(node);
|
detach(node);
|
||||||
deleteNode(node);
|
deleteNode(node);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -119,8 +119,10 @@ namespace tp {
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// pos
|
||||||
Vec2<Type> p1() { return pos; }
|
Vec2<Type> p1() { return pos; }
|
||||||
|
|
||||||
|
// pos + size
|
||||||
Vec2<Type> p3() { return pos + size; }
|
Vec2<Type> p3() { return pos + size; }
|
||||||
|
|
||||||
Vec2<Type> p2() { return { pos.x, pos.y + size.y }; }
|
Vec2<Type> p2() { return { pos.x, pos.y + size.y }; }
|
||||||
|
|
@ -224,6 +226,25 @@ namespace tp {
|
||||||
|
|
||||||
Vec2<Type> center() { return pos + size / 2.f; }
|
Vec2<Type> center() { return pos + size / 2.f; }
|
||||||
|
|
||||||
|
// splits by Factor Horizontally returning Left rect
|
||||||
|
Rect<Type> splitByFactorHL(halnf factor) {
|
||||||
|
return { x, y, size.x * factor, size.y };
|
||||||
|
}
|
||||||
|
|
||||||
|
Rect<Type> splitByFactorHR(halnf factor) {
|
||||||
|
const auto abs = size.x * factor;
|
||||||
|
return { x + abs, y, size.x - abs, size.y };
|
||||||
|
}
|
||||||
|
|
||||||
|
Rect<Type> splitByFactorVT(halnf factor) {
|
||||||
|
return { x, y, size.x, size.y * factor };
|
||||||
|
}
|
||||||
|
|
||||||
|
Rect<Type> splitByFactorVB(halnf factor) {
|
||||||
|
const auto abs = size.y * factor;
|
||||||
|
return { x, y + abs, size.x, size.y - abs };
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
union {
|
union {
|
||||||
Vec2<Type> v1;
|
Vec2<Type> v1;
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,27 @@ class SimpleGUI : public Application {
|
||||||
public:
|
public:
|
||||||
SimpleGUI() {
|
SimpleGUI() {
|
||||||
|
|
||||||
|
// debug
|
||||||
|
|
||||||
|
mGui.addSideWidget(&mButtons[0], DockSpaceWidget::BOTTOM);
|
||||||
|
mGui.addSideWidget(&mButtons[1], DockSpaceWidget::RIGHT);
|
||||||
|
|
||||||
|
mGui.removeSideWidget(DockSpaceWidget::BOTTOM);
|
||||||
|
|
||||||
|
mGui.addSideWidget(&mButtons[0], DockSpaceWidget::TOP);
|
||||||
|
mGui.addSideWidget(&mButtons[2], DockSpaceWidget::LEFT);
|
||||||
|
|
||||||
|
mGui.removeSideWidget(DockSpaceWidget::TOP);
|
||||||
|
|
||||||
|
mGui.addSideWidget(&mButtons[0], DockSpaceWidget::BOTTOM);
|
||||||
|
|
||||||
|
mGui.addSideWidget(&mButtons[3], DockSpaceWidget::TOP);
|
||||||
|
mGui.setCenterWidget(&mButtons[4]);
|
||||||
|
|
||||||
|
mButtons[4].mCallback = [&]() { mGui.toggleHiddenState(DockSpaceWidget::BOTTOM); };
|
||||||
|
mButtons[0].mCallback = [&]() { mGui.toggleHiddenState(DockSpaceWidget::TOP); };
|
||||||
|
mButtons[2].mCallback = [&]() { mGui.toggleHiddenState(DockSpaceWidget::RIGHT); };
|
||||||
|
mButtons[3].mCallback = [&]() { mGui.toggleHiddenState(DockSpaceWidget::LEFT); };
|
||||||
}
|
}
|
||||||
|
|
||||||
void processFrame(EventHandler* eventHandler) override {
|
void processFrame(EventHandler* eventHandler) override {
|
||||||
|
|
@ -28,7 +49,9 @@ public:
|
||||||
private:
|
private:
|
||||||
WidgetManager mWidgetManager;
|
WidgetManager mWidgetManager;
|
||||||
|
|
||||||
SimpleWidget2 mGui;
|
DockSpaceWidget mGui;
|
||||||
|
|
||||||
|
ButtonWidget mButtons[5];
|
||||||
};
|
};
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,13 @@ ButtonWidget::ButtonWidget(const std::string& label, const tp::RectF& aArea) {
|
||||||
|
|
||||||
bool ButtonWidget::isFired() { return this->isReleased(); }
|
bool ButtonWidget::isFired() { return this->isReleased(); }
|
||||||
|
|
||||||
void ButtonWidget::eventProcess(const Events&) { mLabel.setArea(this->mArea); }
|
void ButtonWidget::eventProcess(const Events&) {
|
||||||
|
mLabel.setArea(this->mArea);
|
||||||
|
|
||||||
|
if (isFired()) {
|
||||||
|
mCallback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ButtonWidget::eventDraw(Canvas& canvas) {
|
void ButtonWidget::eventDraw(Canvas& canvas) {
|
||||||
if (this->isHolding()) {
|
if (this->isHolding()) {
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,90 @@
|
||||||
|
|
||||||
using namespace tp;
|
using namespace tp;
|
||||||
|
|
||||||
void DockSpaceWidget::eventProcess(const tp::Events& events) {
|
DockSpaceWidget::DockSpaceWidget() = default;
|
||||||
|
|
||||||
|
void DockSpaceWidget::addSideWidget(Widget* widget, Side side) {
|
||||||
|
if (mSideWidgets[side]) return;
|
||||||
|
mSideWidgets[side] = widget;
|
||||||
|
for (auto& order : mSideSplitOrder) {
|
||||||
|
if (order == -1) {
|
||||||
|
order = side;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mChildWidgets.pushBack(widget);
|
||||||
|
widget->mIsDocked = true;
|
||||||
|
mSideHidden[side] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockSpaceWidget::removeSideWidget(Side side) {
|
||||||
|
if (!mSideWidgets[side]) return;
|
||||||
|
|
||||||
|
bool removed = false;
|
||||||
|
for (ualni i = 0; i < 3; i++) {
|
||||||
|
if (mSideSplitOrder[i] == side) {
|
||||||
|
removed = true;
|
||||||
|
}
|
||||||
|
if (removed) {
|
||||||
|
mSideSplitOrder[i] = mSideSplitOrder[i + 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mSideSplitOrder[3] = -1;
|
||||||
|
|
||||||
|
auto widget = mSideWidgets[side];
|
||||||
|
mSideWidgets[side] = nullptr;
|
||||||
|
widget->mIsDocked = false;
|
||||||
|
|
||||||
|
mChildWidgets.removeNode(mChildWidgets.find(widget));
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockSpaceWidget::setCenterWidget(Widget* widget) {
|
||||||
|
mChildWidgets.removeNode(mChildWidgets.find(mCenterWidget));
|
||||||
|
mCenterWidget = widget;
|
||||||
|
mChildWidgets.pushBack(mCenterWidget);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockSpaceWidget::toggleHiddenState(DockSpaceWidget::Side side) {
|
||||||
|
if (!isSideExists(side)) return;
|
||||||
|
mSideHidden[side] = !mSideHidden[side];
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockSpaceWidget::eventProcess(const tp::Events& events) {
|
||||||
|
updateActiveWindow(events);
|
||||||
|
|
||||||
|
calculateSideAreas();
|
||||||
|
calculateResizeHandles();
|
||||||
|
// calculateHeaderAreas();
|
||||||
|
handleResizeEvents(events);
|
||||||
|
|
||||||
|
updateChildSideWidgets();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockSpaceWidget::eventDraw(Canvas& canvas) {
|
||||||
|
canvas.rect(this->mArea, mBackgroundColor, 0);
|
||||||
|
|
||||||
|
for (auto sizeHandle : mResizeHandles) {
|
||||||
|
if (sizeHandle->active) {
|
||||||
|
canvas.rect(sizeHandle->area.shrink(mPadding / 1.5f), mResizeHandleColorActive, 0);
|
||||||
|
} else if (sizeHandle->hover) {
|
||||||
|
canvas.rect(sizeHandle->area.shrink(mPadding / 1.5f), mResizeHandleColorHovered, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& header : mSideHeaderAreas) {
|
||||||
|
canvas.rect(header, mResizeHandleColorActive, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockSpaceWidget::updateActiveWindow(const tp::Events& events) {
|
||||||
if (this->mChildWidgets.size() > 1 && events.isPressed(InputID::MOUSE1)) {
|
if (this->mChildWidgets.size() > 1 && events.isPressed(InputID::MOUSE1)) {
|
||||||
|
|
||||||
Widget* activeChild = nullptr;
|
Widget* activeChild = nullptr;
|
||||||
|
|
||||||
for (auto childNode = this->mChildWidgets.firstNode(); childNode; childNode = childNode->next) {
|
for (auto childNode = this->mChildWidgets.firstNode(); childNode; childNode = childNode->next) {
|
||||||
auto child = childNode->data;
|
auto child = childNode->data;
|
||||||
|
|
||||||
|
if (child->mIsDocked) continue;
|
||||||
if (child->mArea.isInside(events.getPointer())) {
|
if (child->mArea.isInside(events.getPointer())) {
|
||||||
|
|
||||||
mChildWidgets.detach(childNode);
|
mChildWidgets.detach(childNode);
|
||||||
|
|
@ -23,12 +99,151 @@ void DockSpaceWidget::eventProcess(const tp::Events& events) {
|
||||||
|
|
||||||
if (activeChild) {
|
if (activeChild) {
|
||||||
for (auto child : this->mChildWidgets) {
|
for (auto child : this->mChildWidgets) {
|
||||||
if (activeChild != child.data()) child->mHandlesEvents = false;
|
if (activeChild != child.data() && !child->mIsDocked) child->mHandlesEvents = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DockSpaceWidget::eventDraw(Canvas& canvas) {
|
void DockSpaceWidget::calculateSideAreas() {
|
||||||
canvas.rect(this->mArea, RGBA(0, 0, 0, 1), 0);
|
auto startArea = this->mArea;
|
||||||
|
for (ualni i = 0; i < 4; i++) {
|
||||||
|
const auto side = mSideSplitOrder[i];
|
||||||
|
|
||||||
|
if (side == -1) break;
|
||||||
|
if (mSideHidden[side]) continue;
|
||||||
|
|
||||||
|
bool vertical = side == TOP || side == BOTTOM;
|
||||||
|
bool opposite = side == BOTTOM || side == RIGHT;
|
||||||
|
|
||||||
|
const auto maxAbsoluteSize = startArea.size[vertical];
|
||||||
|
const auto paddingFactor = mSideSizePadding / maxAbsoluteSize;
|
||||||
|
|
||||||
|
mSideAbsoluteSize[side] =
|
||||||
|
clamp(mSideAbsoluteSize[side], maxAbsoluteSize * paddingFactor, maxAbsoluteSize * (1.f - paddingFactor));
|
||||||
|
|
||||||
|
auto factor = mSideAbsoluteSize[side] / startArea.size[vertical];
|
||||||
|
if (opposite) factor = factor * -1 + 1;
|
||||||
|
|
||||||
|
if (!vertical) {
|
||||||
|
const auto first = startArea.splitByFactorHL(factor);
|
||||||
|
const auto second = startArea.splitByFactorHR(factor);
|
||||||
|
mSideAreas[side] = side == LEFT ? first : second;
|
||||||
|
startArea = side == LEFT ? second : first;
|
||||||
|
} else {
|
||||||
|
const auto first = startArea.splitByFactorVT(factor);
|
||||||
|
const auto second = startArea.splitByFactorVB(factor);
|
||||||
|
mSideAreas[side] = side == TOP ? first : second;
|
||||||
|
startArea = side == TOP ? second : first;
|
||||||
|
}
|
||||||
|
|
||||||
|
mSideAreas[side] = mSideAreas[side].shrink(mPadding);
|
||||||
|
}
|
||||||
|
|
||||||
|
mCenterArea = startArea.shrink(mPadding);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockSpaceWidget::calculateResizeHandles() {
|
||||||
|
mResizeHandles.clear();
|
||||||
|
RectF rec;
|
||||||
|
|
||||||
|
if (isSideVisible(LEFT)) {
|
||||||
|
rec = { mSideAreas[LEFT].p4(), { mPadding * 2, mSideAreas[LEFT].size.y } };
|
||||||
|
mResizeHandles.append({ rec, LEFT, 0, mCenterArea.p3().x });
|
||||||
|
}
|
||||||
|
if (isSideVisible(RIGHT)) {
|
||||||
|
rec = { mSideAreas[RIGHT].p1(), { mPadding * 2, mSideAreas[RIGHT].size.y } };
|
||||||
|
rec.x -= mPadding * 2;
|
||||||
|
mResizeHandles.append({ rec, RIGHT, mCenterArea.p1().x, rec.p3().x });
|
||||||
|
}
|
||||||
|
if (isSideVisible(TOP)) {
|
||||||
|
rec = { mSideAreas[TOP].p2(), { mSideAreas[TOP].size.x, mPadding * 2 } };
|
||||||
|
mResizeHandles.append({ rec, TOP, 0, mCenterArea.p2().y });
|
||||||
|
}
|
||||||
|
if (isSideVisible(BOTTOM)) {
|
||||||
|
rec = { mSideAreas[BOTTOM].p1(), { mSideAreas[BOTTOM].size.x, mPadding * 2 } };
|
||||||
|
rec.y -= mPadding * 2;
|
||||||
|
mResizeHandles.append({ rec, BOTTOM, mCenterArea.p1().y, rec.p2().y });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockSpaceWidget::handleResizeEvents(const Events& events) {
|
||||||
|
for (auto resizeHandle : mResizeHandles) {
|
||||||
|
resizeHandle->hover = false;
|
||||||
|
if (resizeHandle->area.isInside(events.getPointerPrev())) {
|
||||||
|
resizeHandle->hover = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (events.isDown(InputID::MOUSE1)) {
|
||||||
|
for (auto resizeHandle : mResizeHandles) {
|
||||||
|
resizeHandle->active = false;
|
||||||
|
if (resizeHandle->area.isInside(events.getPointerPrev())) {
|
||||||
|
auto factorDeltaVec = events.getPointerDelta();
|
||||||
|
halnf factorDelta = factorDeltaVec[(resizeHandle->side == TOP || resizeHandle->side == BOTTOM)];
|
||||||
|
if (resizeHandle->side == BOTTOM || resizeHandle->side == RIGHT) factorDelta *= -1;
|
||||||
|
mSideAbsoluteSize[resizeHandle->side] += factorDelta;
|
||||||
|
resizeHandle->active = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockSpaceWidget::updateChildSideWidgets() {
|
||||||
|
// Update Child Widgets
|
||||||
|
{
|
||||||
|
for (ualni i = 0; i < 4; i++) {
|
||||||
|
if (!isSideExists(Side(i))) continue;
|
||||||
|
|
||||||
|
if (!isSideVisible(Side(i))) {
|
||||||
|
mSideWidgets[i]->mEnable = false;
|
||||||
|
} else {
|
||||||
|
mSideWidgets[i]->setArea(mSideAreas[i]);
|
||||||
|
mSideWidgets[i]->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 DockSpaceWidget::calculateHeaderAreas() {
|
||||||
|
for (ualni i = 0; i < 4; i++) {
|
||||||
|
if (!isSideVisible(Side(i))) continue;
|
||||||
|
const auto factor = mHeaderSize / mSideAreas[i].size.y;
|
||||||
|
mSideHeaderAreas[i] = mSideAreas[i].splitByFactorVT(factor);
|
||||||
|
mSideAreas[i] = mSideAreas[i].splitByFactorVB(factor);
|
||||||
|
|
||||||
|
mSideHeaderAreas[i].size.y -= mPadding;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DockSpaceWidget::isSideVisible(DockSpaceWidget::Side side) { return mSideWidgets[side] && !mSideHidden[side]; }
|
||||||
|
|
||||||
|
bool DockSpaceWidget::isSideExists(DockSpaceWidget::Side side) { return mSideWidgets[side]; }
|
||||||
|
|
||||||
|
|
||||||
|
ualni DockSpaceWidget::getVisibleSidesSize() {
|
||||||
|
ualni out = 0;
|
||||||
|
for (ualni i = 0; i < 4; i++) {
|
||||||
|
if (isSideVisible(Side(i))) out++;
|
||||||
|
}
|
||||||
|
return out;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
#include "LabelWidget.hpp"
|
#include "LabelWidget.hpp"
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
namespace tp {
|
namespace tp {
|
||||||
|
|
||||||
class ButtonWidget : public Widget {
|
class ButtonWidget : public Widget {
|
||||||
|
|
@ -23,5 +25,7 @@ namespace tp {
|
||||||
RGBA hoveredColor;
|
RGBA hoveredColor;
|
||||||
RGBA accentColor;
|
RGBA accentColor;
|
||||||
halnf rounding = 0;
|
halnf rounding = 0;
|
||||||
|
|
||||||
|
std::function<void()> mCallback = [](){};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -3,9 +3,67 @@
|
||||||
namespace tp {
|
namespace tp {
|
||||||
class DockSpaceWidget : public Widget {
|
class DockSpaceWidget : public Widget {
|
||||||
public:
|
public:
|
||||||
DockSpaceWidget() = default;
|
enum Side { LEFT, TOP, RIGHT, BOTTOM };
|
||||||
|
|
||||||
|
public:
|
||||||
|
DockSpaceWidget();
|
||||||
|
|
||||||
void eventProcess(const Events& events) override;
|
void eventProcess(const Events& events) override;
|
||||||
void eventDraw(Canvas& canvas) override;
|
void eventDraw(Canvas& canvas) override;
|
||||||
|
|
||||||
|
void addSideWidget(Widget* widget, Side side);
|
||||||
|
void removeSideWidget(Side side);
|
||||||
|
|
||||||
|
void toggleHiddenState(DockSpaceWidget::Side side);
|
||||||
|
|
||||||
|
void setCenterWidget(Widget* widget);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void updateActiveWindow(const tp::Events& events);
|
||||||
|
void calculateSideAreas();
|
||||||
|
void calculateResizeHandles();
|
||||||
|
void handleResizeEvents(const Events& events);
|
||||||
|
void updateChildSideWidgets();
|
||||||
|
|
||||||
|
void calculateHeaderAreas();
|
||||||
|
|
||||||
|
|
||||||
|
bool isSideVisible(Side side);
|
||||||
|
bool isSideExists(Side side);
|
||||||
|
ualni getVisibleSidesSize();
|
||||||
|
|
||||||
|
private:
|
||||||
|
// todo : make better grouping into structs
|
||||||
|
|
||||||
|
Widget* mSideWidgets[4] { nullptr, nullptr, nullptr, nullptr };
|
||||||
|
Widget* mCenterWidget = nullptr;
|
||||||
|
|
||||||
|
alni mSideSplitOrder[4] = { -1, -1, -1, -1 };
|
||||||
|
halnf mSideAbsoluteSize[4] { 200, 100, 200, 100 };
|
||||||
|
bool mSideHidden[4] { true, false, false, false };
|
||||||
|
halnf mSideSizePadding = 50.f;
|
||||||
|
|
||||||
|
// Geometry cache
|
||||||
|
struct ResizeHandle {
|
||||||
|
RectF area{ 0, 0, 0, 0 };
|
||||||
|
Side side{ TOP };
|
||||||
|
halnf start{ 0 };
|
||||||
|
halnf end{ 0 };
|
||||||
|
bool active = false;
|
||||||
|
bool hover = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
Buffer<ResizeHandle> mResizeHandles;
|
||||||
|
RectF mSideAreas[4];
|
||||||
|
RectF mSideHeaderAreas[4];
|
||||||
|
RectF mCenterArea {};
|
||||||
|
|
||||||
|
// View Parameters
|
||||||
|
halnf mPadding = 4;
|
||||||
|
halnf mHeaderSize = 27;
|
||||||
|
|
||||||
|
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);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -63,5 +63,9 @@ namespace tp {
|
||||||
bool mHolding = false;
|
bool mHolding = false;
|
||||||
bool mPressed = false;
|
bool mPressed = false;
|
||||||
bool mReleased = false;
|
bool mReleased = false;
|
||||||
|
|
||||||
|
|
||||||
|
// docking
|
||||||
|
bool mIsDocked = false;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue