Something went wrong. Try again.
art software for the rest of us
drawing paint image sdl art
Something went wrong. Try again.
C++
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169#include "Widget.h"#include "../UserInterface/Primitives.h"#include <algorithm>
namespace UI {
std::weak_ptr<void> Widget::GetLifetimeToken() const { return lifetimeToken;}
void Widget::AddChild(std::unique_ptr<Widget> child) { if (!child) return; child->parent = this; children.push_back(std::move(child)); MarkDirty(DirtyFlags::Layout | DirtyFlags::Hierarchy);}
std::unique_ptr<Widget> Widget::RemoveChild(Widget* child) { auto it = std::find_if(children.begin(), children.end(), [child](const std::unique_ptr<Widget>& p) { return p.get() == child; });
if (it != children.end()) { std::unique_ptr<Widget> removed = std::move(*it); removed->parent = nullptr; children.erase(it); MarkDirty(DirtyFlags::Layout | DirtyFlags::Hierarchy); return removed; } return nullptr;}
void Widget::MarkDirty(DirtyFlags flags) { dirtyFlags |= flags; // Propagate layout dirty flags upwards to parent containers if (parent && (flags & DirtyFlags::Layout) != DirtyFlags::Clean) { parent->MarkDirty(DirtyFlags::Layout); }}
void Widget::ClearDirty(DirtyFlags flags) { dirtyFlags &= flags;}
bool Widget::IsDirty(DirtyFlags flags) const { return (dirtyFlags & flags) != DirtyFlags::Clean;}
void Widget::Measure(Size availableSpace) { // Basic leaf/default measure implementation: // Takes available space minus margins and calculates desired size based on children float availW = std::max(0.0f, availableSpace.width - margin.HorizontalTotal()); float availH = std::max(0.0f, availableSpace.height - margin.VerticalTotal()); Size childConstraint{availW, availH};
float maxChildW = 0.0f; float maxChildH = 0.0f;
for (auto& child : children) { if (!child->IsVisible()) continue; child->Measure(childConstraint); maxChildW = std::max(maxChildW, child->GetDesiredSize().width); maxChildH = std::max(maxChildH, child->GetDesiredSize().height); }
float widthWithPadding = maxChildW + padding.HorizontalTotal(); float heightWithPadding = maxChildH + padding.VerticalTotal();
desiredSize.width = std::clamp(widthWithPadding + margin.HorizontalTotal(), minSize.width, maxSize.width); desiredSize.height = std::clamp(heightWithPadding + margin.VerticalTotal(), minSize.height, maxSize.height);}
void Widget::ArrangeCore(Rect finalBounds) { localBounds = finalBounds;
if (parent) { globalBounds.X = parent->GetGlobalBounds().X + localBounds.X; globalBounds.Y = parent->GetGlobalBounds().Y + localBounds.Y; } else { globalBounds.X = localBounds.X; globalBounds.Y = localBounds.Y; } globalBounds.Width = localBounds.Width; globalBounds.Height = localBounds.Height;}
void Widget::Arrange(Rect finalBounds) { ArrangeCore(finalBounds);
Rect contentBounds{padding.Left, padding.Top, localBounds.Width - padding.HorizontalTotal(), localBounds.Height - padding.VerticalTotal()};
for (auto& child : children) { if (!child->IsVisible()) continue;
Size childDesired = child->GetDesiredSize(); Thickness childMargin = child->GetMargin();
float availW = std::max(0.0f, contentBounds.Width - childMargin.HorizontalTotal()); float availH = std::max(0.0f, contentBounds.Height - childMargin.VerticalTotal()); float naturalW = std::max(0.0f, childDesired.width - childMargin.HorizontalTotal()); float naturalH = std::max(0.0f, childDesired.height - childMargin.VerticalTotal());
auto horiz = CalculateAxisAlignment(child->GetHorizontalAlignment(), availW, naturalW, childMargin.Left); auto vert = CalculateAxisAlignment(child->GetVerticalAlignment(), availH, naturalH, childMargin.Top);
child->Arrange(Rect(contentBounds.X + horiz.offset, contentBounds.Y + vert.offset, horiz.size, vert.size)); }
ClearDirty(DirtyFlags::Layout);}
void Widget::Draw() { if (!visible) return;
if (clipContents) { Primitives::PushClip(globalBounds); }
for (auto& child : children) { if (child->IsVisible()) { child->Draw(); } }
if (clipContents) { Primitives::PopClip(); } ClearDirty(DirtyFlags::Render);}
void Widget::Update(float deltaSeconds) { for (auto& child : children) { if (child->IsVisible()) { child->Update(deltaSeconds); } }}
void Widget::SetScrollOffset(Point /*offset*/) {}
bool Widget::OnPointerStream(const std::shared_ptr<PointerStream>& stream) { const auto event = stream->PeekLatestInput(); // Default implementation passes event down to children in reverse z-order for (auto it = children.rbegin(); it != children.rend(); ++it) { if ((*it)->IsVisible() && (*it)->IsEnabled() && (*it)->globalBounds.Contains(event.screenX, event.screenY)) { if ((*it)->OnPointerStream(stream)) { return true; } } } return false;}
Widget* Widget::HitTest(Point screenPoint) { if (!visible || !enabled) return nullptr; if (!globalBounds.Contains(screenPoint)) return nullptr;
// Search children in reverse z-order (topmost first) for (auto it = children.rbegin(); it != children.rend(); ++it) { Widget* hit = (*it)->HitTest(screenPoint); if (hit) return hit; }
return this;}
}