| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693 |
- // zlib open source license
- //
- // Copyright (c) 2018 to 2023 David Forsgren Piuva
- //
- // This software is provided 'as-is', without any express or implied
- // warranty. In no event will the authors be held liable for any damages
- // arising from the use of this software.
- //
- // Permission is granted to anyone to use this software for any purpose,
- // including commercial applications, and to alter it and redistribute it
- // freely, subject to the following restrictions:
- //
- // 1. The origin of this software must not be misrepresented; you must not
- // claim that you wrote the original software. If you use this software
- // in a product, an acknowledgment in the product documentation would be
- // appreciated but is not required.
- //
- // 2. Altered source versions must be plainly marked as such, and must not be
- // misrepresented as being the original software.
- //
- // 3. This notice may not be removed or altered from any source
- // distribution.
- #include <cstdint>
- #include "VisualComponent.h"
- using namespace dsr;
- PERSISTENT_DEFINITION(VisualComponent)
- VisualComponent::VisualComponent() {}
- VisualComponent::~VisualComponent() {
- this->callback_destroyEvent();
- // Let the children know that the parent component no longer exists.
- for (int i = 0; i < this->getChildCount(); i++) {
- this->children[i]->parent = nullptr;
- }
- }
- void VisualComponent::declareAttributes(StructureDefinition &target) const {
- target.declareAttribute(U"Name");
- target.declareAttribute(U"Index");
- target.declareAttribute(U"Visible");
- target.declareAttribute(U"Left");
- target.declareAttribute(U"Top");
- target.declareAttribute(U"Right");
- target.declareAttribute(U"Bottom");
- }
- Persistent* VisualComponent::findAttribute(const ReadableString &name) {
- if (string_caseInsensitiveMatch(name, U"Name")) {
- return &(this->name);
- } else if (string_caseInsensitiveMatch(name, U"Index")) {
- return &(this->index);
- } else if (string_caseInsensitiveMatch(name, U"Visible")) {
- return &(this->visible);
- } else if (string_caseInsensitiveMatch(name, U"Left")) {
- this->regionAccessed = true;
- return &(this->region.left);
- } else if (string_caseInsensitiveMatch(name, U"Top")) {
- this->regionAccessed = true;
- return &(this->region.top);
- } else if (string_caseInsensitiveMatch(name, U"Right")) {
- this->regionAccessed = true;
- return &(this->region.right);
- } else if (string_caseInsensitiveMatch(name, U"Bottom")) {
- this->regionAccessed = true;
- return &(this->region.bottom);
- } else {
- return nullptr;
- }
- }
- // Pre-condition: component != nullptr
- // Post-condition: Returns the root of component
- static Handle<VisualComponent> getRoot(VisualComponent *component) {
- assert(component != nullptr);
- while (component->parent != nullptr) {
- component = component->parent;
- }
- return component->getHandle();
- }
- IVector2D VisualComponent::getDesiredDimensions() {
- // Unless this virtual method is overridden, toolbars and such will try to give these dimensions to the component.
- return IVector2D(32, 32);
- }
- bool VisualComponent::isContainer() const {
- return true;
- }
- IRect VisualComponent::getLocation() {
- // If someone requested access to Left, Top, Right or Bottom, regionAccessed will be true
- if (this->regionAccessed) {
- // Now that a fixed location is requested, we need to recalculate the location from the flexible region based on parent dimensions
- this->updateLayout();
- this->regionAccessed = false;
- }
- return this->location;
- }
- void VisualComponent::changedAttribute(const ReadableString &name) {
- if (this->parent) {
- this->parent->childChanged = true;
- }
- }
- void VisualComponent::setRegion(const FlexRegion &newRegion) {
- this->region = newRegion;
- if (this->parent) {
- this->parent->childChanged = true;
- }
- }
- FlexRegion VisualComponent::getRegion() const {
- return this->region;
- }
- void VisualComponent::setVisible(bool visible) {
- this->visible.value = visible;
- if (this->parent) {
- this->parent->childChanged = true;
- }
- }
- bool VisualComponent::getVisible() const {
- return this->visible.value;
- }
- void VisualComponent::setName(const String& newName) {
- this->name.value = newName;
- if (this->parent) {
- this->parent->childChanged = true;
- }
- }
- String VisualComponent::getName() const {
- return this->name.value;
- }
- void VisualComponent::setIndex(int newIndex) {
- this->index.value = newIndex;
- if (this->parent) {
- this->parent->childChanged = true;
- }
- }
- int VisualComponent::getIndex() const {
- return this->index.value;
- }
- void VisualComponent::setLocation(const IRect &newLocation) {
- IRect oldLocation = this->location;
- this->location = newLocation;
- if (oldLocation != newLocation) {
- this->updateLocationEvent(oldLocation, newLocation);
- }
- this->changedLocation(oldLocation, newLocation);
- }
- void VisualComponent::updateLayout() {
- this->setLocation(this->region.getNewLocation(this->givenSpace));
- }
- void VisualComponent::applyLayout(const IRect& givenSpace) {
- this->givenSpace = givenSpace;
- this->updateLayout();
- }
- void VisualComponent::updateLocationEvent(const IRect& oldLocation, const IRect& newLocation) {
- // Place each child component
- for (int i = 0; i < this->getChildCount(); i++) {
- this->children[i]->applyLayout(IRect(0, 0, newLocation.width(), newLocation.height()));
- }
- }
- // Check if any change requires the child layout to update.
- // Used to realign members of toolbars after a desired dimension changed.
- void VisualComponent::updateChildLocations() {
- if (this->childChanged) {
- this->updateLocationEvent(this->location, this->location);
- this->childChanged = false;
- }
- }
- // Overlays are only cropped by the entire canvas, so the offset is the upper left corner of component relative to the upper left corner of the canvas.
- static void drawOverlays(ImageRgbaU8& targetImage, VisualComponent &component, const IVector2D& offset) {
- // Invisible components are not allowed to display overlays, because the component system is
- // responsible for visibility settings that specific components are likely to forget about.
- if (component.getVisible() && component.ownsOverlay()) {
- // Check if the component has the overlay shown.
- if (component.showingOverlay()) {
- // Draw the component's own overlay below child overlays.
- component.drawOverlay(targetImage, offset - component.location.upperLeft());
- }
- // Draw overlays in each child component on top.
- for (int i = 0; i < component.getChildCount(); i++) {
- drawOverlays(targetImage, component.children[i].getReference(), offset + component.children[i]->location.upperLeft());
- }
- }
- }
- // Offset may become non-zero when the origin is outside of targetImage from being clipped outside of the parent region
- void VisualComponent::draw(ImageRgbaU8& targetImage, const IVector2D& offset) {
- // TODO: Any more good places to send notifications to make the GUI respond faster?
- // When about to start drawing from the root, check for state changes and handle events before drawing,
- // so that anything needed for visuals is handled without further delay.
- if (this->parent == nullptr) {
- this->sendNotifications();
- }
- if (this->getVisible()) {
- this->updateChildLocations();
- IRect containerBound = this->getLocation() + offset;
- this->drawSelf(targetImage, containerBound);
- // Draw each child component
- if (!this->managesChildren()) {
- for (int i = 0; i < this->getChildCount(); i++) {
- this->children[i]->drawClipped(targetImage, containerBound.upperLeft(), containerBound);
- }
- }
- // When drawing the root, start recursive drawing of all overlays.
- if (this->parent == nullptr) {
- drawOverlays(targetImage, *this, this->location.upperLeft());
- }
- }
- }
- void VisualComponent::drawClipped(ImageRgbaU8 targetImage, const IVector2D& offset, const IRect& clipRegion) {
- IRect finalRegion = IRect::cut(clipRegion, IRect(0, 0, image_getWidth(targetImage), image_getHeight(targetImage)));
- if (finalRegion.hasArea()) {
- // TODO: Optimize allocation of sub-images
- ImageRgbaU8 target = image_getSubImage(targetImage, finalRegion);
- this->draw(target, offset - finalRegion.upperLeft());
- }
- }
- // A red rectangle is drawn as a placeholder if the class couldn't be found
- // TODO: Should the type name be remembered in the base class for serializing missing components?
- void VisualComponent::drawSelf(ImageRgbaU8& targetImage, const IRect &relativeLocation) {
- draw_rectangle(targetImage, relativeLocation, ColorRgbaI32(200, 50, 50, 255));
- }
- void VisualComponent::drawOverlay(ImageRgbaU8& targetImage, const IVector2D &absoluteOffset) {}
- // Manual use with the correct type
- void VisualComponent::addChildComponent(Handle<VisualComponent> child) {
- if (!this->isContainer()) {
- sendWarning(U"Cannot attach a child to a non-container parent component!\n");
- } else if (child.getUnsafe() == this) {
- sendWarning(U"Cannot attach a component to itself!\n");
- } else if (child->hasChild(this)) {
- sendWarning(U"Cannot attach to its own parent as a child component!\n");
- } else {
- // Remove from any previous parent
- child->detachFromParent();
- // Update layout based on the new parent size
- child->applyLayout(IRect(0, 0, this->location.width(), this->location.height()));
- // Connect to the new parent
- this->children.push(child);
- this->childChanged = true;
- child->parent = this;
- child->window = this->window;
- }
- }
- // Automatic insertion from loading
- bool VisualComponent::addChild(Handle<Persistent> child) {
- // Try to cast from base class Persistent to derived class VisualComponent
- Handle<VisualComponent> visualComponent = handle_dynamicCast<VisualComponent>(child);
- if (visualComponent.isNull()) {
- return false; // Wrong type!
- } else {
- this->addChildComponent(visualComponent);
- return true; // Success!
- }
- }
- int VisualComponent::getChildCount() const {
- return this->children.length();
- }
- Handle<Persistent> VisualComponent::getChild(int index) const {
- if (index >= 0 && index < this->children.length()) {
- return this->children[index];
- } else {
- return Handle<Persistent>(); // Null handle for out of bound.
- }
- }
- static void detachFromWindow(Handle<VisualComponent> component) {
- component->window = Handle<BackendWindow>();
- for (int c = 0; c < component->children.length(); c++) {
- detachFromWindow(component->children[c]);
- }
- }
- void VisualComponent::detachFromParent() {
- // Check if there's a parent component
- VisualComponent *parent = this->parent;
- if (parent != nullptr) {
- parent->childChanged = true;
- // Find the component to detach among the child components.
- for (int i = 0; i < parent->getChildCount(); i++) {
- Handle<VisualComponent> current = parent->children[i];
- if (current.getUnsafe() == this) {
- // Disconnect child from backend window.
- detachFromWindow(parent->children[i]);
- // Disconnect parent from child.
- current->parent = nullptr;
- // Disconnect child from parent.
- parent->children.remove(i);
- return;
- }
- }
- // Update indirect states.
- getRoot(this)->updateIndirectStates();
- // Any ongoing drag action will allow the component to get the mouse up event to finish transactions safely before being deleted by reference counting.
- // Otherwise it may break program logic or cause crashes.
- }
- }
- bool VisualComponent::hasChild(VisualComponent *child) const {
- for (int i = 0; i < this->getChildCount(); i++) {
- Handle<VisualComponent> current = this->children[i];
- if (current.getUnsafe() == child) {
- return true; // Found the component
- } else {
- if (current->hasChild(child)) {
- return true; // Found the component recursively
- }
- }
- }
- return false; // Could not find the component
- }
- bool VisualComponent::hasChild(Handle<VisualComponent> child) const {
- return this->hasChild(child.getUnsafe());
- }
- Handle<VisualComponent> VisualComponent::findChildByName(ReadableString name) const {
- for (int i = 0; i < this->getChildCount(); i++) {
- Handle<VisualComponent> current = this->children[i];
- if (string_match(current->getName(), name)) {
- return current; // Found the component
- } else {
- Handle<VisualComponent> searchResult = current->findChildByName(name);
- if (searchResult.isNotNull()) {
- return searchResult; // Found the component recursively
- }
- }
- }
- return Handle<VisualComponent>(); // Could not find the component
- }
- Handle<VisualComponent> VisualComponent::findChildByNameAndIndex(ReadableString name, int index) const {
- for (int i = 0; i < this->getChildCount(); i++) {
- Handle<VisualComponent> current = this->children[i];
- if (string_match(current->getName(), name) && current->getIndex() == index) {
- return current; // Found the component
- } else {
- Handle<VisualComponent> searchResult = current->findChildByNameAndIndex(name, index);
- if (searchResult.isNotNull()) {
- return searchResult; // Found the component recursively
- }
- }
- }
- return Handle<VisualComponent>(); // Could not find the component
- }
- bool VisualComponent::pointIsInside(const IVector2D& pixelPosition) {
- return pixelPosition.x > this->location.left() && pixelPosition.x < this->location.right()
- && pixelPosition.y > this->location.top() && pixelPosition.y < this->location.bottom();
- }
- bool VisualComponent::pointIsInsideOfOverlay(const IVector2D& pixelPosition) {
- return false;
- }
- bool VisualComponent::pointIsInsideOfHover(const IVector2D& pixelPosition) {
- return this->pointIsInside(pixelPosition);
- }
- // Non-recursive top-down search
- Handle<VisualComponent> VisualComponent::getDirectChild(const IVector2D& pixelPosition) {
- // Iterate child components in reverse drawing order
- for (int i = this->getChildCount() - 1; i >= 0; i--) {
- Handle<VisualComponent> currentChild = this->children[i];
- // Check if the point is inside the child component
- if (currentChild->getVisible() && currentChild->pointIsInside(pixelPosition)) {
- return currentChild;
- }
- }
- // Return nothing if the point missed all child components
- return Handle<VisualComponent>();
- }
- // Create a Handle to the component.
- Handle<VisualComponent> VisualComponent::getHandle() {
- #ifdef SAFE_POINTER_CHECKS
- return Handle<VisualComponent>(this, heap_getHeader(this)->allocationIdentity);
- #else
- return Handle<VisualComponent>(this);
- #endif
- }
- void VisualComponent::updateStateEvent(ComponentState oldState, ComponentState newState) {}
- void VisualComponent::updateIndirectStates() {
- // Call recursively for child components while checking what they contain.
- ComponentState childStates = 0;
- for (int i = this->getChildCount() - 1; i >= 0; i--) {
- this->children[i]->updateIndirectStates();
- childStates |= this->children[i]->currentState;
- }
- // Direct and indirect inheritance.
- ComponentState expectedIndirectStates = ((childStates & componentState_direct) << 1) | (childStates & componentState_indirect);
- this->currentState = (this->currentState & componentState_direct) | expectedIndirectStates;
- }
- void VisualComponent::sendNotifications() {
- // Call recursively for child components while checking what they contain.
- // Run the loop backwards, so that no components are missed when once is detached.
- for (int i = this->getChildCount() - 1; i >= 0; i--) {
- // Use a reference counted pointer to the child, so that it can be removed safely outside of custom events.
- Handle<VisualComponent> child = this->children[i];
- if (child->detach) {
- child->detach = false;
- child->detachFromParent();
- } else {
- child->sendNotifications();
- }
- }
- // Detect differences for all flags at once using bits in the integers.
- if (this->currentState != this->previousState) {
- updateStateEvent(this->previousState, this->currentState);
- this->previousState = this->currentState;
- }
- }
- // Find the topmost overlay by searching backwards with the parent last and returning a pointer to the component.
- // The point is relative to the upper left corner of component.
- static VisualComponent *getTopmostOverlay(VisualComponent *component, const IVector2D &point) {
- // Only visible component may show its overlay or child components.
- if (component->getVisible()) {
- // Go through child components in reverse draw order to stop when reaching the one that is visible.
- for (int i = component->getChildCount() - 1; i >= 0; i--) {
- VisualComponent *result = getTopmostOverlay(component->children[i].getUnsafe(), point - component->children[i]->location.upperLeft());
- if (result != nullptr) return result;
- }
- // Check itself behind child overlays.
- if (component->showingOverlay() && component->pointIsInsideOfOverlay(point + component->location.upperLeft())) {
- return component;
- } else {
- return nullptr;
- }
- } else {
- return nullptr;
- }
- }
- // Get the upper left corner of child relative to the upper left corner of parent.
- // If parent is null or not a parent of child, then child's offset is relative to the window's canvas.
- static IVector2D getTotalOffset(const VisualComponent *child, const VisualComponent *parent = nullptr) {
- IVector2D result;
- while ((child != nullptr) && (child != parent)) {
- result += child->location.upperLeft();
- child = child->parent;
- }
- return result;
- }
- // Remove its pointer to its child and the whole trail of focus.
- void VisualComponent::defocusChildren() {
- for (int i = 0; i < this->getChildCount(); i++) {
- this->children[i]->applyStateAndMask(~componentState_focus);
- }
- }
- void VisualComponent::addStateBits(ComponentState directStates, bool unique) {
- Handle<VisualComponent> root = getRoot(this);
- // Remove all focus in the window if unique.
- if (unique) root->applyStateAndMask(~directStates);
- // Apply state directly to itself and indirectly to parents.
- this->currentState |= directStates;
- // Update indirect states, so that parent components know what happens to their child components.
- root->updateIndirectStates();
- }
- void VisualComponent::removeStateBits(ComponentState directStates) {
- Handle<VisualComponent> root = getRoot(this);
- // Remove state directly from itself and indirectly from parents.
- this->currentState &= ~directStates;
- // Update indirect states, so that parent components know what happens to their child components.
- root->updateIndirectStates();
- }
- // Create a chain of pointers from the root to this component
- // Any focus pointers that are not along the chain will not count but work as a memory for when one of its parents get focus again.
- void VisualComponent::makeFocused() {
- this->addStateBits(componentState_focus, true);
- }
- void VisualComponent::hover() {
- this->addStateBits(componentState_hoverDirect, true);
- }
- void VisualComponent::leave() {
- this->removeStateBits(componentState_hoverDirect);
- }
- void VisualComponent::showOverlay() {
- this->addStateBits(componentState_showingOverlayDirect, false);
- }
- // When multiple components are allowed to have the direct flag set, one needs to clean it up like a tree.
- void VisualComponent::hideOverlay() {
- this->removeStateBits(componentState_showingOverlayDirect);
- }
- void VisualComponent::applyStateAndMask(ComponentState keepMask) {
- this->currentState &= keepMask;
- for (int i = 0; i < this->getChildCount(); i++) {
- this->children[i]->applyStateAndMask(keepMask);
- }
- }
- // Takes events with points relative to the upper left corner of the called component.
- void VisualComponent::sendMouseEvent(const MouseEvent& event, bool recursive) {
- if (this->parent == nullptr && !recursive) {
- // Use a combined bit mask for any state that needs to be reset at this time.
- this->applyStateAndMask(~(componentState_hover));
- // Update the layout if needed.
- this->updateChildLocations();
- }
- // Get the point of interaction within the component being sent to,
- // so that it can be used to find direct child components expressed
- // relative to their container's upper left corner.
- // If a button is pressed down, this method will try to grab a component to begin mouse interaction.
- // Grabbing with the dragComponent pointer makes sure that move and up events can be given even if the cursor moves outside of the component.
- VisualComponent *childComponent = nullptr;
- // Find the component to interact with.
- if (event.mouseEventType == MouseEventType::MouseDown || this->dragComponent.isNull()) {
- // Check the overlays first when getting mouse events to the root component.
- if (this->parent == nullptr) {
- childComponent = getTopmostOverlay(this, event.position);
- }
- // Check for direct child components for passing on the event recursively.
- // The sendMouseEvent method can be called recursively from a member of an overlay, so we can't know
- // which component is at the top without asking the components that manage interaction with their children.
- if (childComponent == nullptr && !this->managesChildren()) {
- Handle<VisualComponent> nextContainer = this->getDirectChild(event.position);
- if (nextContainer.isNotNull()) {
- childComponent = nextContainer.getUnsafe();
- }
- }
- } else if (dragComponent.isNotNull()) {
- // If we're grabbing a component, keep sending events to it.
- childComponent = this->dragComponent.getUnsafe();
- }
- // Grab any detected component on mouse down events.
- if (event.mouseEventType == MouseEventType::MouseDown && childComponent != nullptr) {
- childComponent->makeFocused();
- this->dragComponent = childComponent->getHandle();
- this->holdCount++;
- }
- // Send the signal to a child component or itself.
- if (childComponent != nullptr) {
- // Recalculate local offset through one or more levels of ownership.
- IVector2D offset = getTotalOffset(childComponent, this);
- MouseEvent localEvent = event;
- localEvent.position = event.position - offset;
- childComponent->sendMouseEvent(localEvent);
- } else {
- // If there is no child component found, interact directly with the parent.
- MouseEvent parentEvent = event;
- parentEvent.position += this->location.upperLeft();
- // Itself is directly hovered.
- if (pointIsInsideOfHover(parentEvent.position)) {
- this->hover();
- } else {
- this->leave();
- }
- // If the event receiver pass it on to child components, it can just reset the hover flags again.
- this->receiveMouseEvent(parentEvent);
- }
- // Release a component on mouse up.
- if (event.mouseEventType == MouseEventType::MouseUp) {
- this->holdCount--;
- if (this->holdCount <= 0) {
- this->dragComponent = Handle<VisualComponent>(); // Abort drag.
- // Reset when we had more up than down events, in case that the root panel was created with a button already pressed.
- this->holdCount = 0;
- }
- }
- // Once all focusing and defocusing with arbitrary callbacks is over, send the focus notifications to the components that actually changed focus.
- if (this->parent == nullptr && !recursive) {
- //Should not be needed if everything works. this->updateIndirectStates();
- this->sendNotifications();
- }
- }
- void VisualComponent::receiveMouseEvent(const MouseEvent& event) {
- if (event.mouseEventType == MouseEventType::MouseDown) {
- this->callback_mouseDownEvent(event);
- } else if (event.mouseEventType == MouseEventType::MouseUp) {
- this->callback_mouseUpEvent(event);
- } else if (event.mouseEventType == MouseEventType::MouseMove) {
- this->callback_mouseMoveEvent(event);
- } else if (event.mouseEventType == MouseEventType::Scroll) {
- this->callback_mouseScrollEvent(event);
- }
- }
- void VisualComponent::sendKeyboardEvent(const KeyboardEvent& event) {
- for (int i = 0; i < this->getChildCount(); i++) {
- ComponentState state = this->children[i]->currentState;
- if (state & componentState_focus) {
- if (state & componentState_focusDirect) {
- this->children[i]->receiveKeyboardEvent(event);
- } else if (state & componentState_focusIndirect) {
- this->children[i]->sendKeyboardEvent(event);
- }
- }
- }
- // Check for any state updates.
- if (this->parent == nullptr) {
- //Should not be needed if everything works. this->updateIndirectStates();
- this->sendNotifications();
- }
- }
- void VisualComponent::receiveKeyboardEvent(const KeyboardEvent& event) {
- if (event.keyboardEventType == KeyboardEventType::KeyDown) {
- this->callback_keyDownEvent(event);
- } else if (event.keyboardEventType == KeyboardEventType::KeyUp) {
- this->callback_keyUpEvent(event);
- } else if (event.keyboardEventType == KeyboardEventType::KeyType) {
- this->callback_keyTypeEvent(event);
- }
- }
- void VisualComponent::applyTheme(VisualTheme theme) {
- this->theme = theme;
- this->changedTheme(theme);
- for (int i = 0; i < this->getChildCount(); i++) {
- this->children[i]->applyTheme(theme);
- }
- }
- VisualTheme VisualComponent::getTheme() const {
- return this->theme;
- }
- void VisualComponent::changedTheme(VisualTheme newTheme) {}
- String VisualComponent::call(const ReadableString &methodName, const ReadableString &arguments) {
- sendWarning("Unimplemented custom call received");
- return U"";
- }
- bool VisualComponent::managesChildren() {
- return false;
- }
- MediaResult dsr::component_generateImage(VisualTheme theme, MediaMethod &method, int width, int height, int red, int green, int blue, int pressed, int focused, int hovered) {
- return method.callUsingKeywords([&theme, &method, width, height, red, green, blue, pressed, focused, hovered](MediaMachine &machine, int methodIndex, int inputIndex, const ReadableString &argumentName) {
- if (string_caseInsensitiveMatch(argumentName, U"width")) {
- machine_setInputByIndex(machine, methodIndex, inputIndex, width);
- } else if (string_caseInsensitiveMatch(argumentName, U"height")) {
- machine_setInputByIndex(machine, methodIndex, inputIndex, height);
- } else if (string_caseInsensitiveMatch(argumentName, U"pressed")) {
- machine_setInputByIndex(machine, methodIndex, inputIndex, pressed);
- } else if (string_caseInsensitiveMatch(argumentName, U"focused")) {
- machine_setInputByIndex(machine, methodIndex, inputIndex, focused);
- } else if (string_caseInsensitiveMatch(argumentName, U"hovered")) {
- machine_setInputByIndex(machine, methodIndex, inputIndex, hovered);
- } else if (string_caseInsensitiveMatch(argumentName, U"red")) {
- machine_setInputByIndex(machine, methodIndex, inputIndex, red);
- } else if (string_caseInsensitiveMatch(argumentName, U"green")) {
- machine_setInputByIndex(machine, methodIndex, inputIndex, green);
- } else if (string_caseInsensitiveMatch(argumentName, U"blue")) {
- machine_setInputByIndex(machine, methodIndex, inputIndex, blue);
- } else if (theme_assignMediaMachineArguments(theme, method.contextIndex, machine, methodIndex, inputIndex, argumentName)) {
- // Assigned by theme_assignMediaMachineArguments.
- } else {
- // TODO: Ask the theme for the argument using a specified style class for variations between different types of buttons, checkboxes, panels, et cetera.
- // Send a warning if the theme did not provide an input argument to its own media function.
- sendWarning(U"Unhandled setting \"", argumentName, U"\" requested by the media method \"", machine_getMethodName(machine, methodIndex), U"\" in the visual theme!\n");
- }
- });
- }
|