1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
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>
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/>.
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
;
42 // *****************************************************************************************************************
43 CGroupHeader::CGroupHeader(const TCtorParam
¶m
) : CGroupList(param
), _HeaderMaxSize(32767)
47 // *****************************************************************************************************************
48 void CGroupHeader::enlargeColumns(sint32 margin
)
50 std::vector
<CGroupHeaderEntry
*> 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];
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();
91 // if all at min size, just exit ...
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
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();
117 if (limitingParent
->getWReal() <= totalWidth
|| totalWidth
>= _HeaderMaxSize
) break;
125 // *****************************************************************************************************************
126 void CGroupHeader::resizeColumnsAndContainer(sint32 margin
)
128 std::vector
<CGroupHeaderEntry
*> 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];
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();
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
163 limitingParent
= limitingParent
->getParent();
167 getParentContainer()->setW(totalWidth
+ getParentContainer()->getWReal() - limitingParent
->getWReal());
171 nlwarning("No limiting parent for width");
176 if (!entries
.empty())
178 CInterfaceGroup
*colEnclosing
= entries
[0]->getTargetColumn();
179 if (colEnclosing
&& !colEnclosing
->getGroups().empty())
181 CInterfaceGroup
*col
= colEnclosing
->getGroups()[0];
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();
190 getParentContainer()->setH(col
->getH() + getParentContainer()->getHReal() - limitingParent
->getHReal());
194 nlwarning("No limiting parent for height");
204 // *****************************************************************************************************************
205 void CGroupHeader::getEntries(std::vector
<CGroupHeaderEntry
*> &dest
)
208 const std::vector
<CInterfaceGroup
*> &groups
= getGroups();
209 for (uint k
= 0; k
< groups
.size(); ++k
)
211 CGroupHeaderEntry
*entry
= dynamic_cast<CGroupHeaderEntry
*>(groups
[k
]);
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));
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));
239 std::string
CGroupHeader::getProperty( const std::string
&name
) const
241 if( name
== "header_max_size" )
243 return toString( _HeaderMaxSize
);
246 return CGroupList::getProperty( name
);
249 void CGroupHeader::setProperty( const std::string
&name
, const std::string
&value
)
251 if( name
== "header_max_size" )
254 if( fromString( value
, i
) )
259 return CGroupList::setProperty( name
, value
);
263 xmlNodePtr
CGroupHeader::serialize( xmlNodePtr parentNode
, const char *type
) const
265 xmlNodePtr node
= CGroupList::serialize( parentNode
, type
);
269 xmlSetProp( node
, BAD_CAST
"type", BAD_CAST
"header" );
270 xmlSetProp( node
, BAD_CAST
"header_max_size", BAD_CAST
toString( _HeaderMaxSize
).c_str() );
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
);
284 /////////////////////////
285 // CHeaderEntryResizer //
286 /////////////////////////
288 class CHeaderEntryResizer
: public CCtrlBase
291 CHeaderEntryResizer(bool rightSide
, sint32 wMin
) : CCtrlBase(TCtorParam()),
292 _RightSide(rightSide
),
300 if (CWidgetManager::getInstance()->getCapturePointerLeft() == this)
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
)
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)
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);
341 _OffsetX
= _TargetGroup
->getW() - eventDesc
.getX();
344 if (eventDesc
.getEventTypeExtended() == NLGUI::CEventDescriptorMouse::mouseleftup
)
348 if (eventDesc
.getEventTypeExtended() == NLGUI::CEventDescriptorMouse::mousemove
)
350 if (_Moving
&& CWidgetManager::getInstance()->getCapturePointerLeft() == this)
357 sint32 newW
= eventDesc
.getX() + _OffsetX
;
358 // compute width of all header entries but this one
359 CGroupHeader
*header
= dynamic_cast<CGroupHeader
*>(getParent()->getParent());
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();
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()));
381 ghe
->setW(ghe
->getW() - wGain
);
382 ghe
->invalidateCoords();
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
);
394 ghe
->setW(_TargetGroup
->getW());
395 ghe
->invalidateCoords();
396 CAHManager::getInstance()->runActionHandler(ghe
->getAHOnResize(), ghe
, ghe
->getAHOnResizeParams());
404 return CCtrlBase::handleEvent(event
);
410 virtual bool getMouseOverShape(std::string
&texName
, uint8
&rot
, NLMISC::CRGBA
&col
)
413 if (!getTargetGroup()) return false;
414 texName
= "curs_resize_LR.tga";
421 NLMISC::CRefPtr
<CInterfaceGroup
> _TargetGroup
; // group for which w is modified
422 bool _RightSide
; // right or left side mover ?
424 sint32 _StartX
; // value to add to mouse to get local x pos of target group
429 // *****************************************************************************************************************
430 CGroupHeaderEntry::CGroupHeaderEntry(const TCtorParam
¶m
) : CInterfaceGroup(param
)
436 xmlNodePtr
CGroupHeaderEntry::serialize( xmlNodePtr parentNode
, const char *type
) const
438 xmlNodePtr node
= CInterfaceGroup::serialize( parentNode
, type
);
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() );
452 // *****************************************************************************************************************
453 bool CGroupHeaderEntry::parse(xmlNodePtr cur
, CInterfaceGroup
* parentGroup
)
455 if (!CInterfaceGroup::parse(cur
, parentGroup
)) return false;
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
);
471 hm
->setW(_ResizerSize
);
474 hm
->setParentPosRef(Hotspot_TL
);
475 hm
->setPosRef(Hotspot_TL
);
477 hm
= new CHeaderEntryResizer(true, _MinSize
);
479 hm
->setW(_ResizerSize
);
482 hm
->setParentPosRef(Hotspot_TR
);
483 hm
->setPosRef(Hotspot_TR
);
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();
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");