Merge branch 'main/rendor-staging' into fixes
[ryzomcore.git] / nel / src / gui / group_header.cpp
blob2671c2a62143f26d49b853fb741b5d0871ce4343
1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2013 Laszlo KIS-ADAM (dfighter) <dfighter1985@gmail.com>
6 // Copyright (C) 2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
7 //
8 // This program is free software: you can redistribute it and/or modify
9 // it under the terms of the GNU Affero General Public License as
10 // published by the Free Software Foundation, either version 3 of the
11 // License, or (at your option) any later version.
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU Affero General Public License for more details.
18 // You should have received a copy of the GNU Affero General Public License
19 // along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "stdpch.h"
23 #include "nel/gui/group_container.h"
24 #include "nel/gui/group_header.h"
25 #include "nel/gui/lua_ihm.h"
26 #include "nel/gui/widget_manager.h"
29 using namespace NLMISC;
31 #ifdef DEBUG_NEW
32 #define new DEBUG_NEW
33 #endif
35 namespace NLGUI
38 //////////////////
39 // CGroupHeader //
40 //////////////////
42 // *****************************************************************************************************************
43 CGroupHeader::CGroupHeader(const TCtorParam &param) : CGroupList(param), _HeaderMaxSize(32767)
47 // *****************************************************************************************************************
48 void CGroupHeader::enlargeColumns(sint32 margin)
50 std::vector<CGroupHeaderEntry *> entries;
51 getEntries(entries);
52 sint32 totalWidth = 0;
53 for (uint k = 0; k < entries.size(); ++k)
55 CInterfaceGroup *colEnclosing = entries[k]->getTargetColumn();
56 if (colEnclosing && !colEnclosing->getGroups().empty())
58 CInterfaceGroup *col = colEnclosing->getGroups()[0];
59 if (col)
61 // enlarge to the max to be able to measure the sub text (they may clamp themselves based
62 // on their first non-"child resizing" parent (see CViewText::updateCoords)
63 colEnclosing->setW(16384);
64 colEnclosing->invalidateCoords();
65 colEnclosing->updateCoords();
67 // assume that first child is resizing from its children width (either 'child_resize_w=true' or a CGroupList)
68 entries[k]->setW(std::max(entries[k]->getMinSize(), col->getW() + margin));
69 entries[k]->invalidateCoords();
70 totalWidth += entries[k]->getW();
74 // if total width bigger than allowed, reduce proportionnally
75 if (totalWidth > _HeaderMaxSize)
77 while (totalWidth > _HeaderMaxSize)
79 bool adjusted = false;
80 // stupid algo here, but ponctual ...
81 for (uint k = 0; k < entries.size() && totalWidth > _HeaderMaxSize; ++k)
83 if (entries[k]->getW() > entries[k]->getMinSize())
85 entries[k]->setW(entries[k]->getW() - 1);
86 entries[k]->invalidateCoords();
87 --totalWidth;
88 adjusted = true;
91 // if all at min size, just exit ...
92 if (!adjusted) break;
95 else
97 // search first parent that limit size, if it is larger then enlarge to fit size
98 CInterfaceGroup *limitingParent = getParent();
99 while (limitingParent && (limitingParent->getResizeFromChildW() || dynamic_cast<CGroupList *>(limitingParent)))
101 // NB nico : the dynamic_cast for CGroupList is bad!!
102 // can't avoid it for now, because, CGroupList implicitly does a "resize from child" in its update coords
103 // ...
104 limitingParent = limitingParent->getParent();
106 if (limitingParent && limitingParent->getWReal() > totalWidth)
108 while (limitingParent->getWReal() > totalWidth && totalWidth < _HeaderMaxSize)
110 // enlarge to matche parent size
111 // stupid algo here, but ponctual ...
112 for (uint k = 0; k < entries.size(); ++k)
114 entries[k]->setW(entries[k]->getW() + 1);
115 entries[k]->invalidateCoords();
116 ++totalWidth;
117 if (limitingParent->getWReal() <= totalWidth || totalWidth >= _HeaderMaxSize) break;
122 invalidateCoords();
125 // *****************************************************************************************************************
126 void CGroupHeader::resizeColumnsAndContainer(sint32 margin)
128 std::vector<CGroupHeaderEntry *> entries;
129 getEntries(entries);
130 sint32 totalWidth = 0;
131 for (uint k = 0; k < entries.size(); ++k)
133 CInterfaceGroup *colEnclosing = entries[k]->getTargetColumn();
134 if (colEnclosing && !colEnclosing->getGroups().empty())
136 CInterfaceGroup *col = colEnclosing->getGroups()[0];
137 if (col)
139 // enlarge to the max to be able to measure the sub text (they may clamp themselves based
140 // on their first non-"child resizing" parent (see CViewText::updateCoords)
141 colEnclosing->setW(16384);
142 colEnclosing->invalidateCoords();
143 colEnclosing->updateCoords();
145 // assume that first child is resizing from its children width (either 'child_resize_w=true' or a CGroupList)
146 entries[k]->setW(std::max(entries[k]->getMinSize(), col->getW() + margin));
147 entries[k]->invalidateCoords();
148 totalWidth += entries[k]->getW();
153 // resize W
154 if (totalWidth <= _HeaderMaxSize)
156 // search first parent that limit size, if it is larger then enlarge to fit size
157 CInterfaceGroup *limitingParent = getParent();
158 while (limitingParent && (limitingParent->getResizeFromChildW() || dynamic_cast<CGroupList *>(limitingParent)))
160 // NB nico : the dynamic_cast for CGroupList is bad!!
161 // can't avoid it for now, because, CGroupList implicitly does a "resize from child" in its update coords
162 // ...
163 limitingParent = limitingParent->getParent();
165 if (limitingParent)
167 getParentContainer()->setW(totalWidth + getParentContainer()->getWReal() - limitingParent->getWReal());
169 else
171 nlwarning("No limiting parent for width");
175 // resize H
176 if (!entries.empty())
178 CInterfaceGroup *colEnclosing = entries[0]->getTargetColumn();
179 if (colEnclosing && !colEnclosing->getGroups().empty())
181 CInterfaceGroup *col = colEnclosing->getGroups()[0];
182 if (col)
184 // search first parent that limit size, if it is larger then enlarge to fit size
185 CInterfaceGroup *limitingParent = colEnclosing->getParent();
186 while (limitingParent && (limitingParent->getResizeFromChildH() || dynamic_cast<CGroupList *>(limitingParent)))
187 limitingParent = limitingParent->getParent();
188 if (limitingParent)
190 getParentContainer()->setH(col->getH() + getParentContainer()->getHReal() - limitingParent->getHReal());
192 else
194 nlwarning("No limiting parent for height");
201 invalidateCoords();
204 // *****************************************************************************************************************
205 void CGroupHeader::getEntries(std::vector<CGroupHeaderEntry *> &dest)
207 dest.clear();
208 const std::vector<CInterfaceGroup*> &groups = getGroups();
209 for (uint k = 0; k < groups.size(); ++k)
211 CGroupHeaderEntry *entry = dynamic_cast<CGroupHeaderEntry *>(groups[k]);
212 if (entry)
214 dest.push_back(entry);
219 // *****************************************************************************************************************
220 int CGroupHeader::luaEnlargeColumns(CLuaState &ls)
222 const char *funcName = "enlargeColumns";
223 CLuaIHM::checkArgCount(ls, funcName, 1);
224 CLuaIHM::checkArgType(ls, funcName, 1, LUA_TNUMBER);
225 enlargeColumns((sint32) ls.toInteger(1));
226 return 0;
229 // *****************************************************************************************************************
230 int CGroupHeader::luaResizeColumnsAndContainer(CLuaState &ls)
232 const char *funcName = "resizeColumnsAndContainer";
233 CLuaIHM::checkArgCount(ls, funcName, 1);
234 CLuaIHM::checkArgType(ls, funcName, 1, LUA_TNUMBER);
235 resizeColumnsAndContainer((sint32) ls.toInteger(1));
236 return 0;
239 std::string CGroupHeader::getProperty( const std::string &name ) const
241 if( name == "header_max_size" )
243 return toString( _HeaderMaxSize );
245 else
246 return CGroupList::getProperty( name );
249 void CGroupHeader::setProperty( const std::string &name, const std::string &value )
251 if( name == "header_max_size" )
253 sint32 i;
254 if( fromString( value, i ) )
255 _HeaderMaxSize = i;
256 return;
258 else
259 return CGroupList::setProperty( name, value );
263 xmlNodePtr CGroupHeader::serialize( xmlNodePtr parentNode, const char *type ) const
265 xmlNodePtr node = CGroupList::serialize( parentNode, type );
266 if( node == NULL )
267 return NULL;
269 xmlSetProp( node, BAD_CAST "type", BAD_CAST "header" );
270 xmlSetProp( node, BAD_CAST "header_max_size", BAD_CAST toString( _HeaderMaxSize ).c_str() );
272 return node;
275 // *****************************************************************************************************************
276 bool CGroupHeader::parse(xmlNodePtr cur, CInterfaceGroup * parentGroup)
278 if(!CGroupList::parse(cur, parentGroup)) return false;
279 CXMLAutoPtr prop((const char*) xmlGetProp( cur, (xmlChar*)"header_max_size" ));
280 if (prop) fromString((const char*)prop, _HeaderMaxSize);
281 return true;
284 /////////////////////////
285 // CHeaderEntryResizer //
286 /////////////////////////
288 class CHeaderEntryResizer : public CCtrlBase
290 public:
291 CHeaderEntryResizer(bool rightSide, sint32 wMin) : CCtrlBase(TCtorParam()),
292 _RightSide(rightSide),
293 _Moving(false),
294 _StartX(0),
295 _OffsetX(0),
296 _WMin(wMin)
298 void release()
300 if (CWidgetManager::getInstance()->getCapturePointerLeft() == this)
302 _Moving = false;
303 CWidgetManager::getInstance()->setCapturePointerLeft(NULL);
306 virtual uint getDeltaDepth() const { return 100; }
307 CInterfaceGroup *getTargetGroup()
309 if (_RightSide) return _Parent;
311 if (getParent()->getParent() == _Parent->getParentPos()) return NULL; // leftmost header
312 return dynamic_cast<CInterfaceGroup *>(getParent()->getParentPos());
314 bool handleEvent (const NLGUI::CEventDescriptor &event)
316 if (_Parent)
318 if (event.getType() == NLGUI::CEventDescriptor::system)
320 const NLGUI::CEventDescriptorSystem &eds = (const NLGUI::CEventDescriptorSystem &) event;
321 if (eds.getEventTypeExtended() == NLGUI::CEventDescriptorSystem::setfocus)
323 const NLGUI::CEventDescriptorSetFocus &edsf = (const NLGUI::CEventDescriptorSetFocus &) eds;
324 if (edsf.hasFocus() == false)
326 release();
327 return CCtrlBase::handleEvent(event);
331 if (event.getType() == NLGUI::CEventDescriptor::mouse)
333 const NLGUI::CEventDescriptorMouse &eventDesc = (const NLGUI::CEventDescriptorMouse &)event;
334 if (eventDesc.getEventTypeExtended() == NLGUI::CEventDescriptorMouse::mouseleftdown)
336 if (!this->isIn(eventDesc.getX(), eventDesc.getY())) return false;
337 _TargetGroup = getTargetGroup();
338 if (!_TargetGroup) return false;
339 CWidgetManager::getInstance()->setCapturePointerLeft(this);
340 _Moving = true;
341 _OffsetX = _TargetGroup->getW() - eventDesc.getX();
342 return true;
344 if (eventDesc.getEventTypeExtended() == NLGUI::CEventDescriptorMouse::mouseleftup)
346 release();
348 if (eventDesc.getEventTypeExtended() == NLGUI::CEventDescriptorMouse::mousemove)
350 if (_Moving && CWidgetManager::getInstance()->getCapturePointerLeft() == this)
352 if (!_TargetGroup)
354 release();
355 return false;
357 sint32 newW = eventDesc.getX() + _OffsetX;
358 // compute width of all header entries but this one
359 CGroupHeader *header = dynamic_cast<CGroupHeader *>(getParent()->getParent());
360 if (header)
362 sint32 w = 0;
363 for (uint k = 0; k < header->getNumChildren(); ++k)
365 if (header->getChild(k) != _TargetGroup)
367 w += header->getChild(k)->getW();
370 sint32 excess = w + newW - header->getHeaderMaxSize();
371 if (excess)
373 // try to diminish the size of all headers starting from the last
374 for (sint k = header->getNumChildren() - 1; k >= 0 && excess > 0; --k)
376 if (header->getChild(k) == _TargetGroup) break;
377 CGroupHeaderEntry *ghe = dynamic_cast<CGroupHeaderEntry *>(header->getChild(k));
378 sint32 wGain = std::min(excess, std::max((sint32) 0, ghe->getW() - ghe->getMinSize()));
379 if (wGain > 0)
381 ghe->setW(ghe->getW() - wGain);
382 ghe->invalidateCoords();
383 excess -= wGain;
387 newW -= std::max((sint32) 0, excess);
389 _TargetGroup->setW(std::max(_WMin, newW));
390 _TargetGroup->invalidateCoords();
391 CGroupHeaderEntry *ghe = dynamic_cast<CGroupHeaderEntry *>((CInterfaceGroup *) _TargetGroup);
392 if (ghe)
394 ghe->setW(_TargetGroup->getW());
395 ghe->invalidateCoords();
396 CAHManager::getInstance()->runActionHandler(ghe->getAHOnResize(), ghe, ghe->getAHOnResizeParams());
398 return true;
400 _Moving = false;
404 return CCtrlBase::handleEvent(event);
406 virtual void draw ()
408 // no-op
410 virtual bool getMouseOverShape(std::string &texName, uint8 &rot, NLMISC::CRGBA &col)
413 if (!getTargetGroup()) return false;
414 texName = "curs_resize_LR.tga";
415 rot = 0;
416 col = CRGBA::White;
417 return true;
420 private:
421 NLMISC::CRefPtr<CInterfaceGroup> _TargetGroup; // group for which w is modified
422 bool _RightSide; // right or left side mover ?
423 bool _Moving;
424 sint32 _StartX; // value to add to mouse to get local x pos of target group
425 sint32 _OffsetX;
426 sint32 _WMin;
429 // *****************************************************************************************************************
430 CGroupHeaderEntry::CGroupHeaderEntry(const TCtorParam &param) : CInterfaceGroup(param)
432 _MinSize = 4;
433 _ResizerSize = 4;
436 xmlNodePtr CGroupHeaderEntry::serialize( xmlNodePtr parentNode, const char *type ) const
438 xmlNodePtr node = CInterfaceGroup::serialize( parentNode, type );
439 if( node == NULL )
440 return NULL;
442 xmlSetProp( node, BAD_CAST "wmin", BAD_CAST toString( _MinSize ).c_str() );
443 xmlSetProp( node, BAD_CAST "resizer_size", BAD_CAST toString( _ResizerSize ).c_str() );
444 xmlSetProp( node, BAD_CAST "target", BAD_CAST toString( _TargetColumnId ).c_str() );
445 xmlSetProp( node, BAD_CAST "on_resize", BAD_CAST _AHOnResize.c_str() );
446 xmlSetProp( node, BAD_CAST "on_resize_params", BAD_CAST _AHOnResizeParams.c_str() );
449 return node;
452 // *****************************************************************************************************************
453 bool CGroupHeaderEntry::parse(xmlNodePtr cur, CInterfaceGroup * parentGroup)
455 if (!CInterfaceGroup::parse(cur, parentGroup)) return false;
456 // left mover
457 CXMLAutoPtr prop((const char*) xmlGetProp( cur, (xmlChar*)"wmin" ));
458 if (prop) fromString((const char*)prop, _MinSize);
459 prop = (char*) xmlGetProp( cur, (xmlChar*)"resizer_size" );
460 if (prop) fromString((const char*)prop, _ResizerSize);
461 prop = (char*) xmlGetProp(cur, (xmlChar*) "target");
462 if (prop) _TargetColumnId = (const char *) prop;
464 prop = (char*) xmlGetProp(cur, (xmlChar*) "on_resize");
465 if (prop) _AHOnResize = (const char *) prop;
466 prop = (char*) xmlGetProp(cur, (xmlChar*) "on_resize_params");
467 if (prop) _AHOnResizeParams = (const char *) prop;
469 CHeaderEntryResizer *hm = new CHeaderEntryResizer(false, _MinSize);
470 addCtrl(hm);
471 hm->setW(_ResizerSize);
472 hm->setSizeRef(2);
473 hm->setParent(this);
474 hm->setParentPosRef(Hotspot_TL);
475 hm->setPosRef(Hotspot_TL);
476 // right mover
477 hm = new CHeaderEntryResizer(true, _MinSize);
478 addCtrl(hm);
479 hm->setW(_ResizerSize);
480 hm->setSizeRef(2);
481 hm->setParent(this);
482 hm->setParentPosRef(Hotspot_TR);
483 hm->setPosRef(Hotspot_TR);
485 return true;
488 // *****************************************************************************************************************
489 CInterfaceGroup *CGroupHeaderEntry::getTargetColumn() const
491 return dynamic_cast<CInterfaceGroup*>(CWidgetManager::getInstance()->getElementFromId(_TargetColumnId));
494 // *****************************************************************************************************************
495 void CGroupHeaderEntry::updateCoords()
497 CInterfaceGroup::updateCoords();
498 CInterfaceGroup *targetColumn = getTargetColumn();
499 if (targetColumn)
501 if (targetColumn->getW() != getW())
503 targetColumn->setW(getW());
504 targetColumn->invalidateCoords();
510 NLMISC_REGISTER_OBJECT(CViewBase, CGroupHeader, std::string, "header");
511 NLMISC_REGISTER_OBJECT(CViewBase, CGroupHeaderEntry, std::string, "header_entry");