AUI Framework  master
Cross-platform base for C++ UI apps
Loading...
Searching...
No Matches
HVLayout.h
    1/*
    2 * AUI Framework - Declarative UI toolkit for modern C++20
    3 * Copyright (C) 2020-2025 Alex2772 and Contributors
    4 *
    5 * SPDX-License-Identifier: MPL-2.0
    6 *
    7 * This Source Code Form is subject to the terms of the Mozilla Public
    8 * License, v. 2.0. If a copy of the MPL was not distributed with this
    9 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
   10 */
   11
   12#pragma once
   13
   14#include <AUI/Util/ALayoutDirection.h>
   15
   16namespace aui {
   17
   26template <ALayoutDirection direction>
   31    template <typename T>
   32    [[nodiscard]]
   33    static T& getAxisValue(glm::tvec2<T>& v) {
   34        return aui::layout_direction::getAxisValue(direction, v);
   35    }
   36
   40    template <typename T>
   41    [[nodiscard]]
   42    static T getAxisValue(const glm::tvec2<T>& v) {
   43        return aui::layout_direction::getAxisValue(direction, v);
   44    }
   45
   49    template <typename T>
   50    [[nodiscard]]
   51    static T& getPerpAxisValue(glm::tvec2<T>& v) {
   52        return aui::layout_direction::getPerpendicularAxisValue(direction, v);
   53    }
   54
   58    template <typename T>
   59    [[nodiscard]]
   60    static T getPerpAxisValue(const glm::tvec2<T>& v) {
   61        return aui::layout_direction::getPerpendicularAxisValue(direction, v);
   62    }
   63
   64    static void onResize(glm::ivec2 paddedPosition, glm::ivec2 paddedSize, ranges::range auto&& views, int spacing) {
   65        if (views.empty())
   66            return;
   67
   68        int sum = 0;
   69        int availableSpaceForExpandingViews = getAxisValue(paddedSize) + spacing;
   70
   71        // first phase: calculate sum and availableSpaceForExpandingViews
   72        for (const auto& view : views) {
   73            view->ensureAssUpdated();
   74            if (!(view->getVisibility() & Visibility::FLAG_CONSUME_SPACE))
   75                continue;
   76            int expanding = getAxisValue(view->getExpanding());
   77            int minSpace = getAxisValue(view->getMinimumSize());
   78            sum += expanding;
   79            if (expanding == 0 || getAxisValue(view->getFixedSize()) != 0)   // expanding view is fixed size is equal to
   80                                                                             // non-expanding
   81                availableSpaceForExpandingViews -= minSpace + getAxisValue(view->getMargin().occupiedSize()) + spacing;
   82            else
   83                availableSpaceForExpandingViews -= getAxisValue(view->getMargin().occupiedSize()) + spacing;
   84        }
   85
   86        bool containsExpandingItems = sum > 0;
   87
   88        sum = glm::max(sum, 1);
   89
   90        // second phase: validate availableSpaceForExpanding for expanding views with min size and max size
   91        if (containsExpandingItems) {
   92            for (const auto& view : views) {
   93                if (!(view->getVisibility() & Visibility::FLAG_CONSUME_SPACE))
   94                    continue;
   95
   96                int expanding = getAxisValue(view->getExpanding());
   97
   98                if (expanding == 0 || getAxisValue(view->getFixedSize()) != 0)   // expanding view is fixed size is
   99                                                                                 // equal to non-expanding
  100                    continue;
  101
  102                int spaceAcquiredByExpanding = availableSpaceForExpandingViews * expanding / sum;
  103                int viewMinSize = getAxisValue(view->getMinimumSize());
  104                auto viewMaxSize = view->getMaxSize();
  105                int validatedSpace = glm::clamp(spaceAcquiredByExpanding, viewMinSize, getAxisValue(viewMaxSize));
  106                availableSpaceForExpandingViews += (spaceAcquiredByExpanding - validatedSpace) * sum / expanding;
  107            }
  108        }
  109
  110        // third phase: apply layout to views
  111        int posOurAxis = getAxisValue(paddedPosition);
  112        const auto& last = views.back();
  113        for (const auto& view : views) {
  114            if (!(view->getVisibility() & Visibility::FLAG_CONSUME_SPACE))
  115                continue;
  116            auto margins = view->getMargin();
  117            auto viewMaxSize = view->getMaxSize();
  118
  119            int viewPosOurAxis = posOurAxis + getAxisValue(margins.leftTop());
  120            int viewPosPerpAxis = getPerpAxisValue(paddedPosition + margins.leftTop());
  121
  122            if (containsExpandingItems && view == last) {
  123                // the last element should stick right to the border.
  124                int viewSizeOurAxis =
  125                    getAxisValue(paddedSize) - viewPosOurAxis - getAxisValue(margins.rightBottom()) +
  126                    getAxisValue(paddedPosition);
  127                int viewSizePerpAxis = getPerpAxisValue(paddedSize - margins.occupiedSize());
  128
  129                view->setGeometry(
  130                    getAxisValue(glm::ivec2 { viewPosOurAxis, viewPosPerpAxis }),
  131                    getAxisValue(glm::ivec2 { viewPosPerpAxis, viewPosOurAxis }),
  132                    getAxisValue(glm::ivec2 { viewSizeOurAxis, viewSizePerpAxis }),
  133                    getAxisValue(glm::ivec2 { viewSizePerpAxis, viewSizeOurAxis }));
  134            } else {
  135                int expanding = getAxisValue(view->getExpanding());
  136                int viewMinSize = getAxisValue(view->getMinimumSize());
  137                int viewSizeOurAxis =
  138                    glm::clamp(availableSpaceForExpandingViews * expanding / sum, viewMinSize, getAxisValue(viewMaxSize));
  139                int viewSizePerpAxis =
  140                    glm::min(getPerpAxisValue(paddedSize - margins.occupiedSize()), getPerpAxisValue(viewMaxSize));
  141
  142                view->setGeometry(
  143                    getAxisValue(glm::ivec2 { viewPosOurAxis, viewPosPerpAxis }),
  144                    getAxisValue(glm::ivec2 { viewPosPerpAxis, viewPosOurAxis }),
  145                    getAxisValue(glm::ivec2 { viewSizeOurAxis, viewSizePerpAxis }),
  146                    getAxisValue(glm::ivec2 { viewSizePerpAxis, viewSizeOurAxis }));
  147
  148                posOurAxis += getAxisValue(view->getSize() + margins.occupiedSize()) + spacing;
  149                availableSpaceForExpandingViews += viewSizeOurAxis - getAxisValue(view->getSize());
  150            }
  151        }
  152    }
  153
  154    static int getMinimumWidth(ranges::range auto&& views, int spacing) {
  155        if constexpr (direction == ALayoutDirection::HORIZONTAL) {
  156            return getMinimumSizeOurAxis(views, spacing);
  157        }
  158        if constexpr (direction == ALayoutDirection::VERTICAL) {
  159            return getMinimumSizePerpAxis(views, spacing);
  160        }
  161    }
  162
  163    static int getMinimumHeight(ranges::range auto&& views, int spacing) {
  164        if constexpr (direction == ALayoutDirection::VERTICAL) {
  165            return getMinimumSizeOurAxis(views, spacing);
  166        }
  167        if constexpr (direction == ALayoutDirection::HORIZONTAL) {
  168            return getMinimumSizePerpAxis(views, spacing);
  169        }
  170    }
  171
  172private:
  173    static int getMinimumSizeOurAxis(ranges::range auto&& views, int spacing) {
  174        int minSize = -spacing;
  175
  176        for (const auto& v : views) {
  177            if (!(v->getVisibility() & Visibility::FLAG_CONSUME_SPACE))
  178                continue;
  179            minSize += getAxisValue(v->getMinimumSize() + v->getMargin().occupiedSize()) + spacing;
  180        }
  181
  182        return glm::max(minSize, 0);
  183    }
  184
  185    static int getMinimumSizePerpAxis(ranges::range auto&& views, int spacing) {
  186        int minSize = 0;
  187        for (const auto& v : views) {
  188            if (!(v->getVisibility() & Visibility::FLAG_CONSUME_SPACE))
  189                continue;
  190            auto h = getPerpAxisValue(v->getMinimumSize() + +v->getMargin().occupiedSize());
  191            minSize = glm::max(minSize, int(h));
  192        }
  193        return minSize;
  194    }
  195};
  196}   // namespace aui
Shared implementation of AVerticalLayout and AHorizontalLayout.
Definition HVLayout.h:27
static T & getPerpAxisValue(glm::tvec2< T > &v)
On direction == HORIZONTAL returns y; on direction == VERTICAL returns x.
Definition HVLayout.h:51
static T getAxisValue(const glm::tvec2< T > &v)
On direction == HORIZONTAL returns x; on direction == VERTICAL returns y.
Definition HVLayout.h:42
static T & getAxisValue(glm::tvec2< T > &v)
On direction == HORIZONTAL returns x; on direction == VERTICAL returns y.
Definition HVLayout.h:33
static T getPerpAxisValue(const glm::tvec2< T > &v)
On direction == HORIZONTAL returns y; on direction == VERTICAL returns x.
Definition HVLayout.h:60