1#include "RootDisplay.hpp"
3#include "Constraint.hpp"
4#include "Animation.hpp"
10static void safeElementDeleter(Element* elem) {
11 if (elem && !elem->isProtected) {
38 this->recalcPosition(
parent);
43 if (event->
touchIn(0, 0, 400, 240))
return ret;
49 ret |= onTouchDown(event);
50 ret |= onTouchDrag(event);
51 ret |= onTouchUp(event);
56 size_t elementCount = this->
elements.size();
57 for (
size_t x = 0; x < elementCount; x++)
60 if (x < this->
elements.size() && this->elements[x])
62 bool childHandled = this->
elements[x]->process(event);
65 if (childHandled && this->
elements.size() != elementCount) {
81 if (RootDisplay::idleCursorPulsing) {
95 this->recalcPosition(
parent);
98 if (this->hasBackground) {
100 this->renderBackground(
true);
102 else if (RootDisplay::isDebug) {
103 backgroundColor = randomColor();
104 this->renderBackground(
false);
110 subelement->render(
this);
113 CST_Renderer* renderer = getRenderer();
118 float effectiveScale = getEffectiveScale();
119 int scaledWidth = (int)(this->
width * effectiveScale);
120 int scaledHeight = (int)(this->height * effectiveScale);
122 auto marginSpacing = cornerRadius > 0 ? 0 : 5;
123 CST_Rect d = { this->xAbs - marginSpacing, this->yAbs - marginSpacing, scaledWidth + marginSpacing*2, scaledHeight + marginSpacing*2 };
124 if (cornerRadius > 0) {
126 CST_roundedBoxRGBA(renderer, d.x, d.y, d.x + d.w, d.y + d.h,
127 cornerRadius, 0x10, 0xD9, 0xD9, 0x40);
129 CST_SetDrawBlend(renderer,
true);
130 CST_SetDrawColorRGBA(renderer, 0x10, 0xD9, 0xD9, 0x40);
131 CST_FillRect(renderer, &d);
137 float effectiveScale = getEffectiveScale();
138 int scaledWidth = (int)(this->
width * effectiveScale);
139 int scaledHeight = (int)(this->height * effectiveScale);
141 auto marginSpacing = cornerRadius > 0 ? 0 : 5;
142 CST_Rect d = { this->xAbs - marginSpacing, this->yAbs - marginSpacing, scaledWidth + marginSpacing*2, scaledHeight + marginSpacing*2 };
145 int ticks = CST_GetTicks() / 100;
146 int pulseState = ticks % 20;
147 if (pulseState > 9) {
148 pulseState = 19 - pulseState;
151 if (!RootDisplay::idleCursorPulsing) {
157 auto decreaser = cornerRadius > 0 ? 0 : -2;
158 for (
int x = decreaser; x <= 3; x++)
162 int g = 0xD9 - 0x01 * pulseState;
163 int b = 0xD9 - 0x01 * pulseState;
164 int edgeMod = x==1 ? 0 : abs(x);
165 int a = fmax(0x0, 0xFF - 0x10 * pulseState * edgeMod);
166 CST_roundedRectangleRGBA(renderer, d.x + x, d.y + x, d.x + d.w - x, d.y + d.h - x, cornerRadius, r, g, b, a);
170 CST_roundedRectangleRGBA(renderer, d.x, d.y, d.x + d.w, d.y + d.h, cornerRadius, 0x10, 0xD9, 0xD9, 0xFF);
172 CST_roundedRectangleRGBA(renderer, d.x + 1, d.y + 1, d.x + d.w - 1, d.y + d.h - 1, cornerRadius, 0x10, 0xD9, 0xD9, 0xFF);
177void Element::recalcPosition(
Element* parent) {
179 for (
auto& constraint : constraints)
181 constraint->apply(
this);
184 float effectiveScale = getEffectiveScale();
187 if (
parent && !isAbsolute)
189 if (constraints.empty()) {
190 this->xAbs =
parent->xAbs + (int)(this->x * effectiveScale);
191 this->yAbs =
parent->yAbs + (int)(this->y * effectiveScale);
193 this->xAbs =
parent->xAbs + this->x;
194 this->yAbs =
parent->yAbs + this->y;
198 this->xAbs = this->x;
199 this->yAbs = this->y;
203 if (animations.size() > 0) {
204 std::vector<size_t> toRemove;
205 for (
size_t i = 0; i < animations.size(); i++)
207 auto& animation = animations[i];
211 bool finished = animation->step();
213 toRemove.push_back(i);
217 for (
auto it = toRemove.rbegin(); it != toRemove.rend(); ++it) {
218 animations.erase(animations.begin() + *it);
223float Element::getEffectiveScale()
const {
225 return RootDisplay::globalScale * this->scale;
228CST_Rect Element::getBounds()
230 float effectiveScale = getEffectiveScale();
234 .w = (int)(this->
width * effectiveScale),
235 .h = (int)(this->height * effectiveScale),
239void Element::renderBackground(
bool fill) {
240 CST_Renderer* renderer = getRenderer();
241 CST_Rect bounds = getBounds();
242 auto r = backgroundColor.r * 0xFF;
243 auto g = backgroundColor.g * 0xFF;
244 auto b = backgroundColor.b * 0xFF;
246 if (cornerRadius > 0) {
247 const auto renderRect = fill ? CST_roundedBoxRGBA : CST_roundedRectangleRGBA;
248 renderRect(renderer, bounds.x, bounds.y, bounds.x + bounds.w, bounds.y + bounds.h,
249 cornerRadius, backgroundColor.r * 0xFF, backgroundColor.g * 0xFF, backgroundColor.b * 0xFF, backgroundOpacity);
251 CST_SetDrawColorRGBA(renderer, r, g, b, backgroundOpacity);
252 const auto renderRect = fill ? CST_FillRect : CST_DrawRect;
253 renderRect(renderer, &bounds);
265 if (!event->isTouchDown())
268 float effectiveScale = getEffectiveScale();
269 int scaledWidth = (int)(this->
width * effectiveScale);
270 int scaledHeight = (int)(this->height * effectiveScale);
272 if (!event->
touchIn(this->xAbs, this->yAbs, scaledWidth, scaledHeight))
278 this->lastMouseX =
event->xPos;
287bool Element::onTouchDrag(InputEvents* event)
291 if (!event->isTouchDrag())
294 float effectiveScale = getEffectiveScale();
295 int scaledWidth = (int)(this->
width * effectiveScale);
296 int scaledHeight = (int)(this->height * effectiveScale);
300 if (event->touchIn(this->xAbs, this->yAbs, scaledWidth, scaledHeight)) {
307 CST_LowRumble(event, 200);
310 CST_SetCursor(CST_CURSOR_HAND);
317 if (initialElasticCounter != NO_HIGHLIGHT) {
319 CST_SetCursor(CST_CURSOR_ARROW);
327 int TRESHOLD = 40 * effectiveScale;
331 if (this->
dragging && (abs(event->yPos - this->lastMouseY) >= TRESHOLD || abs(event->xPos - this->lastMouseX) >= TRESHOLD))
336 if (prevElasticCounter != NO_HIGHLIGHT) {
338 CST_SetCursor(CST_CURSOR_ARROW);
345bool Element::onTouchUp(InputEvents* event)
347 if (!event->isTouchUp())
352 float effectiveScale = getEffectiveScale();
353 int scaledWidth = (int)(this->
width * effectiveScale);
354 int scaledHeight = (int)(this->height * effectiveScale);
362 if (event->touchIn(this->xAbs, this->yAbs, scaledWidth, scaledHeight))
377 if (actionWithEvents != NULL) {
378 this->actionWithEvents(event);
383 ret |= wasHighlighted;
399void Element::addNode(std::unique_ptr<Element> node)
403 printf(
"[Chesto] Warning: Attempted to add null node to %p\n",
this);
408 Element* rawPtr = node.get();
411 for (
const auto& existing :
elements) {
412 if (existing.get() == rawPtr) {
414 printf(
"[Chesto] Warning: Node %p already exists in parent %p\n", rawPtr,
this);
420 rawPtr->parent =
this;
423 Element* ptr = node.release();
424 elements.push_back(std::unique_ptr<Element, std::function<
void(Element*)>>(
430void Element::addStackMember(Element* element)
432 if (!element)
return;
435 for (
const auto& existing :
elements) {
436 if (existing.get() == element) {
441 element->parent =
this;
442 element->isProtected =
true;
446 elements.push_back(std::unique_ptr<Element, std::function<
void(Element*)>>(
453void Element::remove(Element *element)
458 [element](
const std::unique_ptr<Element, std::function<
void(Element*)>>& e) {
459 return e.get() == element;
465void Element::removeAll()
472Element* Element::setPosition(
int x,
int y)
478Element* Element::setAction(std::function<
void()> func)
484Element* Element::setAbsolute(
bool isAbs)
490CST_Renderer* Element::getRenderer() {
491 return RootDisplay::renderer;
494Element* Element::constrain(
int flags,
int padding)
496 constraints.push_back(std::make_unique<Constraint>(flags, padding));
500Element* Element::constrainToTarget(Element* target,
int flags,
int padding)
502 constraints.push_back(std::make_unique<Constraint>(flags, padding, std::vector<Element*>{target}));
507Element* Element::animate(
509 std::function<
void(
float)> onStep,
510 std::function<
void()> onFinish
512 animations.push_back(std::make_unique<Animation>(
513 CST_GetTicks(), duration, onStep, onFinish)
520Element* Element::moveToFront() {
524 [
this](
const std::unique_ptr<Element, std::function<
void(Element*)>>& e) {
525 return e.get() == this;
538Element* Element::setTouchable(
bool touchable)
546 CST_Texture* target = SDL_CreateTexture(getRenderer(), SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET,
width, height);
549 SDL_SetRenderTarget(getRenderer(), target);
552 SDL_SetRenderDrawColor(getRenderer(), 255, 255, 255, 255);
553 SDL_RenderClear(getRenderer());
559 SDL_SetRenderTarget(getRenderer(), NULL);
562 CST_SavePNG(target, path.c_str());
std::vector< std::unique_ptr< Element, std::function< void(Element *)> > > elements
visible GUI child elements of this element
int width
width and height of this element (must be manually set, isn't usually calculated (but is in some case...
virtual void render(Element *parent)
display the current state of the display
bool hidden
whether this element should skip rendering or not
virtual bool process(InputEvents *event)
process any input that is received for this element
bool needsRedraw
whether or not this element needs the screen redrawn next time it's processed
bool touchable
whether or not this element can be touched (highlights bounds)
int lastMouseY
the last Y, X coordinate of the mouse (from a drag probably)
Element * parent
the parent element (reference only, not owned)
void screenshot(std::string path)
Take a screenshot of this element and its children, and save it to the given path.
int elasticCounter
how much time is left in an elastic-type flick/scroll set by the last distance traveled in a scroll,...
std::function< void()> action
the action to call (from binded callback) on touch or button selection https://stackoverflow....
void position(int x, int y)
position the element
int futureRedrawCounter
whether this element needs a redraw for the next X redraws (decreases each time) (0 is no redraws)
bool dragging
whether or not this element is currently being dragged