Otclient  14/8/2020
uianchorlayout.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2010-2020 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 "uianchorlayout.h"
24 #include "uiwidget.h"
25 
26 UIWidgetPtr UIAnchor::getHookedWidget(const UIWidgetPtr& widget, const UIWidgetPtr& parentWidget)
27 {
28  // determine hooked widget
29  UIWidgetPtr hookedWidget;
30  if(parentWidget) {
31  if(m_hookedWidgetId == "parent")
32  hookedWidget = parentWidget;
33  else if(m_hookedWidgetId == "next")
34  hookedWidget = parentWidget->getChildAfter(widget);
35  else if(m_hookedWidgetId == "prev")
36  hookedWidget = parentWidget->getChildBefore(widget);
37  else
38  hookedWidget = parentWidget->getChildById(m_hookedWidgetId);
39  }
40  return hookedWidget;
41 }
42 
43 int UIAnchor::getHookedPoint(const UIWidgetPtr& hookedWidget, const UIWidgetPtr& parentWidget)
44 {
45  // determine hooked widget edge point
46  Rect hookedWidgetRect = hookedWidget->getRect();
47  if(hookedWidget == parentWidget)
48  hookedWidgetRect = parentWidget->getPaddingRect();
49 
50  int point = 0;
51  switch(m_hookedEdge) {
52  case Fw::AnchorLeft:
53  point = hookedWidgetRect.left();
54  break;
55  case Fw::AnchorRight:
56  point = hookedWidgetRect.right();
57  break;
58  case Fw::AnchorTop:
59  point = hookedWidgetRect.top();
60  break;
61  case Fw::AnchorBottom:
62  point = hookedWidgetRect.bottom();
63  break;
65  point = hookedWidgetRect.horizontalCenter();
66  break;
68  point = hookedWidgetRect.verticalCenter();
69  break;
70  default:
71  // must never happens
72  assert(false);
73  break;
74  }
75 
76  if(hookedWidget == parentWidget) {
77  switch(m_hookedEdge) {
78  case Fw::AnchorLeft:
79  case Fw::AnchorRight:
81  point -= parentWidget->getVirtualOffset().x;
82  break;
83  case Fw::AnchorBottom:
84  case Fw::AnchorTop:
86  point -= parentWidget->getVirtualOffset().y;
87  break;
88  default:
89  break;
90  }
91  }
92 
93  return point;
94 }
95 
97 {
98  // duplicated anchors must be replaced
99  for(UIAnchorPtr& other : m_anchors) {
100  if(other->getAnchoredEdge() == anchor->getAnchoredEdge()) {
101  other = anchor;
102  return;
103  }
104  }
105  m_anchors.push_back(anchor);
106 }
107 
108 void UIAnchorLayout::addAnchor(const UIWidgetPtr& anchoredWidget, Fw::AnchorEdge anchoredEdge,
109  const std::string& hookedWidgetId, Fw::AnchorEdge hookedEdge)
110 {
111  if(!anchoredWidget)
112  return;
113 
114  assert(anchoredWidget != getParentWidget());
115 
116  UIAnchorPtr anchor(new UIAnchor(anchoredEdge, hookedWidgetId, hookedEdge));
117  UIAnchorGroupPtr& anchorGroup = m_anchorsGroups[anchoredWidget];
118  if(!anchorGroup)
119  anchorGroup = UIAnchorGroupPtr(new UIAnchorGroup);
120 
121  anchorGroup->addAnchor(anchor);
122 
123  // layout must be updated because a new anchor got in
124  update();
125 }
126 
127 void UIAnchorLayout::removeAnchors(const UIWidgetPtr& anchoredWidget)
128 {
129  m_anchorsGroups.erase(anchoredWidget);
130  update();
131 }
132 
133 bool UIAnchorLayout::hasAnchors(const UIWidgetPtr& anchoredWidget)
134 {
135  return m_anchorsGroups.find(anchoredWidget) != m_anchorsGroups.end();
136 }
137 
138 void UIAnchorLayout::centerIn(const UIWidgetPtr& anchoredWidget, const std::string& hookedWidgetId)
139 {
140  addAnchor(anchoredWidget, Fw::AnchorHorizontalCenter, hookedWidgetId, Fw::AnchorHorizontalCenter);
141  addAnchor(anchoredWidget, Fw::AnchorVerticalCenter, hookedWidgetId, Fw::AnchorVerticalCenter);
142 }
143 
144 void UIAnchorLayout::fill(const UIWidgetPtr& anchoredWidget, const std::string& hookedWidgetId)
145 {
146  addAnchor(anchoredWidget, Fw::AnchorLeft, hookedWidgetId, Fw::AnchorLeft);
147  addAnchor(anchoredWidget, Fw::AnchorRight, hookedWidgetId, Fw::AnchorRight);
148  addAnchor(anchoredWidget, Fw::AnchorTop, hookedWidgetId, Fw::AnchorTop);
149  addAnchor(anchoredWidget, Fw::AnchorBottom, hookedWidgetId, Fw::AnchorBottom);
150 }
151 
153 {
154  update();
155 }
156 
158 {
159  removeAnchors(widget);
160 }
161 
162 bool UIAnchorLayout::updateWidget(const UIWidgetPtr& widget, const UIAnchorGroupPtr& anchorGroup, UIWidgetPtr first)
163 {
164  UIWidgetPtr parentWidget = getParentWidget();
165  if(!parentWidget)
166  return false;
167 
168  if(first == widget) {
169  g_logger.error(stdext::format("child '%s' of parent widget '%s' is recursively anchored to itself, please fix this", widget->getId(), parentWidget->getId()));
170  return false;
171  }
172 
173  if(!first)
174  first = widget;
175 
176  Rect newRect = widget->getRect();
177  bool verticalMoved = false;
178  bool horizontalMoved = false;
179 
180  // calculates new rect based on anchors
181  for(const UIAnchorPtr& anchor : anchorGroup->getAnchors()) {
182  // skip invalid anchors
183  if(anchor->getHookedEdge() == Fw::AnchorNone)
184  continue;
185 
186  // determine hooked widget
187  UIWidgetPtr hookedWidget = anchor->getHookedWidget(widget, parentWidget);
188 
189  // skip invalid anchors
190  if(!hookedWidget)
191  continue;
192 
193  if(hookedWidget != getParentWidget()) {
194  // update this hooked widget anchors
195  auto it = m_anchorsGroups.find(hookedWidget);
196  if(it != m_anchorsGroups.end()) {
197  const UIAnchorGroupPtr& hookedAnchorGroup = it->second;
198  if(!hookedAnchorGroup->isUpdated())
199  updateWidget(hookedWidget, hookedAnchorGroup, first);
200  }
201  }
202 
203  int point = anchor->getHookedPoint(hookedWidget, parentWidget);
204 
205  switch(anchor->getAnchoredEdge()) {
207  newRect.moveHorizontalCenter(point + widget->getMarginLeft() - widget->getMarginRight());
208  horizontalMoved = true;
209  break;
210  case Fw::AnchorLeft:
211  if(!horizontalMoved) {
212  newRect.moveLeft(point + widget->getMarginLeft());
213  horizontalMoved = true;
214  } else
215  newRect.setLeft(point + widget->getMarginLeft());
216  break;
217  case Fw::AnchorRight:
218  if(!horizontalMoved) {
219  newRect.moveRight(point - widget->getMarginRight());
220  horizontalMoved = true;
221  } else
222  newRect.setRight(point - widget->getMarginRight());
223  break;
225  newRect.moveVerticalCenter(point + widget->getMarginTop() - widget->getMarginBottom());
226  verticalMoved = true;
227  break;
228  case Fw::AnchorTop:
229  if(!verticalMoved) {
230  newRect.moveTop(point + widget->getMarginTop());
231  verticalMoved = true;
232  } else
233  newRect.setTop(point + widget->getMarginTop());
234  break;
235  case Fw::AnchorBottom:
236  if(!verticalMoved) {
237  newRect.moveBottom(point - widget->getMarginBottom());
238  verticalMoved = true;
239  } else
240  newRect.setBottom(point - widget->getMarginBottom());
241  break;
242  default:
243  break;
244  }
245  }
246 
247  bool changed = false;
248  if(widget->setRect(newRect))
249  changed = true;
250  anchorGroup->setUpdated(true);
251  return changed;
252 }
253 
255 {
256  bool changed = false;
257 
258  // reset all anchors groups update state
259  for(auto& it : m_anchorsGroups) {
260  const UIAnchorGroupPtr& anchorGroup = it.second;
261  anchorGroup->setUpdated(false);
262  }
263 
264  // update all anchors
265  for(auto& it : m_anchorsGroups) {
266  const UIWidgetPtr& widget = it.first;
267  const UIAnchorGroupPtr& anchorGroup = it.second;
268  if(!anchorGroup->isUpdated()) {
269  if(updateWidget(widget, anchorGroup))
270  changed = true;
271  }
272  }
273 
274  return changed;
275 }
UIWidget::getPaddingRect
Rect getPaddingRect()
Definition: uiwidget.cpp:1054
TRect::moveRight
void moveRight(T pos)
Definition: rect.h:106
TRect::moveHorizontalCenter
void moveHorizontalCenter(T x)
Definition: rect.h:130
TPoint::y
T y
Definition: point.h:83
TRect< int >
Fw::AnchorRight
@ AnchorRight
Definition: const.h:216
TRect::verticalCenter
T verticalCenter() const
Definition: rect.h:57
UIWidget::getMarginBottom
int getMarginBottom()
Definition: uiwidget.h:388
TRect::moveLeft
void moveLeft(T pos)
Definition: rect.h:104
UIAnchorLayout::hasAnchors
bool hasAnchors(const UIWidgetPtr &anchoredWidget)
Definition: uianchorlayout.cpp:133
UIWidget::getMarginRight
int getMarginRight()
Definition: uiwidget.h:387
UIWidget::getId
std::string getId()
Definition: uiwidget.h:254
TRect::left
T left() const
Definition: rect.h:52
Fw::AnchorEdge
AnchorEdge
Definition: const.h:211
UIAnchorGroup
Definition: uianchorlayout.h:46
Logger::error
void error(const std::string &what)
Definition: logger.h:54
TRect::setTop
void setTop(T pos)
Definition: rect.h:76
UIAnchorLayout::removeAnchors
void removeAnchors(const UIWidgetPtr &anchoredWidget)
Definition: uianchorlayout.cpp:127
Fw::AnchorLeft
@ AnchorLeft
Definition: const.h:215
TRect::bottom
T bottom() const
Definition: rect.h:55
TRect::moveBottom
void moveBottom(T pos)
Definition: rect.h:107
uiwidget.h
TRect::setLeft
void setLeft(T pos)
Definition: rect.h:75
stdext::format
std::string format()
Definition: format.h:82
UIAnchorLayout::centerIn
void centerIn(const UIWidgetPtr &anchoredWidget, const std::string &hookedWidgetId)
Definition: uianchorlayout.cpp:138
UIAnchor
Definition: uianchorlayout.h:28
UIWidget::getChildBefore
UIWidgetPtr getChildBefore(const UIWidgetPtr &relativeChild)
Definition: uiwidget.cpp:1121
UIWidget::getMarginLeft
int getMarginLeft()
Definition: uiwidget.h:389
TRect::setRight
void setRight(T pos)
Definition: rect.h:77
UIAnchorLayout::addWidget
void addWidget(const UIWidgetPtr &widget)
Definition: uianchorlayout.cpp:152
UIAnchor::m_hookedWidgetId
std::string m_hookedWidgetId
Definition: uianchorlayout.h:43
uianchorlayout.h
TRect::horizontalCenter
T horizontalCenter() const
Definition: rect.h:56
UIWidget::getMarginTop
int getMarginTop()
Definition: uiwidget.h:386
g_logger
Logger g_logger
Definition: logger.cpp:35
UIWidget::getChildById
UIWidgetPtr getChildById(const std::string &childId)
Definition: uiwidget.cpp:1129
UIAnchor::getHookedPoint
virtual int getHookedPoint(const UIWidgetPtr &hookedWidget, const UIWidgetPtr &parentWidget)
Definition: uianchorlayout.cpp:43
Fw::AnchorNone
@ AnchorNone
Definition: const.h:212
UIWidget::getVirtualOffset
Point getVirtualOffset()
Definition: uiwidget.h:266
UIAnchorLayout::fill
void fill(const UIWidgetPtr &anchoredWidget, const std::string &hookedWidgetId)
Definition: uianchorlayout.cpp:144
Fw::AnchorTop
@ AnchorTop
Definition: const.h:213
TPoint::x
T x
Definition: point.h:83
UILayout::update
void update()
Definition: uilayout.cpp:28
UILayout::getParentWidget
UIWidgetPtr getParentWidget()
Definition: uilayout.h:46
UIAnchorGroupPtr
stdext::shared_object_ptr< UIAnchorGroup > UIAnchorGroupPtr
Definition: declarations.h:50
TRect::moveVerticalCenter
void moveVerticalCenter(T y)
Definition: rect.h:135
UIAnchor::getHookedWidget
virtual UIWidgetPtr getHookedWidget(const UIWidgetPtr &widget, const UIWidgetPtr &parentWidget)
Definition: uianchorlayout.cpp:26
UIAnchorLayout::addAnchor
void addAnchor(const UIWidgetPtr &anchoredWidget, Fw::AnchorEdge anchoredEdge, const std::string &hookedWidgetId, Fw::AnchorEdge hookedEdge)
Definition: uianchorlayout.cpp:108
TRect::right
T right() const
Definition: rect.h:54
UIAnchorLayout::updateWidget
virtual bool updateWidget(const UIWidgetPtr &widget, const UIAnchorGroupPtr &anchorGroup, UIWidgetPtr first=nullptr)
Definition: uianchorlayout.cpp:162
stdext::shared_object_ptr< UIWidget >
UIAnchorLayout::removeWidget
void removeWidget(const UIWidgetPtr &widget)
Definition: uianchorlayout.cpp:157
TRect::moveTop
void moveTop(T pos)
Definition: rect.h:105
TRect::top
T top() const
Definition: rect.h:53
UIWidget::getChildAfter
UIWidgetPtr getChildAfter(const UIWidgetPtr &relativeChild)
Definition: uiwidget.cpp:1113
UIWidget::getRect
Rect getRect()
Definition: uiwidget.h:358
Fw::AnchorVerticalCenter
@ AnchorVerticalCenter
Definition: const.h:217
UIAnchorLayout::internalUpdate
virtual bool internalUpdate()
Definition: uianchorlayout.cpp:254
Fw::AnchorBottom
@ AnchorBottom
Definition: const.h:214
UIAnchorLayout::m_anchorsGroups
std::unordered_map< UIWidgetPtr, UIAnchorGroupPtr > m_anchorsGroups
Definition: uianchorlayout.h:82
UIAnchor::m_hookedEdge
Fw::AnchorEdge m_hookedEdge
Definition: uianchorlayout.h:42
UIWidget::setRect
bool setRect(const Rect &rect)
Definition: uiwidget.cpp:870
TRect::setBottom
void setBottom(T pos)
Definition: rect.h:78
UIAnchorGroup::addAnchor
void addAnchor(const UIAnchorPtr &anchor)
Definition: uianchorlayout.cpp:96
Fw::AnchorHorizontalCenter
@ AnchorHorizontalCenter
Definition: const.h:218