Fix sizing issues and others
This commit is contained in:
parent
0b73f8037b
commit
a36386cc20
12 changed files with 325 additions and 142 deletions
|
|
@ -41,6 +41,8 @@ DebugGUI::DebugGUI(Window* window) {
|
||||||
// appearance
|
// appearance
|
||||||
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
|
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
auto& colors = ImGui::GetStyle().Colors;
|
auto& colors = ImGui::GetStyle().Colors;
|
||||||
colors[ImGuiCol_WindowBg] = ImVec4{ 0.1f, 0.105f, 0.11f, 1.0f };
|
colors[ImGuiCol_WindowBg] = ImVec4{ 0.1f, 0.105f, 0.11f, 1.0f };
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
using namespace tp;
|
using namespace tp;
|
||||||
|
|
||||||
Application::Application() {
|
Application::Application() {
|
||||||
mWindow = Window::createWindow();
|
mWindow = Window::createWindow({1500, 900});
|
||||||
mGraphics = new Graphics(mWindow);
|
mGraphics = new Graphics(mWindow);
|
||||||
|
|
||||||
mDrawTimer.setDuration(1000.f / mDrawPerSecond);
|
mDrawTimer.setDuration(1000.f / mDrawPerSecond);
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,20 @@
|
||||||
|
|
||||||
using namespace tp;
|
using namespace tp;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* adjust child widgets in the layout view
|
||||||
|
* make scrollable area
|
||||||
|
* change Widget::clampMinMax function to include child enclosure
|
||||||
|
* refactor all sizing in Widget -> move to separate files
|
||||||
|
* refactor root widget -> reorganize code
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
class WidgetApplication : public Application {
|
class WidgetApplication : public Application {
|
||||||
public:
|
public:
|
||||||
WidgetApplication() {
|
WidgetApplication() {
|
||||||
setup2();
|
setup1();
|
||||||
}
|
}
|
||||||
|
|
||||||
void setup1() {
|
void setup1() {
|
||||||
|
|
@ -52,6 +62,21 @@ public:
|
||||||
// mLayoutWidget.addChild(&mLabel);
|
// mLayoutWidget.addChild(&mLabel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setup3() {
|
||||||
|
mRootWidget.setRootWidget(&mDesktopLayout);
|
||||||
|
mDesktopLayout.addChild(&mFloatingWidget);
|
||||||
|
|
||||||
|
mFloatingWidget.addChild(&mButton);
|
||||||
|
mFloatingWidget.addChild(&mButton2);
|
||||||
|
mFloatingWidget.addChild(&mButton3);
|
||||||
|
|
||||||
|
mButton.setSizePolicy(Widget::SizePolicy::Minimal, Widget::SizePolicy::Expanding);
|
||||||
|
mButton2.setSizePolicy(Widget::SizePolicy::Minimal, Widget::SizePolicy::Expanding);
|
||||||
|
mButton3.setSizePolicy(Widget::SizePolicy::Expanding, Widget::SizePolicy::Expanding);
|
||||||
|
|
||||||
|
RootWidget::setWidgetArea(mFloatingWidget, { 100, 100, 400, 400 });
|
||||||
|
}
|
||||||
|
|
||||||
void setup2() {
|
void setup2() {
|
||||||
mRootWidget.setRootWidget(&mDesktopLayout);
|
mRootWidget.setRootWidget(&mDesktopLayout);
|
||||||
mDesktopLayout.addChild(&mWidget);
|
mDesktopLayout.addChild(&mWidget);
|
||||||
|
|
@ -60,17 +85,17 @@ public:
|
||||||
mWidget.addChild(&mButton2);
|
mWidget.addChild(&mButton2);
|
||||||
mWidget.addChild(&mButton3);
|
mWidget.addChild(&mButton3);
|
||||||
|
|
||||||
mButton.setSizePolicy(Widget::SizePolicy::Expand, Widget::SizePolicy::Contract);
|
mButton.setSizePolicy(Widget::SizePolicy::Expanding, Widget::SizePolicy::Minimal);
|
||||||
mButton2.setSizePolicy(Widget::SizePolicy::Expand, Widget::SizePolicy::Contract);
|
mButton2.setSizePolicy(Widget::SizePolicy::Expanding, Widget::SizePolicy::Minimal);
|
||||||
mButton3.setSizePolicy(Widget::SizePolicy::Expand, Widget::SizePolicy::Contract);
|
mButton3.setSizePolicy(Widget::SizePolicy::Expanding, Widget::SizePolicy::Minimal);
|
||||||
|
|
||||||
mWidget.setSizePolicy(Widget::SizePolicy::Contract, Widget::SizePolicy::Contract);
|
mWidget.setSizePolicy(Widget::SizePolicy::Minimal, Widget::SizePolicy::Minimal);
|
||||||
mWidget.setLayoutPolicy(Widget::LayoutPolicy::Horizontally);
|
mWidget.setLayoutPolicy(Widget::LayoutPolicy::Horizontally);
|
||||||
|
|
||||||
mButton.setAction([this]() { mButton2.setMinSize(mButton2.getMinSize() + 10); });
|
mButton.setAction([this]() { mButton2.setMinSize(mButton2.getMinSize() + 10); });
|
||||||
mButton2.setAction([this]() { mButton.setMinSize(mButton.getMinSize() + 10); } );
|
mButton2.setAction([this]() { mButton.setMinSize(mButton.getMinSize() + 10); } );
|
||||||
|
|
||||||
RootWidget::setWidgetArea(mWidget, { 300, 100, 150, 200 });
|
RootWidget::setWidgetArea(mWidget, { 400, 100, 150, 200 });
|
||||||
}
|
}
|
||||||
|
|
||||||
void processFrame(EventHandler* eventHandler, halnf deltaTime) override {
|
void processFrame(EventHandler* eventHandler, halnf deltaTime) override {
|
||||||
|
|
@ -96,6 +121,7 @@ private:
|
||||||
ButtonWidget mButton3;
|
ButtonWidget mButton3;
|
||||||
ButtonWidget mButton4;
|
ButtonWidget mButton4;
|
||||||
|
|
||||||
|
FloatingWidget mFloatingWidget;
|
||||||
FloatingMenu mFloatingMenu;
|
FloatingMenu mFloatingMenu;
|
||||||
FloatingMenu mFloatingMenu2;
|
FloatingMenu mFloatingMenu2;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,10 +38,6 @@ void DockLayoutWidget::process(const EventHandler& events) {
|
||||||
if (!mPreviewWidget) {
|
if (!mPreviewWidget) {
|
||||||
handleResizeEvents(events);
|
handleResizeEvents(events);
|
||||||
}
|
}
|
||||||
|
|
||||||
calculateSideAreas();
|
|
||||||
calculateResizeHandles();
|
|
||||||
updateChildSideWidgets();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DockLayoutWidget::draw(Canvas& canvas) {
|
void DockLayoutWidget::draw(Canvas& canvas) {
|
||||||
|
|
@ -146,7 +142,7 @@ void DockLayoutWidget::toggleWidgetVisibility(Side side) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void DockLayoutWidget::calculateSideAreas() {
|
void DockLayoutWidget::calculateSideAreas() {
|
||||||
auto startArea = getRelativeArea();
|
auto startArea = getRelativeAreaCache();
|
||||||
|
|
||||||
for (auto& sideWidget : mSideWidgets) {
|
for (auto& sideWidget : mSideWidgets) {
|
||||||
const auto side = sideWidget.order;
|
const auto side = sideWidget.order;
|
||||||
|
|
@ -246,7 +242,7 @@ void DockLayoutWidget::handleResizeEvents(const EventHandler& events) {
|
||||||
sideWidget.resizeHandle.active = true;
|
sideWidget.resizeHandle.active = true;
|
||||||
|
|
||||||
mResizing = true;
|
mResizing = true;
|
||||||
triggerWidgetUpdate();
|
triggerWidgetUpdate("new docked child");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -275,12 +271,12 @@ void DockLayoutWidget::updateChildSideWidgets() {
|
||||||
if (!isSideVisible(Side(i))) {
|
if (!isSideVisible(Side(i))) {
|
||||||
// widget->mEnable = false;
|
// widget->mEnable = false;
|
||||||
} else {
|
} else {
|
||||||
widget->setArea(mSideWidgets[i].area);
|
widget->setAreaCache(mSideWidgets[i].area);
|
||||||
// widget->mEnable = true;
|
// widget->mEnable = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mCenterWidget) mCenterWidget->setArea(mCenterArea);
|
if (mCenterWidget) mCenterWidget->setAreaCache(mCenterArea);
|
||||||
}
|
}
|
||||||
|
|
||||||
// update depth order
|
// update depth order
|
||||||
|
|
@ -369,3 +365,9 @@ auto DockLayoutWidget::getSideFromWidget(Widget* widget) -> Side {
|
||||||
}
|
}
|
||||||
return DockLayoutWidget::NONE;
|
return DockLayoutWidget::NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DockLayoutWidget::adjustChildrenRect() {
|
||||||
|
calculateSideAreas();
|
||||||
|
calculateResizeHandles();
|
||||||
|
updateChildSideWidgets();
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,18 +5,19 @@ using namespace tp;
|
||||||
void FloatingWidget::process(const EventHandler& events) {
|
void FloatingWidget::process(const EventHandler& events) {
|
||||||
const auto relativePointer = events.getPointer();
|
const auto relativePointer = events.getPointer();
|
||||||
|
|
||||||
bool inside = getRelativeArea().isInside(relativePointer);
|
bool inside = getRelativeAreaT().isInside(relativePointer);
|
||||||
|
|
||||||
if (inside && events.isPressed(InputID::MOUSE1)) {
|
if (inside && events.isPressed(InputID::MOUSE1)) {
|
||||||
mPointerStart = relativePointer;
|
mPointerStart = relativePointer;
|
||||||
mIsFloating = true;
|
mIsFloating = true;
|
||||||
|
|
||||||
|
if (resizeHandleRect().isInside(relativePointer)) {
|
||||||
|
mIsResizing = true;
|
||||||
|
}
|
||||||
|
|
||||||
bringToFront();
|
bringToFront();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mIsFloating && resizeHandleRect().isInside(relativePointer)) {
|
|
||||||
mIsResizing = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mIsFloating && events.isReleased(InputID::MOUSE1)) {
|
if (mIsFloating && events.isReleased(InputID::MOUSE1)) {
|
||||||
mIsFloating = false;
|
mIsFloating = false;
|
||||||
|
|
@ -30,18 +31,25 @@ void FloatingWidget::process(const EventHandler& events) {
|
||||||
|
|
||||||
void FloatingWidget::pickRect() {
|
void FloatingWidget::pickRect() {
|
||||||
if (mIsFloating) {
|
if (mIsFloating) {
|
||||||
auto area = getArea();
|
auto area = getAreaCache();
|
||||||
|
|
||||||
if (mIsResizing) {
|
if (mIsResizing) {
|
||||||
|
mPointerCurrent.clamp(mMinSize, mMaxSize);
|
||||||
|
|
||||||
area.size = mPointerCurrent + mHandleSize / 2.f;
|
area.size = mPointerCurrent + mHandleSize / 2.f;
|
||||||
|
|
||||||
|
for (auto child : mChildren) {
|
||||||
|
child->triggerWidgetUpdate("floating menu resized");
|
||||||
|
}
|
||||||
|
|
||||||
} else if (mIsFloating) {
|
} else if (mIsFloating) {
|
||||||
area.pos += mPointerCurrent - mPointerStart;
|
area.pos += mPointerCurrent - mPointerStart;
|
||||||
}
|
}
|
||||||
|
|
||||||
setArea(area);
|
setAreaCache(area);
|
||||||
} else {
|
|
||||||
Widget::pickRect();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clampMinMaxSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FloatingWidget::draw(Canvas& canvas) {
|
void FloatingWidget::draw(Canvas& canvas) {
|
||||||
|
|
@ -64,3 +72,7 @@ bool FloatingWidget::propagateEventsToChildren() const {
|
||||||
bool FloatingWidget::isFloating() const {
|
bool FloatingWidget::isFloating() const {
|
||||||
return mIsFloating;
|
return mIsFloating;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool FloatingWidget::needsNextFrame() const {
|
||||||
|
return Widget::needsNextFrame() || isFloating();
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,15 +12,15 @@ void RootWidget::setRootWidget(Widget* widget) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void RootWidget::processFrame(EventHandler* events, const RectF& screenArea) {
|
void RootWidget::processFrame(EventHandler* events, const RectF& screenArea) {
|
||||||
|
mScreenArea = screenArea;
|
||||||
|
|
||||||
if (mDebug) {
|
if (mDebug) {
|
||||||
events->setEnableKeyEvents(true);
|
mScreenArea.size -= { 400, 0 };
|
||||||
if (events->isPressed(InputID::K)) mDebugStopProcessing = !mDebugStopProcessing;
|
if (events->isPressed(InputID::K)) mDebugStopProcessing = !mDebugStopProcessing;
|
||||||
if (mDebugStopProcessing) return;
|
if (mDebugStopProcessing) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
mScreenArea = screenArea;
|
mRoot->setArea(mScreenArea);
|
||||||
|
|
||||||
mRoot->setArea(screenArea);
|
|
||||||
|
|
||||||
// construct hierarchy tree of widgets to process
|
// construct hierarchy tree of widgets to process
|
||||||
updateTreeToProcess();
|
updateTreeToProcess();
|
||||||
|
|
@ -31,24 +31,37 @@ void RootWidget::processFrame(EventHandler* events, const RectF& screenArea) {
|
||||||
updateAnimations(root);
|
updateAnimations(root);
|
||||||
|
|
||||||
// update all events and call all event processing callbacks
|
// update all events and call all event processing callbacks
|
||||||
EventHandler dummyEvents;
|
events->setEnableKeyEvents(true);
|
||||||
|
events->setCursorOrigin({ 0, 0 });
|
||||||
processFocusItems(*events);
|
processFocusItems(*events);
|
||||||
processActiveTree(root, dummyEvents);
|
|
||||||
|
events->setEnableKeyEvents(false);
|
||||||
|
events->setCursorOrigin({ 0, 0 });
|
||||||
|
processActiveTree(root, *events, { 0, 0 });
|
||||||
|
|
||||||
|
updateAreaCache(root, true);
|
||||||
|
|
||||||
// update widget sizes base on individual size policies
|
// update widget sizes base on individual size policies
|
||||||
adjustSizes(root);
|
adjustSizes(root);
|
||||||
|
|
||||||
|
updateAreaCache(root, false);
|
||||||
|
|
||||||
// trigger some widgets by moise pointer
|
// trigger some widgets by moise pointer
|
||||||
handleFocusChanges(*events);
|
handleFocusChanges(*events);
|
||||||
|
|
||||||
// check triggered widgets for removal
|
// check triggered widgets for removal
|
||||||
erase_if(mTriggeredWidgets, [this](auto widget) {
|
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();
|
widget->updateAnimations();
|
||||||
if (mWidgetsToProcess.find(widget) == mWidgetsToProcess.end()) return false;
|
|
||||||
auto end = !widget->needsNextFrame();
|
auto end = !widget->needsNextFrame();
|
||||||
if (end) {
|
if (end) {
|
||||||
widget->endAnimations();
|
widget->endAnimations();
|
||||||
|
widget->mTriggerReason = "del";
|
||||||
}
|
}
|
||||||
return end;
|
return end;
|
||||||
});
|
});
|
||||||
|
|
@ -56,6 +69,40 @@ void RootWidget::processFrame(EventHandler* events, const RectF& screenArea) {
|
||||||
|
|
||||||
bool RootWidget::needsUpdate() const { return !mTriggeredWidgets.empty() || mDebugRedrawAlways; }
|
bool RootWidget::needsUpdate() const { return !mTriggeredWidgets.empty() || mDebugRedrawAlways; }
|
||||||
|
|
||||||
|
void RootWidget::debugDrawWidget(Widget* widget) {
|
||||||
|
|
||||||
|
ImGui::PushID(widget);
|
||||||
|
if (ImGui::CollapsingHeader(widget->mName.c_str())) {
|
||||||
|
|
||||||
|
ImGui::Text("trigger reason: %s", widget->mTriggerReason.c_str());
|
||||||
|
|
||||||
|
auto area = widget->getAreaT();
|
||||||
|
if (ImGui::InputFloat4("rect", &area.x)) {
|
||||||
|
widget->setArea(area);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::InputFloat2("min size", &widget->mMinSize.x);
|
||||||
|
ImGui::InputFloat2("max size", &widget->mMaxSize.x);
|
||||||
|
|
||||||
|
int sizePolicyX = int(widget->mSizePolicy.x);
|
||||||
|
int sizePolicyY = int(widget->mSizePolicy.y);
|
||||||
|
int layout = int(widget->mLayoutPolicy);
|
||||||
|
|
||||||
|
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 (ImGui::Combo("Layout", &layout, "Passive\0Vertical\0Horizontal\0")) {
|
||||||
|
widget->setLayoutPolicy(LayoutPolicy(layout));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::PopID();
|
||||||
|
}
|
||||||
|
|
||||||
void RootWidget::drawFrame(Canvas& canvas) {
|
void RootWidget::drawFrame(Canvas& canvas) {
|
||||||
// draw from top to bottom
|
// draw from top to bottom
|
||||||
canvas.rect(mScreenArea, RGBA(0, 0, 0, 1));
|
canvas.rect(mScreenArea, RGBA(0, 0, 0, 1));
|
||||||
|
|
@ -75,43 +122,33 @@ void RootWidget::drawFrame(Canvas& canvas) {
|
||||||
ImGui::Checkbox("Stop processing", &mDebugStopProcessing);
|
ImGui::Checkbox("Stop processing", &mDebugStopProcessing);
|
||||||
ImGui::Checkbox("Force new frames", &mDebugRedrawAlways);
|
ImGui::Checkbox("Force new frames", &mDebugRedrawAlways);
|
||||||
|
|
||||||
|
if (ImGui::CollapsingHeader("Triggered")) {
|
||||||
if (mInFocusWidget) {
|
if (ImGui::BeginListBox("##triggered", { -FLT_MIN, 300 })) {
|
||||||
ImGui::Text("Item under cursor %s", mInFocusWidget->mName.c_str());
|
for (auto widget : mTriggeredWidgets) {
|
||||||
|
debugDrawWidget(widget.first);
|
||||||
if (ImGui::BeginListBox("##empty", { -FLT_MIN, 0 })) {
|
|
||||||
for (auto widget = mInFocusWidget; widget && widget != this; widget = widget->mParent) {
|
|
||||||
if (ImGui::CollapsingHeader(widget->mName.c_str())) {
|
|
||||||
ImGui::InputFloat2("min size", &widget->mMinSize.x);
|
|
||||||
ImGui::InputFloat2("max size", &widget->mMaxSize.x);
|
|
||||||
|
|
||||||
int sizePolicyX = int(widget->mSizePolicy.x);
|
|
||||||
int sizePolicyY = int(widget->mSizePolicy.y);
|
|
||||||
int layout = int(widget->mLayoutPolicy);
|
|
||||||
|
|
||||||
if (ImGui::Combo("Size Policy X", &sizePolicyX, "Passive\0Expand\0Contract\0")) {
|
|
||||||
widget->setSizePolicy(SizePolicy(sizePolicyX), SizePolicy(sizePolicyY));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ImGui::Combo("Size Policy Y", &sizePolicyY, "Passive\0Expand\0Contract\0")) {
|
|
||||||
widget->setSizePolicy(SizePolicy(sizePolicyX), SizePolicy(sizePolicyY));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ImGui::Combo("Layout", &layout, "Passive\0Vertical\0Horizontal\0")) {
|
|
||||||
widget->setLayoutPolicy(LayoutPolicy(layout));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ImGui::EndListBox();
|
ImGui::EndListBox();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mInFocusWidget) {
|
||||||
|
if (ImGui::CollapsingHeader("Under cursor")) {
|
||||||
|
if (ImGui::BeginListBox("##under_cursor", { -FLT_MIN, 300 })) {
|
||||||
|
for (auto widget = mInFocusWidget; widget && widget != this; widget = widget->mParent) {
|
||||||
|
debugDrawWidget(widget);
|
||||||
|
}
|
||||||
|
ImGui::EndListBox();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void RootWidget::updateTreeToProcess() {
|
void RootWidget::updateTreeToProcess() {
|
||||||
mWidgetsToProcess.clear();
|
mWidgetsToProcess.clear();
|
||||||
for (auto widget : mTriggeredWidgets) {
|
for (auto& [widget, flag] : mTriggeredWidgets) {
|
||||||
|
flag = true;
|
||||||
for (auto iter = widget; iter; iter = iter->mParent) {
|
for (auto iter = widget; iter; iter = iter->mParent) {
|
||||||
if (iter->mParent) {
|
if (iter->mParent) {
|
||||||
mWidgetsToProcess[iter->mParent].children.insert(&mWidgetsToProcess[iter]);
|
mWidgetsToProcess[iter->mParent].children.insert(&mWidgetsToProcess[iter]);
|
||||||
|
|
@ -139,24 +176,29 @@ void RootWidget::drawRecursion(Canvas& canvas, Widget* active, const Vec2F& pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
void RootWidget::drawDebug(Canvas& canvas, Widget* active, const Vec2F& pos, int depthOrder) {
|
void RootWidget::drawDebug(Canvas& canvas, Widget* active, const Vec2F& pos, int depthOrder) {
|
||||||
auto area = RectF{ pos, active->getArea().size };
|
auto area = RectF{ pos, active->getAreaT().size };
|
||||||
|
|
||||||
if (mWidgetsToProcess.find(active) != mWidgetsToProcess.end()) {
|
auto processed = mWidgetsToProcess.find(active) != mWidgetsToProcess.end();
|
||||||
|
|
||||||
|
if (processed) {
|
||||||
RGBA color = { 1, 0, 0, 1};
|
RGBA color = { 1, 0, 0, 1};
|
||||||
canvas.frame(area, color);
|
canvas.frame(area, color);
|
||||||
canvas.text((active->mName + ":" + std::to_string(depthOrder)).c_str(), area, 22, Canvas::Align::LC, 2, color);
|
canvas.text((active->mName + ":" + std::to_string(depthOrder)).c_str(), area, 22, Canvas::Align::LC, 2, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (processed || active->mInFocus) {
|
||||||
|
canvas.circle(pos + active->mLocalPoint, 5, active->mDebugColor);
|
||||||
|
canvas.circle(active->mGlobalPoint, 15, active->mDebugColor);
|
||||||
|
}
|
||||||
|
|
||||||
if (active->mInFocus) {
|
if (active->mInFocus) {
|
||||||
RGBA color = { 1, 0, 0, 0.3f};
|
RGBA color = { 1, 0, 0, 0.3f};
|
||||||
canvas.debugCross(area, color);
|
canvas.debugCross(area, color);
|
||||||
canvas.circle(pos + active->mLocalPoint, 5, active->mDebugColor);
|
|
||||||
canvas.circle(active->mGlobalPoint, 10, active->mDebugColor);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int orderIdx = 0;
|
int orderIdx = 0;
|
||||||
for (auto child = active->mDepthOrder.lastNode(); child; child = child->prev) {
|
for (auto child = active->mDepthOrder.lastNode(); child; child = child->prev) {
|
||||||
drawDebug(canvas, child->data, pos + child->data->getArea().pos, orderIdx++);
|
drawDebug(canvas, child->data, pos + child->data->getAreaT().pos, orderIdx++);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -188,9 +230,12 @@ void RootWidget::getWidgetPath(Widget* widget, std::vector<Widget*>& out) {
|
||||||
void RootWidget::handleFocusChanges(EventHandler& events) {
|
void RootWidget::handleFocusChanges(EventHandler& events) {
|
||||||
auto prevFocus = mInFocusWidget;
|
auto prevFocus = mInFocusWidget;
|
||||||
|
|
||||||
|
events.setCursorOrigin({ 0, 0 });
|
||||||
|
|
||||||
findFocusWidget(mRoot, &mInFocusWidget, events.getPointer());
|
findFocusWidget(mRoot, &mInFocusWidget, events.getPointer());
|
||||||
|
|
||||||
if (mInFocusWidget) updateWidget(mInFocusWidget);
|
// if (mInFocusWidget == prevFocus) return;
|
||||||
|
if (mInFocusWidget) updateWidget(mInFocusWidget, "focus entered");
|
||||||
|
|
||||||
std::vector<Widget*> path2;
|
std::vector<Widget*> path2;
|
||||||
getWidgetPath(mInFocusWidget, path2);
|
getWidgetPath(mInFocusWidget, path2);
|
||||||
|
|
@ -213,21 +258,21 @@ void RootWidget::handleFocusChanges(EventHandler& events) {
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t mostCommonIdx = 0;
|
size_t mostCommonIdx = 0;
|
||||||
|
|
||||||
if (!(path1.empty() || path2.empty())) {
|
if (!(path1.empty() || path2.empty())) {
|
||||||
while (path1[mostCommonIdx] == path2[mostCommonIdx]) {
|
while (path1[mostCommonIdx] == path2[mostCommonIdx] && mostCommonIdx < min(path1.size(), path2.size())) {
|
||||||
mostCommonIdx++;
|
mostCommonIdx++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
mostCommonIdx--;
|
||||||
|
|
||||||
for (auto i = 0; i < path1.size(); i++) {
|
for (auto i = 0; i < path1.size(); i++) {
|
||||||
path1[i]->mInFocus = false;
|
path1[i]->mInFocus = false;
|
||||||
if (i >= mostCommonIdx && i < propLen1) path1[i]->mouseLeave();
|
if (i > mostCommonIdx && i < propLen1) path1[i]->mouseLeave();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto i = 0; i < path2.size(); i++) {
|
for (auto i = 0; i < path2.size(); i++) {
|
||||||
path2[i]->mInFocus = true;
|
path2[i]->mInFocus = true;
|
||||||
if (i >= mostCommonIdx && i < propLen2) path2[i]->mouseEnter();
|
if (i > mostCommonIdx && i < propLen2) path2[i]->mouseEnter();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -241,32 +286,62 @@ void RootWidget::findFocusWidget(Widget* iter, Widget** focus, const Vec2F& poin
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RootWidget::updateWidget(Widget* widget) {
|
void RootWidget::updateWidget(Widget* widget, const char* reason) {
|
||||||
mTriggeredWidgets.insert(widget);
|
DEBUG_ASSERT(reason)
|
||||||
|
widget->mTriggerReason = reason;
|
||||||
|
mTriggeredWidgets.insert({ widget, false });
|
||||||
|
}
|
||||||
|
|
||||||
|
void RootWidget::updateAreaCache(ActiveTreeNode* iter, bool read) {
|
||||||
|
if (!iter) return;
|
||||||
|
|
||||||
|
if (read) {
|
||||||
|
iter->widget->mAreaCache = iter->widget->getAreaT();
|
||||||
|
} else {
|
||||||
|
iter->widget->setArea(iter->widget->mAreaCache);
|
||||||
|
iter->widget->mArea.updateCurrentRect();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto child : iter->widget->mChildren) {
|
||||||
|
if (read) {
|
||||||
|
child->mAreaCache = child->getAreaT();
|
||||||
|
} else {
|
||||||
|
child->setArea(child->mAreaCache);
|
||||||
|
child->mArea.updateCurrentRect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto child : iter->children) {
|
||||||
|
updateAreaCache(child, read);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RootWidget::adjustSizes(ActiveTreeNode* iter) {
|
void RootWidget::adjustSizes(ActiveTreeNode* iter) {
|
||||||
if (!iter) return;
|
if (!iter) return;
|
||||||
|
|
||||||
|
iter->widget->pickRect();
|
||||||
|
|
||||||
for (auto child : iter->children) {
|
for (auto child : iter->children) {
|
||||||
adjustSizes(child);
|
adjustSizes(child);
|
||||||
}
|
}
|
||||||
|
|
||||||
iter->widget->pickRect();
|
|
||||||
iter->widget->adjustChildrenRect();
|
iter->widget->adjustChildrenRect();
|
||||||
// iter->widget->pickRect();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RootWidget::processActiveTree(ActiveTreeNode* iter, EventHandler& events) {
|
void RootWidget::processActiveTree(ActiveTreeNode* iter, EventHandler& events, Vec2F parent) {
|
||||||
if (!iter) return;
|
if (!iter) return;
|
||||||
|
|
||||||
|
auto current = parent + iter->widget->getAreaT().pos;
|
||||||
|
|
||||||
if (!iter->widget->mInFocus) {
|
if (!iter->widget->mInFocus) {
|
||||||
iter->widget->updateAnimations();
|
iter->widget->mGlobalPoint = current;
|
||||||
|
|
||||||
|
events.setCursorOrigin(current);
|
||||||
iter->widget->process(events);
|
iter->widget->process(events);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto child : iter->children) {
|
for (auto child : iter->children) {
|
||||||
processActiveTree(child, events);
|
processActiveTree(child, events, current);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -290,8 +365,8 @@ void RootWidget::processFocusItems(EventHandler& events) {
|
||||||
|
|
||||||
widgetGlobalPos[0] = 0;
|
widgetGlobalPos[0] = 0;
|
||||||
for (auto widget = 0; widget < path.size() - 1; widget++) {
|
for (auto widget = 0; widget < path.size() - 1; widget++) {
|
||||||
widgetGlobalPos[widget + 1] = widgetGlobalPos[widget] + path[widget + 1]->getArea().pos;
|
widgetGlobalPos[widget + 1] = widgetGlobalPos[widget] + path[widget + 1]->getAreaT().pos;
|
||||||
path[widget]->mGlobalPoint = widgetGlobalPos[widget];
|
// path[widget]->mGlobalPoint = widgetGlobalPos[widget];
|
||||||
}
|
}
|
||||||
|
|
||||||
bool eventsProcessed = false;
|
bool eventsProcessed = false;
|
||||||
|
|
@ -301,19 +376,15 @@ void RootWidget::processFocusItems(EventHandler& events) {
|
||||||
|
|
||||||
events.setCursorOrigin(widgetGlobalPos[iter]);
|
events.setCursorOrigin(widgetGlobalPos[iter]);
|
||||||
|
|
||||||
widget->mLocalPoint = events.getPointer();
|
widget->mGlobalPoint = widgetGlobalPos[iter];
|
||||||
|
|
||||||
if (!eventsProcessed && widget->processesEvents()) {
|
if (!eventsProcessed && widget->processesEvents()) {
|
||||||
events.setEnableKeyEvents(true);
|
events.setEnableKeyEvents(true);
|
||||||
|
|
||||||
widget->updateAnimations();
|
|
||||||
widget->process(events);
|
widget->process(events);
|
||||||
|
|
||||||
events.setEnableKeyEvents(false);
|
events.setEnableKeyEvents(false);
|
||||||
eventsProcessed = true;
|
eventsProcessed = true;
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
widget->updateAnimations();
|
|
||||||
widget->process(events);
|
widget->process(events);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ void ButtonWidget::setAction(const std::function<void()>& action) {
|
||||||
void ButtonWidget::setColor(const RGBA& in) {
|
void ButtonWidget::setColor(const RGBA& in) {
|
||||||
mColor = in;
|
mColor = in;
|
||||||
mColorAnimated.setTargetColor(mColor);
|
mColorAnimated.setTargetColor(mColor);
|
||||||
triggerWidgetUpdate();
|
triggerWidgetUpdate("color changed");
|
||||||
}
|
}
|
||||||
|
|
||||||
void ButtonWidget::process(const EventHandler& eventHandler) {
|
void ButtonWidget::process(const EventHandler& eventHandler) {
|
||||||
|
|
@ -68,12 +68,14 @@ void ButtonWidget::mouseEnter() {
|
||||||
mColorAnimated.endAnimation();
|
mColorAnimated.endAnimation();
|
||||||
|
|
||||||
mColorAnimated.setTargetColor(mColorHovered);
|
mColorAnimated.setTargetColor(mColorHovered);
|
||||||
mColorAnimated.updateCurrentRect();
|
|
||||||
triggerWidgetUpdate();
|
// mColorAnimated.updateCurrentRect();
|
||||||
|
triggerWidgetUpdate("button hovered");
|
||||||
}
|
}
|
||||||
|
|
||||||
void ButtonWidget::mouseLeave() {
|
void ButtonWidget::mouseLeave() {
|
||||||
mColorAnimated.setTargetColor(mColor);
|
mColorAnimated.setTargetColor(mColor);
|
||||||
mColorAnimated.updateCurrentRect();
|
//mColorAnimated.updateCurrentRect();
|
||||||
triggerWidgetUpdate();
|
|
||||||
|
triggerWidgetUpdate("button out of focus");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,9 @@ WidgetManagerInterface* Widget::getRoot() {
|
||||||
return dynamic_cast<WidgetManagerInterface*>(iter);
|
return dynamic_cast<WidgetManagerInterface*>(iter);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Widget::triggerWidgetUpdate() {
|
void Widget::triggerWidgetUpdate(const char* reason) {
|
||||||
if (auto root = getRoot()) {
|
if (auto root = getRoot()) {
|
||||||
root->updateWidget(this);
|
root->updateWidget(this, reason);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -23,11 +23,15 @@ Widget::Widget() {
|
||||||
mArea.endAnimation();
|
mArea.endAnimation();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Widget::setAreaCache(const tp::RectF& area) {
|
||||||
|
mAreaCache = area;
|
||||||
|
}
|
||||||
|
|
||||||
void Widget::setArea(const RectF& area) {
|
void Widget::setArea(const RectF& area) {
|
||||||
if (mArea.getTargetRect() == area) return;
|
if (mArea.getTargetRect() == area) return;
|
||||||
|
|
||||||
mArea.setTargetRect(area);
|
mArea.setTargetRect(area);
|
||||||
triggerWidgetUpdate();
|
triggerWidgetUpdate("new area");
|
||||||
}
|
}
|
||||||
|
|
||||||
RectF Widget::getArea() const {
|
RectF Widget::getArea() const {
|
||||||
|
|
@ -38,6 +42,10 @@ RectF Widget::getAreaT() const {
|
||||||
return mArea.getTargetRect();
|
return mArea.getTargetRect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RectF Widget::getAreaCache() const {
|
||||||
|
return mAreaCache;
|
||||||
|
}
|
||||||
|
|
||||||
RectF Widget::getRelativeArea() const {
|
RectF Widget::getRelativeArea() const {
|
||||||
return { {}, mArea.getCurrentRect().size };
|
return { {}, mArea.getCurrentRect().size };
|
||||||
}
|
}
|
||||||
|
|
@ -46,12 +54,16 @@ RectF Widget::getRelativeAreaT() const {
|
||||||
return { {}, mArea.getTargetRect().size };
|
return { {}, mArea.getTargetRect().size };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RectF Widget::getRelativeAreaCache() const {
|
||||||
|
return { {}, mAreaCache.size };
|
||||||
|
}
|
||||||
|
|
||||||
void Widget::endAnimations() {
|
void Widget::endAnimations() {
|
||||||
mArea.endAnimation();
|
mArea.endAnimation();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Widget::processesEvents() const {
|
bool Widget::processesEvents() const {
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Widget::updateAnimations() {
|
void Widget::updateAnimations() {
|
||||||
|
|
@ -59,7 +71,7 @@ void Widget::updateAnimations() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Widget::needsNextFrame() const {
|
bool Widget::needsNextFrame() const {
|
||||||
return !mArea.shouldEndTransition();
|
return !mArea.shouldEndTransition(); // || mInFocus;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Widget::bringToFront() {
|
void Widget::bringToFront() {
|
||||||
|
|
@ -107,8 +119,8 @@ void Widget::addChild(Widget* child) {
|
||||||
|
|
||||||
child->mParent = this;
|
child->mParent = this;
|
||||||
|
|
||||||
triggerWidgetUpdate();
|
triggerWidgetUpdate("add child");
|
||||||
child->triggerWidgetUpdate();
|
child->triggerWidgetUpdate("new parent");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Widget::removeChild(Widget* child) {
|
void Widget::removeChild(Widget* child) {
|
||||||
|
|
@ -118,29 +130,29 @@ void Widget::removeChild(Widget* child) {
|
||||||
mDepthOrder.removeNode(node);
|
mDepthOrder.removeNode(node);
|
||||||
mChildren.erase(std::remove(mChildren.begin(), mChildren.end(), child), mChildren.end());
|
mChildren.erase(std::remove(mChildren.begin(), mChildren.end(), child), mChildren.end());
|
||||||
|
|
||||||
triggerWidgetUpdate();
|
triggerWidgetUpdate("removed child");
|
||||||
child->triggerWidgetUpdate();
|
child->triggerWidgetUpdate("parent changed");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Widget::setSizePolicy(tp::Widget::SizePolicy x, tp::Widget::SizePolicy y) {
|
void Widget::setSizePolicy(tp::Widget::SizePolicy x, tp::Widget::SizePolicy y) {
|
||||||
mSizePolicy = { x, y };
|
mSizePolicy = { x, y };
|
||||||
triggerWidgetUpdate();
|
triggerWidgetUpdate("chane size policy");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Widget::setLayoutPolicy(LayoutPolicy layoutPolicy) {
|
void Widget::setLayoutPolicy(LayoutPolicy layoutPolicy) {
|
||||||
mLayoutPolicy = layoutPolicy;
|
mLayoutPolicy = layoutPolicy;
|
||||||
triggerWidgetUpdate();
|
triggerWidgetUpdate("new layout");
|
||||||
}
|
}
|
||||||
|
|
||||||
const Vec2F& Widget::getMinSize() { return mMinSize; }
|
const Vec2F& Widget::getMinSize() { return mMinSize; }
|
||||||
|
|
||||||
void Widget::setMinSize(const Vec2F& size) {
|
void Widget::setMinSize(const Vec2F& size) {
|
||||||
mMinSize = size;
|
mMinSize = size;
|
||||||
triggerWidgetUpdate();
|
triggerWidgetUpdate("new min size");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Widget::pickRect() {
|
void Widget::pickRect() {
|
||||||
auto current = getAreaT();
|
auto current = getAreaCache();
|
||||||
auto children = getChildrenEnclosure();
|
auto children = getChildrenEnclosure();
|
||||||
auto parent = getParentEnclosure();
|
auto parent = getParentEnclosure();
|
||||||
|
|
||||||
|
|
@ -150,21 +162,21 @@ void Widget::pickRect() {
|
||||||
auto newArea = RectF(rangeX, rangeY);
|
auto newArea = RectF(rangeX, rangeY);
|
||||||
|
|
||||||
for (auto child : mChildren) {
|
for (auto child : mChildren) {
|
||||||
child->setArea(child->getAreaT().move(current.pos - newArea.pos));
|
child->setAreaCache(child->getAreaCache().move(current.pos - newArea.pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
setArea(newArea);
|
setAreaCache(newArea);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Widget::clampRect() {
|
void Widget::clampRect() {
|
||||||
auto current = getAreaT();
|
auto current = getAreaCache();
|
||||||
auto children = getChildrenEnclosure();
|
auto children = getChildrenEnclosure();
|
||||||
auto parent = getParentEnclosure();
|
auto parent = getParentEnclosure();
|
||||||
|
|
||||||
auto rangeX = clampRange(current.getRangeX(), children.getRangeX(), parent.getRangeX(), false);
|
auto rangeX = clampRange(current.getRangeX(), children.getRangeX(), parent.getRangeX(), false);
|
||||||
auto rangeY = clampRange(current.getRangeY(), children.getRangeY(), parent.getRangeY(), true);
|
auto rangeY = clampRange(current.getRangeY(), children.getRangeY(), parent.getRangeY(), true);
|
||||||
|
|
||||||
setArea(RectF(rangeX, rangeY));
|
setAreaCache(RectF(rangeX, rangeY));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Widget::adjustChildrenRect() {
|
void Widget::adjustChildrenRect() {
|
||||||
|
|
@ -179,14 +191,14 @@ void Widget::adjustChildrenRect() {
|
||||||
|
|
||||||
|
|
||||||
halnf Widget::changeChildSize(tp::Widget* widget, halnf diff, bool vertical) {
|
halnf Widget::changeChildSize(tp::Widget* widget, halnf diff, bool vertical) {
|
||||||
auto prevSize = widget->getAreaT().size[vertical];
|
auto prevSize = widget->getAreaCache().size[vertical];
|
||||||
{
|
{
|
||||||
auto area = widget->getAreaT();
|
auto area = widget->getAreaCache();
|
||||||
area.size[vertical] += diff;
|
area.size[vertical] += diff;
|
||||||
widget->setArea(area);
|
widget->setAreaCache(area);
|
||||||
widget->clampRect();
|
widget->clampMinMaxSize();
|
||||||
}
|
}
|
||||||
auto newSize = widget->getAreaT().size[vertical];
|
auto newSize = widget->getAreaCache().size[vertical];
|
||||||
|
|
||||||
return newSize - prevSize;
|
return newSize - prevSize;
|
||||||
}
|
}
|
||||||
|
|
@ -197,12 +209,22 @@ void Widget::adjustLayout(bool vertical) {
|
||||||
Vec2F availableSize = getRelativeAreaT().size;
|
Vec2F availableSize = getRelativeAreaT().size;
|
||||||
|
|
||||||
for (auto child : mChildren) {
|
for (auto child : mChildren) {
|
||||||
contentSize += child->getAreaT().size;
|
if (child->mSizePolicy[vertical] == SizePolicy::Expanding) {
|
||||||
if (child->mSizePolicy[vertical] == SizePolicy::Expand) {
|
|
||||||
contributors.emplace_back( child, true );
|
contributors.emplace_back( child, true );
|
||||||
|
|
||||||
|
auto area = child->getAreaCache();
|
||||||
|
area.size[vertical] = 0;
|
||||||
|
child->setAreaCache(area);
|
||||||
|
child->clampRect();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// child->triggerWidgetUpdate("expand child in layout");
|
||||||
}
|
}
|
||||||
|
contentSize += child->getAreaCache().size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
availableSize -= mLayoutGap * ((halnf) mChildren.size() - 1) + mLayoutMargin * 2;
|
||||||
|
|
||||||
auto diff = availableSize - contentSize;
|
auto diff = availableSize - contentSize;
|
||||||
|
|
||||||
// expand or contract as much as possible
|
// expand or contract as much as possible
|
||||||
|
|
@ -212,6 +234,7 @@ void Widget::adjustLayout(bool vertical) {
|
||||||
for (auto& contributor : contributors) {
|
for (auto& contributor : contributors) {
|
||||||
if (!contributor.second) continue;
|
if (!contributor.second) continue;
|
||||||
|
|
||||||
|
// contributor.first->endAnimations();
|
||||||
auto contribution = changeChildSize(contributor.first, quota[vertical], vertical);
|
auto contribution = changeChildSize(contributor.first, quota[vertical], vertical);
|
||||||
|
|
||||||
if (contribution == 0) {
|
if (contribution == 0) {
|
||||||
|
|
@ -228,12 +251,16 @@ void Widget::adjustLayout(bool vertical) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// arrange
|
// arrange
|
||||||
halnf iterPos = 0;
|
halnf iterPos = mLayoutMargin;
|
||||||
for (auto child : mChildren) {
|
for (auto child : mChildren) {
|
||||||
auto area = child->getAreaT();
|
auto area = child->getAreaCache();
|
||||||
area.pos[vertical] = iterPos;
|
area.pos[vertical] = iterPos;
|
||||||
iterPos += area.size[vertical];
|
iterPos += area.size[vertical] + mLayoutGap;
|
||||||
child->setArea(area);
|
child->setAreaCache(area);
|
||||||
|
|
||||||
|
|
||||||
|
// child->updateAnimations();
|
||||||
|
// child->triggerWidgetUpdate("layout changed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -241,15 +268,21 @@ RangeF Widget::pickRange(const RangeF& current, const RangeF& children, const Ra
|
||||||
RangeF out;
|
RangeF out;
|
||||||
|
|
||||||
switch (mSizePolicy[vertical]) {
|
switch (mSizePolicy[vertical]) {
|
||||||
case SizePolicy::Passive: out = current; break;
|
case SizePolicy::Fixed: out = current; break;
|
||||||
case SizePolicy::Expand: out = parent; break;
|
case SizePolicy::Expanding: out = parent; break;
|
||||||
case SizePolicy::Contract: out = children; break;
|
case SizePolicy::Minimal: out = children; break;
|
||||||
}
|
}
|
||||||
|
|
||||||
out = clampRange(out, children, parent, vertical);
|
out = clampRange(out, children, parent, vertical);
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Widget::clampMinMaxSize() {
|
||||||
|
auto current = getAreaCache();
|
||||||
|
current.size.clamp(mMinSize, mMaxSize);
|
||||||
|
setAreaCache(current);
|
||||||
|
}
|
||||||
|
|
||||||
RangeF Widget::clampRange(const RangeF& current, const RangeF& children, const RangeF& parent, bool vertical) {
|
RangeF Widget::clampRange(const RangeF& current, const RangeF& children, const RangeF& parent, bool vertical) {
|
||||||
auto out = current;
|
auto out = current;
|
||||||
|
|
||||||
|
|
@ -274,12 +307,13 @@ RectF Widget::getChildrenEnclosure() {
|
||||||
RectF out;
|
RectF out;
|
||||||
|
|
||||||
if (mChildren.empty()) {
|
if (mChildren.empty()) {
|
||||||
out = { getAreaT().center(), { 0, 0 } };
|
out = { getAreaCache().center(), { 0, 0 } };
|
||||||
} else {
|
} else {
|
||||||
|
out = mChildren.front()->getAreaCache();
|
||||||
for (auto child : mChildren) {
|
for (auto child : mChildren) {
|
||||||
out.expand(child->getAreaT());
|
out.expand(child->getAreaCache());
|
||||||
}
|
}
|
||||||
out.pos += getAreaT().pos;
|
out.pos += getAreaCache().pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
|
|
@ -288,5 +322,10 @@ RectF Widget::getChildrenEnclosure() {
|
||||||
RectF Widget::getParentEnclosure() {
|
RectF Widget::getParentEnclosure() {
|
||||||
DEBUG_ASSERT(mParent);
|
DEBUG_ASSERT(mParent);
|
||||||
if (!mParent) return { { 0, 0 }, mMaxSize };
|
if (!mParent) return { { 0, 0 }, mMaxSize };
|
||||||
return mParent->getRelativeAreaT();
|
|
||||||
|
auto out = mParent->getRelativeAreaT();
|
||||||
|
if (mParent->mLayoutPolicy != LayoutPolicy::Passive) {
|
||||||
|
return out.scaleFromCenter(mParent->mLayoutMargin, true);
|
||||||
|
}
|
||||||
|
return out;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ namespace tp {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void pickRect() override {}
|
void pickRect() override {}
|
||||||
void adjustChildrenRect() override {}
|
void adjustChildrenRect() override;
|
||||||
|
|
||||||
void process(const EventHandler& events) override;
|
void process(const EventHandler& events) override;
|
||||||
void draw(Canvas& canvas) override;
|
void draw(Canvas& canvas) override;
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,10 @@ namespace tp {
|
||||||
class FloatingWidget : public Widget {
|
class FloatingWidget : public Widget {
|
||||||
public:
|
public:
|
||||||
FloatingWidget() {
|
FloatingWidget() {
|
||||||
setDebug("float", { 0.0, 0.9, 0.1, 0.7 });
|
setDebug("float", { 0.0, 0.9, 0.1, 1 });
|
||||||
|
|
||||||
|
// mSizePolicy = { SizePolicy::Contract, SizePolicy::Contract };
|
||||||
|
mLayoutPolicy = LayoutPolicy::Horizontally;
|
||||||
}
|
}
|
||||||
|
|
||||||
void process(const EventHandler& events) override;
|
void process(const EventHandler& events) override;
|
||||||
|
|
@ -18,7 +21,10 @@ namespace tp {
|
||||||
|
|
||||||
RectF resizeHandleRect();
|
RectF resizeHandleRect();
|
||||||
|
|
||||||
|
[[nodiscard]] bool needsNextFrame() const override;
|
||||||
|
|
||||||
[[nodiscard]] bool propagateEventsToChildren() const override;
|
[[nodiscard]] bool propagateEventsToChildren() const override;
|
||||||
|
[[nodiscard]] bool processesEvents() const override { return true; }
|
||||||
|
|
||||||
[[nodiscard]] bool isFloating() const;
|
[[nodiscard]] bool isFloating() const;
|
||||||
|
|
||||||
|
|
@ -38,24 +44,31 @@ namespace tp {
|
||||||
FloatingMenu() {
|
FloatingMenu() {
|
||||||
setDebug("float menu", { 0.0, 0.9, 0.1, 0.7 });
|
setDebug("float menu", { 0.0, 0.9, 0.1, 0.7 });
|
||||||
|
|
||||||
addChild(&mMenuLayout);
|
// addChild(&mMenuLayout);
|
||||||
|
|
||||||
mMenuLayout.addChild(&mHeader);
|
addChild(&mHeader);
|
||||||
mMenuLayout.addChild(&mBodyLayout);
|
addChild(&mBodyLayout);
|
||||||
|
|
||||||
mHeader.setText("Menu");
|
mHeader.setText("Menu");
|
||||||
|
|
||||||
|
mHeader.setSizePolicy(SizePolicy::Expanding, SizePolicy::Minimal);
|
||||||
|
mBodyLayout.setSizePolicy(SizePolicy::Expanding, SizePolicy::Expanding);
|
||||||
|
|
||||||
|
setLayoutPolicy(LayoutPolicy::Vertically);
|
||||||
|
mBodyLayout.setLayoutPolicy(LayoutPolicy::Vertically);
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void addToMenu(Widget* widget) {
|
void addToMenu(Widget* widget) {
|
||||||
|
widget->setSizePolicy(SizePolicy::Expanding, SizePolicy::Minimal);
|
||||||
mBodyLayout.addChild(widget);
|
mBodyLayout.addChild(widget);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
VerticalLayout mMenuLayout;
|
// VerticalLayout mMenuLayout;
|
||||||
VerticalLayout mBodyLayout;
|
Widget mBodyLayout;
|
||||||
LabelWidget mHeader;
|
LabelWidget mHeader;
|
||||||
|
|
||||||
ButtonWidget mTestButton;
|
// ButtonWidget mTestButton;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -19,7 +19,7 @@ namespace tp {
|
||||||
RootWidget() { setDebug("root", RGBA(1)); }
|
RootWidget() { setDebug("root", RGBA(1)); }
|
||||||
|
|
||||||
void setRootWidget(Widget* widget);
|
void setRootWidget(Widget* widget);
|
||||||
void updateWidget(Widget*);
|
void updateWidget(Widget*, const char* reason = nullptr) override;
|
||||||
|
|
||||||
static void setWidgetArea(Widget& widget, const RectF& rect);
|
static void setWidgetArea(Widget& widget, const RectF& rect);
|
||||||
|
|
||||||
|
|
@ -37,16 +37,18 @@ namespace tp {
|
||||||
void findFocusWidget(Widget* iter, Widget** focus, const Vec2F& pointer);
|
void findFocusWidget(Widget* iter, Widget** focus, const Vec2F& pointer);
|
||||||
void handleFocusChanges(EventHandler& events);
|
void handleFocusChanges(EventHandler& events);
|
||||||
void getWidgetPath(Widget* widget, std::vector<Widget*>& out);
|
void getWidgetPath(Widget* widget, std::vector<Widget*>& out);
|
||||||
void processActiveTree(ActiveTreeNode* iter, EventHandler& events);
|
void processActiveTree(ActiveTreeNode* iter, EventHandler& events, Vec2F pos);
|
||||||
void processFocusItems(EventHandler& events);
|
void processFocusItems(EventHandler& events);
|
||||||
void adjustSizes(ActiveTreeNode* iter);
|
void adjustSizes(ActiveTreeNode* iter);
|
||||||
|
void updateAreaCache(ActiveTreeNode* iter, bool read);
|
||||||
|
static void debugDrawWidget(Widget* widget);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
RectF mScreenArea;
|
RectF mScreenArea;
|
||||||
Widget* mRoot = nullptr;
|
Widget* mRoot = nullptr;
|
||||||
|
|
||||||
// frame to frame changes
|
// frame to frame changes
|
||||||
std::set<Widget*> mTriggeredWidgets;
|
std::map<Widget*, bool> mTriggeredWidgets;
|
||||||
std::map<Widget*, ActiveTreeNode> mWidgetsToProcess;
|
std::map<Widget*, ActiveTreeNode> mWidgetsToProcess;
|
||||||
Widget* mInFocusWidget = nullptr;
|
Widget* mInFocusWidget = nullptr;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,9 @@ namespace tp {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum class SizePolicy {
|
enum class SizePolicy {
|
||||||
Passive,
|
Fixed,
|
||||||
Expand,
|
Expanding,
|
||||||
Contract,
|
Minimal,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class LayoutPolicy {
|
enum class LayoutPolicy {
|
||||||
|
|
@ -40,7 +40,7 @@ namespace tp {
|
||||||
|
|
||||||
WidgetManagerInterface* getRoot();
|
WidgetManagerInterface* getRoot();
|
||||||
|
|
||||||
void triggerWidgetUpdate();
|
void triggerWidgetUpdate(const char* reason = nullptr);
|
||||||
|
|
||||||
void bringToFront();
|
void bringToFront();
|
||||||
void bringToBack();
|
void bringToBack();
|
||||||
|
|
@ -67,6 +67,9 @@ namespace tp {
|
||||||
// resizing
|
// resizing
|
||||||
RangeF pickRange(const RangeF& current, const RangeF& children, const RangeF& parent, bool vertical);
|
RangeF pickRange(const RangeF& current, const RangeF& children, const RangeF& parent, bool vertical);
|
||||||
RangeF clampRange(const RangeF& current, const RangeF& children, const RangeF& parent, bool vertical);
|
RangeF clampRange(const RangeF& current, const RangeF& children, const RangeF& parent, bool vertical);
|
||||||
|
|
||||||
|
void clampMinMaxSize();
|
||||||
|
|
||||||
RectF getChildrenEnclosure();
|
RectF getChildrenEnclosure();
|
||||||
RectF getParentEnclosure();
|
RectF getParentEnclosure();
|
||||||
void adjustLayout(bool vertical);
|
void adjustLayout(bool vertical);
|
||||||
|
|
@ -83,11 +86,14 @@ namespace tp {
|
||||||
public:
|
public:
|
||||||
[[nodiscard]] RectF getArea() const;
|
[[nodiscard]] RectF getArea() const;
|
||||||
[[nodiscard]] RectF getAreaT() const;
|
[[nodiscard]] RectF getAreaT() const;
|
||||||
|
[[nodiscard]] RectF getAreaCache() const;
|
||||||
|
|
||||||
[[nodiscard]] RectF getRelativeArea() const;
|
[[nodiscard]] RectF getRelativeArea() const;
|
||||||
[[nodiscard]] RectF getRelativeAreaT() const;
|
[[nodiscard]] RectF getRelativeAreaT() const;
|
||||||
|
[[nodiscard]] RectF getRelativeAreaCache() const;
|
||||||
|
|
||||||
void setArea(const RectF& area);
|
void setArea(const RectF& area);
|
||||||
|
void setAreaCache(const RectF& area);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Widget* mParent = nullptr;
|
Widget* mParent = nullptr;
|
||||||
|
|
@ -100,20 +106,28 @@ namespace tp {
|
||||||
|
|
||||||
// resizing
|
// resizing
|
||||||
LayoutPolicy mLayoutPolicy = LayoutPolicy::Passive;
|
LayoutPolicy mLayoutPolicy = LayoutPolicy::Passive;
|
||||||
Vec2<SizePolicy> mSizePolicy = { SizePolicy::Passive, SizePolicy::Passive };
|
Vec2<SizePolicy> mSizePolicy = { SizePolicy::Fixed, SizePolicy::Fixed };
|
||||||
Vec2F mMinSize = { 50, 50 };
|
Vec2F mMinSize = { 50, 50 };
|
||||||
Vec2F mMaxSize = { FLT_MAX / 2, FLT_MAX / 2 };
|
Vec2F mMaxSize = { FLT_MAX / 2, FLT_MAX / 2 };
|
||||||
|
|
||||||
bool mInFocus = false;
|
bool mInFocus = false;
|
||||||
|
|
||||||
|
halnf mLayoutGap = 5;
|
||||||
|
halnf mLayoutMargin = 10;
|
||||||
|
|
||||||
|
// cache
|
||||||
|
RectF mAreaCache;
|
||||||
|
|
||||||
// debug
|
// debug
|
||||||
|
int inOutCallbacks = 0;
|
||||||
std::string mName = "widget base";
|
std::string mName = "widget base";
|
||||||
Vec2F mLocalPoint;
|
Vec2F mLocalPoint;
|
||||||
Vec2F mGlobalPoint;
|
Vec2F mGlobalPoint;
|
||||||
RGBA mDebugColor = { 1, 1, 1, 0.3 };
|
RGBA mDebugColor = { 1, 1, 1, 0.3 };
|
||||||
|
std::string mTriggerReason = "none";
|
||||||
};
|
};
|
||||||
|
|
||||||
struct WidgetManagerInterface : public Widget {
|
struct WidgetManagerInterface : public Widget {
|
||||||
virtual void updateWidget(Widget*) = 0;
|
virtual void updateWidget(Widget*, const char* reason = nullptr) = 0;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue