1/*
2 * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient>
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 * THE SOFTWARE.
21 */
22
23#include "uigridlayout.h"
24#include "uiwidget.h"
25
26#include <framework/core/eventdispatcher.h>
27
28UIGridLayout::UIGridLayout(UIWidgetPtr parentWidget): UILayout(parentWidget)
29{
30 m_cellSize = Size(16,16);
31 m_cellSpacing = 0;
32 m_numColumns = 1;
33 m_numLines = 0;
34}
35
36void UIGridLayout::applyStyle(const OTMLNodePtr& styleNode)
37{
38 UILayout::applyStyle(styleNode);
39
40 for(const OTMLNodePtr& node : styleNode->children()) {
41 if(node->tag() == "cell-size")
42 setCellSize(node->value<Size>());
43 else if(node->tag() == "cell-width")
44 setCellWidth(node->value<int>());
45 else if(node->tag() == "cell-height")
46 setCellHeight(node->value<int>());
47 else if(node->tag() == "cell-spacing")
48 setCellSpacing(node->value<int>());
49 else if(node->tag() == "num-columns")
50 setNumColumns(node->value<int>());
51 else if(node->tag() == "num-lines")
52 setNumLines(node->value<int>());
53 else if(node->tag() == "fit-children")
54 setFitChildren(node->value<bool>());
55 else if(node->tag() == "auto-spacing")
56 setAutoSpacing(node->value<bool>());
57 else if(node->tag() == "flow")
58 setFlow(node->value<bool>());
59 }
60}
61
62void UIGridLayout::removeWidget(const UIWidgetPtr& widget)
63{
64 update();
65}
66
67void UIGridLayout::addWidget(const UIWidgetPtr& widget)
68{
69 update();
70}
71
72bool UIGridLayout::internalUpdate()
73{
74 bool changed = false;
75 UIWidgetPtr parentWidget = getParentWidget();
76 if(!parentWidget)
77 return false;
78
79 UIWidgetList widgets = parentWidget->getChildren();
80
81 Rect clippingRect = parentWidget->getPaddingRect();
82 Point topLeft = clippingRect.topLeft();
83
84 int numColumns = m_numColumns;
85 if(m_flow && m_cellSize.width() > 0) {
86 numColumns = (clippingRect.width() + m_cellSpacing) / (m_cellSize.width() + m_cellSpacing);
87 if(numColumns > 0) {
88 m_numColumns = numColumns;
89 m_numLines = std::ceil(widgets.size() / (float)numColumns);
90 }
91 }
92
93 if(numColumns <= 0)
94 numColumns = 1;
95
96 int cellSpacing = m_cellSpacing;
97 if(m_autoSpacing && numColumns > 1)
98 cellSpacing = (clippingRect.width() - numColumns * m_cellSize.width()) / (numColumns - 1);
99
100 int index = 0;
101 int preferredHeight = 0;
102 for(const UIWidgetPtr& widget : widgets) {
103 if(!widget->isExplicitlyVisible())
104 continue;
105
106 int line = index / numColumns;
107 int column = index % numColumns;
108 Point virtualPos = Point(column * (m_cellSize.width() + cellSpacing), line * (m_cellSize.height() + cellSpacing));
109 preferredHeight = virtualPos.y + m_cellSize.height();
110 Point pos = topLeft + virtualPos - parentWidget->getVirtualOffset();
111 Rect dest = Rect(pos, m_cellSize);
112 dest.expand(-widget->getMarginTop(), -widget->getMarginRight(), -widget->getMarginBottom(), -widget->getMarginLeft());
113
114 if(widget->setRect(dest))
115 changed = true;
116
117 index++;
118
119 if(m_numLines > 0 && index >= m_numColumns * m_numLines)
120 break;
121 }
122 preferredHeight += parentWidget->getPaddingTop() + parentWidget->getPaddingBottom();
123
124 if(m_fitChildren && preferredHeight != parentWidget->getHeight()) {
125 // must set the preferred height later
126 g_dispatcher.addEvent([=] {
127 parentWidget->setHeight(preferredHeight);
128 });
129 }
130
131 return changed;
132}
133