Added proper item spacing, item offsets and element boundary clipping to GridComponent.

This commit is contained in:
Leon Styhre 2022-11-13 22:31:41 +01:00
parent 75ebd839b9
commit 9013faf445
3 changed files with 75 additions and 17 deletions

View file

@ -164,8 +164,6 @@ std::map<std::string, std::map<std::string, ThemeData::ElementPropertyType>>
{"itemTransitions", STRING}, {"itemTransitions", STRING},
{"itemHorizontalAlignment", STRING}, {"itemHorizontalAlignment", STRING},
{"itemVerticalAlignment", STRING}, {"itemVerticalAlignment", STRING},
{"horizontalMargin", FLOAT},
{"verticalMargin", FLOAT},
{"horizontalOffset", FLOAT}, {"horizontalOffset", FLOAT},
{"verticalOffset", FLOAT}, {"verticalOffset", FLOAT},
{"unfocusedItemOpacity", FLOAT}, {"unfocusedItemOpacity", FLOAT},

View file

@ -101,6 +101,8 @@ private:
float mItemScale; float mItemScale;
glm::vec2 mItemSpacing; glm::vec2 mItemSpacing;
bool mInstantItemTransitions; bool mInstantItemTransitions;
float mHorizontalOffset;
float mVerticalOffset;
float mUnfocusedItemOpacity; float mUnfocusedItemOpacity;
unsigned int mTextColor; unsigned int mTextColor;
unsigned int mTextBackgroundColor; unsigned int mTextBackgroundColor;
@ -126,12 +128,13 @@ GridComponent<T>::GridComponent()
, mTransitionFactor {1.0f} , mTransitionFactor {1.0f}
, mFont {Font::get(FONT_SIZE_LARGE)} , mFont {Font::get(FONT_SIZE_LARGE)}
, mColumns {5} , mColumns {5}
, mItemSize {glm::vec2 {Renderer::getScreenWidth() * 0.15f, , mItemSize {glm::vec2 {mRenderer->getScreenWidth() * 0.15f,
Renderer::getScreenHeight() * 0.25f}} mRenderer->getScreenHeight() * 0.25f}}
, mItemScale {1.05f} , mItemScale {1.05f}
, mItemSpacing {glm::vec2 {Renderer::getScreenWidth() * 0.02f, , mItemSpacing {0.0f, 0.0f}
Renderer::getScreenHeight() * 0.02f}}
, mInstantItemTransitions {false} , mInstantItemTransitions {false}
, mHorizontalOffset {0.0f}
, mVerticalOffset {0.0f}
, mUnfocusedItemOpacity {1.0f} , mUnfocusedItemOpacity {1.0f}
, mTextColor {0x000000FF} , mTextColor {0x000000FF}
, mTextBackgroundColor {0xFFFFFF00} , mTextBackgroundColor {0xFFFFFF00}
@ -358,6 +361,14 @@ template <typename T> void GridComponent<T>::render(const glm::mat4& parentTrans
if (Settings::getInstance()->getBool("DebugImage")) if (Settings::getInstance()->getBool("DebugImage"))
mRenderer->drawRect(0.0f, 0.0f, mSize.x, mSize.y, 0x00FF0033, 0x00FF0033); mRenderer->drawRect(0.0f, 0.0f, mSize.x, mSize.y, 0x00FF0033, 0x00FF0033);
// Clip to element boundaries.
glm::vec3 dim {mSize.x, mSize.y, 0.0f};
dim.x = (trans[0].x * dim.x + trans[3].x) - trans[3].x;
dim.y = (trans[1].y * dim.y + trans[3].y) - trans[3].y;
mRenderer->pushClipRect(glm::ivec2 {static_cast<int>(trans[3].x), static_cast<int>(trans[3].y)},
glm::ivec2 {static_cast<int>(dim.x), static_cast<int>(dim.y)});
// We want to render the currently selected item last and before that the last selected // We want to render the currently selected item last and before that the last selected
// item to avoid incorrect overlapping in case the element has been configured with for // item to avoid incorrect overlapping in case the element has been configured with for
// example large scaling or small or no margins between items. // example large scaling or small or no margins between items.
@ -393,6 +404,7 @@ template <typename T> void GridComponent<T>::render(const glm::mat4& parentTrans
mEntries.at(*it).data.item->setOpacity(1.0f); mEntries.at(*it).data.item->setOpacity(1.0f);
} }
mRenderer->popClipRect();
GuiComponent::renderChildren(trans); GuiComponent::renderChildren(trans);
} }
@ -403,15 +415,21 @@ void GridComponent<T>::applyTheme(const std::shared_ptr<ThemeData>& theme,
unsigned int properties) unsigned int properties)
{ {
mSize.x = Renderer::getScreenWidth(); mSize.x = Renderer::getScreenWidth();
mSize.y = Renderer::getScreenHeight() * 0.8; mSize.y = Renderer::getScreenHeight() * 0.8f;
GuiComponent::mPosition.x = 0.0f; GuiComponent::mPosition.x = 0.0f;
GuiComponent::mPosition.y = Renderer::getScreenHeight() * 0.1; GuiComponent::mPosition.y = Renderer::getScreenHeight() * 0.1f;
mItemSpacing.x = ((mItemSize.x * mItemScale) - mItemSize.x) / 2.0f;
mItemSpacing.y = ((mItemSize.y * mItemScale) - mItemSize.y) / 2.0f;
mHorizontalOffset = ((mItemSize.x * mItemScale) - mItemSize.x) / 2.0f;
mVerticalOffset = ((mItemSize.y * mItemScale) - mItemSize.y) / 2.0f;
GuiComponent::applyTheme(theme, view, element, properties); GuiComponent::applyTheme(theme, view, element, properties);
using namespace ThemeFlags; using namespace ThemeFlags;
const ThemeData::ThemeElement* elem {theme->getElement(view, element, "grid")}; const ThemeData::ThemeElement* elem {theme->getElement(view, element, "grid")};
mSize = glm::round(mSize);
if (!elem) if (!elem)
return; return;
@ -419,7 +437,7 @@ void GridComponent<T>::applyTheme(const std::shared_ptr<ThemeData>& theme,
mColumns = glm::clamp(elem->get<unsigned int>("columns"), 0u, 100u); mColumns = glm::clamp(elem->get<unsigned int>("columns"), 0u, 100u);
if (elem->has("itemSize")) { if (elem->has("itemSize")) {
const glm::vec2 itemSize {glm::clamp(elem->get<glm::vec2>("itemSize"), 0.05f, 1.0f)}; const glm::vec2& itemSize {glm::clamp(elem->get<glm::vec2>("itemSize"), 0.05f, 1.0f)};
mItemSize = itemSize * glm::vec2(Renderer::getScreenWidth(), Renderer::getScreenHeight()); mItemSize = itemSize * glm::vec2(Renderer::getScreenWidth(), Renderer::getScreenHeight());
} }
@ -442,11 +460,50 @@ void GridComponent<T>::applyTheme(const std::shared_ptr<ThemeData>& theme,
} }
} }
// If itemSpacing is not defined, then it's automatically calculated so that scaled items
// don't overlap. If the property is present but one axis is defined as -1 then set this
// axis to the same pixel value as the other axis.
if (elem->has("itemSpacing")) { if (elem->has("itemSpacing")) {
const glm::vec2 itemSpacing {glm::clamp(elem->get<glm::vec2>("itemSpacing"), 0.0f, 0.1f)}; const glm::vec2& itemSpacing {elem->get<glm::vec2>("itemSpacing")};
mItemSpacing = if (itemSpacing.x == -1 && itemSpacing.y == -1) {
itemSpacing * glm::vec2(Renderer::getScreenWidth(), Renderer::getScreenHeight()); mItemSpacing = {0.0f, 0.0f};
}
else if (itemSpacing.x == -1) {
mItemSpacing.y = glm::clamp(itemSpacing.y, 0.0f, 0.1f) * mRenderer->getScreenHeight();
mItemSpacing.x = mItemSpacing.y;
}
else if (itemSpacing.y == -1) {
mItemSpacing.x = glm::clamp(itemSpacing.x, 0.0f, 0.1f) * mRenderer->getScreenWidth();
mItemSpacing.y = mItemSpacing.x;
}
else {
mItemSpacing = glm::clamp(itemSpacing, 0.0f, 0.1f) *
glm::vec2(Renderer::getScreenWidth(), Renderer::getScreenHeight());
}
} }
else if (mItemScale < 1.0f) {
mItemSpacing = glm::vec2 {0.0f, 0.0f};
}
else {
mItemSpacing.x = ((mItemSize.x * mItemScale) - mItemSize.x) / 2.0f;
mItemSpacing.y = ((mItemSize.y * mItemScale) - mItemSize.y) / 2.0f;
}
// If horizontalOffset or verticalOffset are not defined, then they are automatically
// calculated so that scaled items don't get clipped at grid boundaries.
if (elem->has("horizontalOffset"))
mHorizontalOffset = glm::clamp(elem->get<float>("horizontalOffset"), -0.5f, 0.5f) * mSize.x;
else if (mItemScale < 1.0f)
mHorizontalOffset = 0.0f;
else
mHorizontalOffset = ((mItemSize.x * mItemScale) - mItemSize.x) / 2.0f;
if (elem->has("verticalOffset"))
mVerticalOffset = glm::clamp(elem->get<float>("verticalOffset"), -0.5f, 0.5f) * mSize.y;
else if (mItemScale < 1.0f)
mVerticalOffset = 0.0f;
else
mVerticalOffset = ((mItemSize.y * mItemScale) - mItemSize.y) / 2.0f;
if (elem->has("unfocusedItemOpacity")) if (elem->has("unfocusedItemOpacity"))
mUnfocusedItemOpacity = glm::clamp(elem->get<float>("unfocusedItemOpacity"), 0.1f, 1.0f); mUnfocusedItemOpacity = glm::clamp(elem->get<float>("unfocusedItemOpacity"), 0.1f, 1.0f);
@ -556,9 +613,12 @@ template <typename T> void GridComponent<T>::calculateLayout()
unsigned int rowCount {0}; unsigned int rowCount {0};
for (auto& entry : mEntries) { for (auto& entry : mEntries) {
entry.data.item->setPosition(glm::vec3 { entry.data.item->setPosition(
(mItemSize.x * columnCount) + (mItemSize.x * 0.5f) + mItemSpacing.x * columnCount, glm::vec3 {mHorizontalOffset + (mItemSize.x * columnCount) + (mItemSize.x * 0.5f) +
(mItemSize.y * rowCount) + (mItemSize.y * 0.5f) + mItemSpacing.y * rowCount, 0.0f}); mItemSpacing.x * columnCount,
mVerticalOffset + (mItemSize.y * rowCount) + (mItemSize.y * 0.5f) +
mItemSpacing.y * rowCount,
0.0f});
if (columnCount == mColumns - 1) { if (columnCount == mColumns - 1) {
++rowCount; ++rowCount;
columnCount = 0; columnCount = 0;

View file

@ -485,7 +485,7 @@ void TextListComponent<T>::applyTheme(const std::shared_ptr<ThemeData>& theme,
mSize.x = Renderer::getScreenWidth(); mSize.x = Renderer::getScreenWidth();
mSize.y = Renderer::getScreenHeight() * 0.8f; mSize.y = Renderer::getScreenHeight() * 0.8f;
GuiComponent::mPosition.x = 0.0f; GuiComponent::mPosition.x = 0.0f;
GuiComponent::mPosition.y = Renderer::getScreenHeight() * 0.1; GuiComponent::mPosition.y = Renderer::getScreenHeight() * 0.1f;
setAlignment(PrimaryAlignment::ALIGN_LEFT); setAlignment(PrimaryAlignment::ALIGN_LEFT);
GuiComponent::applyTheme(theme, view, element, properties); GuiComponent::applyTheme(theme, view, element, properties);
@ -559,8 +559,8 @@ void TextListComponent<T>::applyTheme(const std::shared_ptr<ThemeData>& theme,
<< "\""; << "\"";
} }
} }
// Legacy themes only.
else if (elem->has("alignment")) { else if (elem->has("alignment")) {
// Legacy themes only.
const std::string& alignment {elem->get<std::string>("alignment")}; const std::string& alignment {elem->get<std::string>("alignment")};
if (alignment == "left") { if (alignment == "left") {
setAlignment(PrimaryAlignment::ALIGN_LEFT); setAlignment(PrimaryAlignment::ALIGN_LEFT);