2021-01-15 11:07:36 +01:00
/* Copyright (C) 2021 Wildfire Games.
2009-04-18 19:00:33 +02:00
* This file is part of 0 A . D .
*
* 0 A . D . is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 2 of the License , or
* ( at your option ) any later version .
*
* 0 A . D . is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with 0 A . D . If not , see < http : //www.gnu.org/licenses/>.
*/
2004-06-03 20:38:14 +02:00
# include "precompiled.h"
2004-06-15 23:08:05 +02:00
2019-09-18 22:51:45 +02:00
# include "CGUI.h"
2004-05-29 06:06:50 +02:00
2019-09-23 01:28:25 +02:00
# include "gui/IGUIScrollBar.h"
2021-05-12 15:48:55 +02:00
# include "gui/ObjectBases/IGUIObject.h"
2020-12-09 15:39:14 +01:00
# include "gui/ObjectTypes/CGUIDummyObject.h"
2020-11-21 18:49:06 +01:00
# include "gui/ObjectTypes/CTooltip.h"
2019-10-02 11:44:00 +02:00
# include "gui/Scripting/ScriptFunctions.h"
2020-12-13 16:06:18 +01:00
# include "gui/Scripting/JSInterface_GUIProxy.h"
2014-04-20 22:03:57 +02:00
# include "i18n/L10n.h"
2021-04-11 13:27:53 +02:00
# include "lib/allocators/DynamicArena.h"
# include "lib/allocators/STLAllocators.h"
2015-08-21 19:08:41 +02:00
# include "lib/bits.h"
# include "lib/input.h"
2007-12-20 21:21:45 +01:00
# include "lib/sysdep/sysdep.h"
2015-08-21 19:08:41 +02:00
# include "lib/timer.h"
2014-04-20 22:03:57 +02:00
# include "lib/utf8.h"
2012-01-29 21:04:21 +01:00
# include "ps/CLogger.h"
# include "ps/Filesystem.h"
2016-08-02 18:58:30 +02:00
# include "ps/GameSetup/Config.h"
2012-01-29 21:04:21 +01:00
# include "ps/Globals.h"
2015-08-21 19:08:41 +02:00
# include "ps/Hotkey.h"
2007-06-15 19:03:26 +02:00
# include "ps/Profile.h"
2012-01-29 21:04:21 +01:00
# include "ps/Pyrogenesis.h"
# include "ps/XML/Xeromyces.h"
2012-01-30 01:27:23 +01:00
# include "renderer/Renderer.h"
Improve JS Exception handling.
- Check for pending exceptions after function calls and script
executions.
- Call LOGERROR instead of JS_ReportError when there is a conversion
error in FromJSVal, since that can only be called from C++ (where JS
errors don't really make sense). Instead, C++ callers of FromJSVal
should handle the failure and, themselves, either report an error or
simply do something else.
- Wrap JS_ReportError since that makes updating it later easier.
This isn't a systematical fix since ToJSVal also ought return a boolean
for failures, and we probably should trigger errors instead of warnings
on 'implicit' conversions, rather a preparation diff.
Part of the SM52 migration, stage: SM45 compatible (actually SM52
incompatible, too).
Based on a patch by: Itms
Comments by: Vladislavbelov, Stan`
Refs #742, #4893
Differential Revision: https://code.wildfiregames.com/D3093
This was SVN commit r24187.
2020-11-15 19:29:17 +01:00
# include "scriptinterface/ScriptContext.h"
2010-11-17 00:00:52 +01:00
# include "scriptinterface/ScriptInterface.h"
2012-01-29 21:04:21 +01:00
2019-09-23 01:28:25 +02:00
# include <string>
2021-01-17 16:44:14 +01:00
# include <unordered_map>
2019-11-25 15:30:25 +01:00
# include <unordered_set>
2019-09-23 01:28:25 +02:00
2012-01-29 21:04:21 +01:00
extern int g_yres ;
2004-07-08 17:23:47 +02:00
2006-01-21 12:07:25 +01:00
const double SELECT_DBLCLICK_RATE = 0.5 ;
2015-06-01 02:29:23 +02:00
const u32 MAX_OBJECT_DEPTH = 100 ; // Max number of nesting for GUI includes. Used to detect recursive inclusion
2004-08-15 22:57:31 +02:00
2020-01-15 17:00:37 +01:00
const CStr CGUI : : EventNameLoad = " Load " ;
const CStr CGUI : : EventNameTick = " Tick " ;
const CStr CGUI : : EventNamePress = " Press " ;
2020-05-26 23:47:03 +02:00
const CStr CGUI : : EventNameKeyDown = " KeyDown " ;
2020-01-15 17:00:37 +01:00
const CStr CGUI : : EventNameRelease = " Release " ;
const CStr CGUI : : EventNameMouseRightPress = " MouseRightPress " ;
const CStr CGUI : : EventNameMouseLeftPress = " MouseLeftPress " ;
const CStr CGUI : : EventNameMouseWheelDown = " MouseWheelDown " ;
const CStr CGUI : : EventNameMouseWheelUp = " MouseWheelUp " ;
const CStr CGUI : : EventNameMouseLeftDoubleClick = " MouseLeftDoubleClick " ;
const CStr CGUI : : EventNameMouseLeftRelease = " MouseLeftRelease " ;
const CStr CGUI : : EventNameMouseRightDoubleClick = " MouseRightDoubleClick " ;
const CStr CGUI : : EventNameMouseRightRelease = " MouseRightRelease " ;
2021-04-11 13:27:53 +02:00
namespace
{
struct VisibleObject
{
IGUIObject * object ;
// Index of the object in a depth-first search inside GUI tree.
size_t index ;
// Cached value of GetBufferedZ to avoid recursive calls in a deep hierarchy.
float bufferedZ ;
} ;
template < class Container >
void CollectVisibleObjectsRecursively ( const std : : vector < IGUIObject * > & objects , Container * visibleObjects )
{
for ( IGUIObject * const & object : objects )
{
if ( ! object - > IsHidden ( ) )
{
visibleObjects - > emplace_back ( VisibleObject { object , visibleObjects - > size ( ) , 0.0f } ) ;
CollectVisibleObjectsRecursively ( object - > GetChildren ( ) , visibleObjects ) ;
}
}
}
} // anonynous namespace
2020-11-14 11:57:50 +01:00
CGUI : : CGUI ( const shared_ptr < ScriptContext > & context )
2021-02-14 00:53:40 +01:00
: m_BaseObject ( std : : make_unique < CGUIDummyObject > ( * this ) ) ,
2019-11-24 11:26:48 +01:00
m_FocusedObject ( nullptr ) ,
m_InternalNameNumber ( 0 ) ,
m_MouseButtons ( 0 )
{
2021-04-11 13:27:53 +02:00
m_ScriptInterface = std : : make_shared < ScriptInterface > ( " Engine " , " GUIPage " , context ) ;
2019-11-24 11:26:48 +01:00
m_ScriptInterface - > SetCallbackData ( this ) ;
GuiScriptingInit ( * m_ScriptInterface ) ;
m_ScriptInterface - > LoadGlobalScripts ( ) ;
}
CGUI : : ~ CGUI ( )
{
2020-12-31 15:27:02 +01:00
for ( const std : : pair < const CStr , IGUIObject * > & p : m_pAllObjects )
2019-11-24 11:26:48 +01:00
delete p . second ;
2020-12-31 15:27:02 +01:00
for ( const std : : pair < const CStr , const CGUISprite * > & p : m_Sprites )
2019-11-24 11:26:48 +01:00
delete p . second ;
}
2006-08-26 23:52:18 +02:00
InReaction CGUI : : HandleEvent ( const SDL_Event_ * ev )
2003-11-03 17:22:45 +01:00
{
2005-10-20 17:27:39 +02:00
InReaction ret = IN_PASS ;
2004-10-14 12:09:26 +02:00
2020-05-26 23:47:03 +02:00
if ( ev - > ev . type = = SDL_HOTKEYDOWN | | ev - > ev . type = = SDL_HOTKEYPRESS | | ev - > ev . type = = SDL_HOTKEYUP )
2004-07-22 18:18:12 +02:00
{
2010-10-23 04:37:00 +02:00
const char * hotkey = static_cast < const char * > ( ev - > ev . user . data1 ) ;
2016-12-07 20:18:57 +01:00
2020-05-26 23:47:03 +02:00
const CStr & eventName = ev - > ev . type = = SDL_HOTKEYPRESS ? EventNamePress : ev - > ev . type = = SDL_HOTKEYDOWN ? EventNameKeyDown : EventNameRelease ;
if ( m_GlobalHotkeys . find ( hotkey ) ! = m_GlobalHotkeys . end ( ) & & m_GlobalHotkeys [ hotkey ] . find ( eventName ) ! = m_GlobalHotkeys [ hotkey ] . end ( ) )
2019-09-05 12:42:16 +02:00
{
ret = IN_HANDLED ;
Improve JS Exception handling.
- Check for pending exceptions after function calls and script
executions.
- Call LOGERROR instead of JS_ReportError when there is a conversion
error in FromJSVal, since that can only be called from C++ (where JS
errors don't really make sense). Instead, C++ callers of FromJSVal
should handle the failure and, themselves, either report an error or
simply do something else.
- Wrap JS_ReportError since that makes updating it later easier.
This isn't a systematical fix since ToJSVal also ought return a boolean
for failures, and we probably should trigger errors instead of warnings
on 'implicit' conversions, rather a preparation diff.
Part of the SM52 migration, stage: SM45 compatible (actually SM52
incompatible, too).
Based on a patch by: Itms
Comments by: Vladislavbelov, Stan`
Refs #742, #4893
Differential Revision: https://code.wildfiregames.com/D3093
This was SVN commit r24187.
2020-11-15 19:29:17 +01:00
ScriptRequest rq ( m_ScriptInterface ) ;
2020-11-14 09:46:32 +01:00
JS : : RootedObject globalObj ( rq . cx , rq . glob ) ;
2020-11-13 14:18:22 +01:00
JS : : RootedValue result ( rq . cx ) ;
Improve JS Exception handling.
- Check for pending exceptions after function calls and script
executions.
- Call LOGERROR instead of JS_ReportError when there is a conversion
error in FromJSVal, since that can only be called from C++ (where JS
errors don't really make sense). Instead, C++ callers of FromJSVal
should handle the failure and, themselves, either report an error or
simply do something else.
- Wrap JS_ReportError since that makes updating it later easier.
This isn't a systematical fix since ToJSVal also ought return a boolean
for failures, and we probably should trigger errors instead of warnings
on 'implicit' conversions, rather a preparation diff.
Part of the SM52 migration, stage: SM45 compatible (actually SM52
incompatible, too).
Based on a patch by: Itms
Comments by: Vladislavbelov, Stan`
Refs #742, #4893
Differential Revision: https://code.wildfiregames.com/D3093
This was SVN commit r24187.
2020-11-15 19:29:17 +01:00
if ( ! JS_CallFunctionValue ( rq . cx , globalObj , m_GlobalHotkeys [ hotkey ] [ eventName ] , JS : : HandleValueArray : : empty ( ) , & result ) )
ScriptException : : CatchPending ( rq ) ;
2019-09-05 12:42:16 +02:00
}
2010-10-23 04:37:00 +02:00
std : : map < CStr , std : : vector < IGUIObject * > > : : iterator it = m_HotkeyObjects . find ( hotkey ) ;
if ( it ! = m_HotkeyObjects . end ( ) )
2015-08-21 19:08:41 +02:00
for ( IGUIObject * const & obj : it - > second )
2016-12-07 20:18:57 +01:00
{
2020-05-26 23:47:03 +02:00
if ( ev - > ev . type = = SDL_HOTKEYPRESS )
2020-01-15 17:00:37 +01:00
ret = obj - > SendEvent ( GUIM_PRESSED , EventNamePress ) ;
2020-05-26 23:47:03 +02:00
else if ( ev - > ev . type = = SDL_HOTKEYDOWN )
ret = obj - > SendEvent ( GUIM_KEYDOWN , EventNameKeyDown ) ;
2016-12-07 20:18:57 +01:00
else
2020-01-15 17:00:37 +01:00
ret = obj - > SendEvent ( GUIM_RELEASED , EventNameRelease ) ;
2016-12-07 20:18:57 +01:00
}
2004-07-22 18:18:12 +02:00
}
2006-08-26 23:52:18 +02:00
else if ( ev - > ev . type = = SDL_MOUSEMOTION )
2003-12-27 07:26:03 +01:00
{
2004-09-03 07:48:47 +02:00
// Yes the mouse position is stored as float to avoid
2005-10-20 19:44:56 +02:00
// constant conversions when operating in a
2004-09-03 07:48:47 +02:00
// float-based environment.
2021-03-28 23:55:13 +02:00
m_MousePos = CVector2D ( ( float ) ev - > ev . motion . x / g_GuiScale , ( float ) ev - > ev . motion . y / g_GuiScale ) ;
2003-11-03 17:22:45 +01:00
2011-04-28 22:42:11 +02:00
SGUIMessage msg ( GUIM_MOUSE_MOTION ) ;
2020-12-09 15:39:14 +01:00
m_BaseObject - > RecurseObject ( & IGUIObject : : IsHiddenOrGhost , & IGUIObject : : HandleMessage , msg ) ;
2003-12-27 07:26:03 +01:00
}
2004-07-08 17:23:47 +02:00
// Update m_MouseButtons. (BUTTONUP is handled later.)
2006-08-26 23:52:18 +02:00
else if ( ev - > ev . type = = SDL_MOUSEBUTTONDOWN )
2003-12-27 07:26:03 +01:00
{
2006-08-26 23:52:18 +02:00
switch ( ev - > ev . button . button )
2005-10-24 23:10:45 +02:00
{
case SDL_BUTTON_LEFT :
case SDL_BUTTON_RIGHT :
case SDL_BUTTON_MIDDLE :
2008-09-06 23:15:53 +02:00
m_MouseButtons | = Bit < unsigned int > ( ev - > ev . button . button ) ;
2005-10-24 23:10:45 +02:00
break ;
default :
break ;
}
2003-12-27 07:26:03 +01:00
}
2010-11-14 23:47:39 +01:00
// Update m_MousePos (for delayed mouse button events)
2021-03-28 23:55:13 +02:00
CVector2D oldMousePos = m_MousePos ;
2010-11-14 23:47:39 +01:00
if ( ev - > ev . type = = SDL_MOUSEBUTTONDOWN | | ev - > ev . type = = SDL_MOUSEBUTTONUP )
{
2021-03-28 23:55:13 +02:00
m_MousePos = CVector2D ( ( float ) ev - > ev . button . x / g_GuiScale , ( float ) ev - > ev . button . y / g_GuiScale ) ;
2010-11-14 23:47:39 +01:00
}
Let players remap hotkeys in-game, fix default hotkeys being qwerty-specific.
- Provide a "Hotkey" screen to let players remap hotkeys in-game using a
convenient setup.
- Make all .cfg hotkeys refer to scancodes (i.e. position on the
keyboard), so that default hotkeys now translate correctly for AZERTY,
QWERTZ and other layouts.
- 'BackSpace' is now an alias for 'Delete', and works for killing units.
This fixes #1917, as macs don't have a proper delete key and would need
to use Fn+Del otherwise. This shifts "timewarp" to Shift+BackSpace.
Functionally, this switches hotkeys to scancodes, as that makes more
sense (they are combinations of key positions, not actual text output).
SDL includes are cleaned and key names are reused.
Fixes #2850, Fixes #2604, Refs #1810, Fixes #1917.
Follows work in 3d7784d2af.
Various diffs tested by: Angen, Stan, Freagarach
Comments by: Nescio
Differential Revision: https://code.wildfiregames.com/D2814
This was SVN commit r24215.
2020-11-19 10:27:26 +01:00
// Allow the focused object to pre-empt regular GUI events.
if ( GetFocusedObject ( ) )
ret = GetFocusedObject ( ) - > PreemptEvent ( ev ) ;
2003-11-03 17:22:45 +01:00
// Only one object can be hovered
Let players remap hotkeys in-game, fix default hotkeys being qwerty-specific.
- Provide a "Hotkey" screen to let players remap hotkeys in-game using a
convenient setup.
- Make all .cfg hotkeys refer to scancodes (i.e. position on the
keyboard), so that default hotkeys now translate correctly for AZERTY,
QWERTZ and other layouts.
- 'BackSpace' is now an alias for 'Delete', and works for killing units.
This fixes #1917, as macs don't have a proper delete key and would need
to use Fn+Del otherwise. This shifts "timewarp" to Shift+BackSpace.
Functionally, this switches hotkeys to scancodes, as that makes more
sense (they are combinations of key positions, not actual text output).
SDL includes are cleaned and key names are reused.
Fixes #2850, Fixes #2604, Refs #1810, Fixes #1917.
Follows work in 3d7784d2af.
Various diffs tested by: Angen, Stan, Freagarach
Comments by: Nescio
Differential Revision: https://code.wildfiregames.com/D2814
This was SVN commit r24215.
2020-11-19 10:27:26 +01:00
// pNearest will after this point at the hovered object, possibly nullptr
IGUIObject * pNearest = FindObjectUnderMouse ( ) ;
2003-11-03 17:22:45 +01:00
Let players remap hotkeys in-game, fix default hotkeys being qwerty-specific.
- Provide a "Hotkey" screen to let players remap hotkeys in-game using a
convenient setup.
- Make all .cfg hotkeys refer to scancodes (i.e. position on the
keyboard), so that default hotkeys now translate correctly for AZERTY,
QWERTZ and other layouts.
- 'BackSpace' is now an alias for 'Delete', and works for killing units.
This fixes #1917, as macs don't have a proper delete key and would need
to use Fn+Del otherwise. This shifts "timewarp" to Shift+BackSpace.
Functionally, this switches hotkeys to scancodes, as that makes more
sense (they are combinations of key positions, not actual text output).
SDL includes are cleaned and key names are reused.
Fixes #2850, Fixes #2604, Refs #1810, Fixes #1917.
Follows work in 3d7784d2af.
Various diffs tested by: Angen, Stan, Freagarach
Comments by: Nescio
Differential Revision: https://code.wildfiregames.com/D2814
This was SVN commit r24215.
2020-11-19 10:27:26 +01:00
if ( ret = = IN_PASS )
2003-11-03 17:22:45 +01:00
{
2003-11-25 03:47:12 +01:00
// Now we'll call UpdateMouseOver on *all* objects,
Let players remap hotkeys in-game, fix default hotkeys being qwerty-specific.
- Provide a "Hotkey" screen to let players remap hotkeys in-game using a
convenient setup.
- Make all .cfg hotkeys refer to scancodes (i.e. position on the
keyboard), so that default hotkeys now translate correctly for AZERTY,
QWERTZ and other layouts.
- 'BackSpace' is now an alias for 'Delete', and works for killing units.
This fixes #1917, as macs don't have a proper delete key and would need
to use Fn+Del otherwise. This shifts "timewarp" to Shift+BackSpace.
Functionally, this switches hotkeys to scancodes, as that makes more
sense (they are combinations of key positions, not actual text output).
SDL includes are cleaned and key names are reused.
Fixes #2850, Fixes #2604, Refs #1810, Fixes #1917.
Follows work in 3d7784d2af.
Various diffs tested by: Angen, Stan, Freagarach
Comments by: Nescio
Differential Revision: https://code.wildfiregames.com/D2814
This was SVN commit r24215.
2020-11-19 10:27:26 +01:00
// we'll input the one hovered, and they will each
// update their own data and send messages accordingly
2020-12-09 15:39:14 +01:00
m_BaseObject - > RecurseObject ( & IGUIObject : : IsHiddenOrGhost , & IGUIObject : : UpdateMouseOver , static_cast < IGUIObject * const & > ( pNearest ) ) ;
2003-11-25 03:47:12 +01:00
2006-08-26 23:52:18 +02:00
if ( ev - > ev . type = = SDL_MOUSEBUTTONDOWN )
2003-11-25 03:47:12 +01:00
{
2006-08-26 23:52:18 +02:00
switch ( ev - > ev . button . button )
2003-11-25 03:47:12 +01:00
{
2004-05-29 06:06:50 +02:00
case SDL_BUTTON_LEFT :
2010-08-13 15:50:03 +02:00
// Focus the clicked object (or focus none if nothing clicked on)
SetFocusedObject ( pNearest ) ;
2004-05-29 06:06:50 +02:00
if ( pNearest )
2020-01-15 17:00:37 +01:00
ret = pNearest - > SendMouseEvent ( GUIM_MOUSE_PRESS_LEFT , EventNameMouseLeftPress ) ;
2004-07-11 18:22:35 +02:00
break ;
2004-05-29 06:06:50 +02:00
2010-08-22 01:58:08 +02:00
case SDL_BUTTON_RIGHT :
if ( pNearest )
2020-01-15 17:00:37 +01:00
ret = pNearest - > SendMouseEvent ( GUIM_MOUSE_PRESS_RIGHT , EventNameMouseRightPress ) ;
2010-08-22 01:58:08 +02:00
break ;
2004-05-29 06:06:50 +02:00
default :
break ;
}
}
2016-06-14 17:06:39 +02:00
else if ( ev - > ev . type = = SDL_MOUSEWHEEL & & pNearest )
2012-02-06 23:47:35 +01:00
{
if ( ev - > ev . wheel . y < 0 )
2020-01-15 17:00:37 +01:00
ret = pNearest - > SendMouseEvent ( GUIM_MOUSE_WHEEL_DOWN , EventNameMouseWheelDown ) ;
2012-02-06 23:47:35 +01:00
else if ( ev - > ev . wheel . y > 0 )
2020-01-15 17:00:37 +01:00
ret = pNearest - > SendMouseEvent ( GUIM_MOUSE_WHEEL_UP , EventNameMouseWheelUp ) ;
2012-02-06 23:47:35 +01:00
}
else if ( ev - > ev . type = = SDL_MOUSEBUTTONUP )
2004-05-29 06:06:50 +02:00
{
2006-08-26 23:52:18 +02:00
switch ( ev - > ev . button . button )
2004-05-29 06:06:50 +02:00
{
2004-12-21 14:37:24 +01:00
case SDL_BUTTON_LEFT :
2003-12-27 07:26:03 +01:00
if ( pNearest )
2004-07-11 18:22:35 +02:00
{
2008-01-07 21:03:19 +01:00
double timeElapsed = timer_Time ( ) - pNearest - > m_LastClickTime [ SDL_BUTTON_LEFT ] ;
pNearest - > m_LastClickTime [ SDL_BUTTON_LEFT ] = timer_Time ( ) ;
2006-01-21 12:07:25 +01:00
if ( timeElapsed < SELECT_DBLCLICK_RATE )
2020-01-15 17:00:37 +01:00
ret = pNearest - > SendMouseEvent ( GUIM_MOUSE_DBLCLICK_LEFT , EventNameMouseLeftDoubleClick ) ;
2006-01-21 12:07:25 +01:00
else
2020-01-15 17:00:37 +01:00
ret = pNearest - > SendMouseEvent ( GUIM_MOUSE_RELEASE_LEFT , EventNameMouseLeftRelease ) ;
2006-01-21 12:07:25 +01:00
}
break ;
case SDL_BUTTON_RIGHT :
if ( pNearest )
{
2008-01-07 21:03:19 +01:00
double timeElapsed = timer_Time ( ) - pNearest - > m_LastClickTime [ SDL_BUTTON_RIGHT ] ;
pNearest - > m_LastClickTime [ SDL_BUTTON_RIGHT ] = timer_Time ( ) ;
2006-01-21 12:07:25 +01:00
if ( timeElapsed < SELECT_DBLCLICK_RATE )
2020-01-15 17:00:37 +01:00
ret = pNearest - > SendMouseEvent ( GUIM_MOUSE_DBLCLICK_RIGHT , EventNameMouseRightDoubleClick ) ;
2006-01-21 12:07:25 +01:00
else
2020-01-15 17:00:37 +01:00
ret = pNearest - > SendMouseEvent ( GUIM_MOUSE_RELEASE_RIGHT , EventNameMouseRightRelease ) ;
2004-07-11 18:22:35 +02:00
}
2004-12-21 14:37:24 +01:00
break ;
2003-12-27 07:26:03 +01:00
}
2004-05-29 06:06:50 +02:00
// Reset all states on all visible objects
2020-12-09 15:39:14 +01:00
m_BaseObject - > RecurseObject ( & IGUIObject : : IsHidden , & IGUIObject : : ResetStates ) ;
2004-05-29 06:06:50 +02:00
2014-03-07 23:43:57 +01:00
// Since the hover state will have been reset, we reload it.
2020-12-09 15:39:14 +01:00
m_BaseObject - > RecurseObject ( & IGUIObject : : IsHiddenOrGhost , & IGUIObject : : UpdateMouseOver , static_cast < IGUIObject * const & > ( pNearest ) ) ;
2003-11-25 03:47:12 +01:00
}
}
2003-11-03 17:22:45 +01:00
2004-07-08 17:23:47 +02:00
// BUTTONUP's effect on m_MouseButtons is handled after
// everything else, so that e.g. 'press' handlers (activated
// on button up) see which mouse button had been pressed.
2006-08-26 23:52:18 +02:00
if ( ev - > ev . type = = SDL_MOUSEBUTTONUP )
2004-07-08 17:23:47 +02:00
{
2006-08-26 23:52:18 +02:00
switch ( ev - > ev . button . button )
2005-10-24 23:10:45 +02:00
{
case SDL_BUTTON_LEFT :
case SDL_BUTTON_RIGHT :
case SDL_BUTTON_MIDDLE :
2008-09-06 23:15:53 +02:00
m_MouseButtons & = ~ Bit < unsigned int > ( ev - > ev . button . button ) ;
2005-10-24 23:10:45 +02:00
break ;
default :
break ;
}
2004-07-08 17:23:47 +02:00
}
2010-11-14 23:47:39 +01:00
// Restore m_MousePos (for delayed mouse button events)
if ( ev - > ev . type = = SDL_MOUSEBUTTONDOWN | | ev - > ev . type = = SDL_MOUSEBUTTONUP )
m_MousePos = oldMousePos ;
Let players remap hotkeys in-game, fix default hotkeys being qwerty-specific.
- Provide a "Hotkey" screen to let players remap hotkeys in-game using a
convenient setup.
- Make all .cfg hotkeys refer to scancodes (i.e. position on the
keyboard), so that default hotkeys now translate correctly for AZERTY,
QWERTZ and other layouts.
- 'BackSpace' is now an alias for 'Delete', and works for killing units.
This fixes #1917, as macs don't have a proper delete key and would need
to use Fn+Del otherwise. This shifts "timewarp" to Shift+BackSpace.
Functionally, this switches hotkeys to scancodes, as that makes more
sense (they are combinations of key positions, not actual text output).
SDL includes are cleaned and key names are reused.
Fixes #2850, Fixes #2604, Refs #1810, Fixes #1917.
Follows work in 3d7784d2af.
Various diffs tested by: Angen, Stan, Freagarach
Comments by: Nescio
Differential Revision: https://code.wildfiregames.com/D2814
This was SVN commit r24215.
2020-11-19 10:27:26 +01:00
// Let GUI items handle keys after everything else, e.g. for input boxes.
if ( ret = = IN_PASS & & GetFocusedObject ( ) )
2004-10-14 12:09:26 +02:00
{
Let players remap hotkeys in-game, fix default hotkeys being qwerty-specific.
- Provide a "Hotkey" screen to let players remap hotkeys in-game using a
convenient setup.
- Make all .cfg hotkeys refer to scancodes (i.e. position on the
keyboard), so that default hotkeys now translate correctly for AZERTY,
QWERTZ and other layouts.
- 'BackSpace' is now an alias for 'Delete', and works for killing units.
This fixes #1917, as macs don't have a proper delete key and would need
to use Fn+Del otherwise. This shifts "timewarp" to Shift+BackSpace.
Functionally, this switches hotkeys to scancodes, as that makes more
sense (they are combinations of key positions, not actual text output).
SDL includes are cleaned and key names are reused.
Fixes #2850, Fixes #2604, Refs #1810, Fixes #1917.
Follows work in 3d7784d2af.
Various diffs tested by: Angen, Stan, Freagarach
Comments by: Nescio
Differential Revision: https://code.wildfiregames.com/D2814
This was SVN commit r24215.
2020-11-19 10:27:26 +01:00
if ( ev - > ev . type = = SDL_KEYUP | | ev - > ev . type = = SDL_KEYDOWN | |
ev - > ev . type = = SDL_HOTKEYUP | | ev - > ev . type = = SDL_HOTKEYDOWN | |
ev - > ev . type = = SDL_TEXTINPUT | | ev - > ev . type = = SDL_TEXTEDITING )
ret = GetFocusedObject ( ) - > ManuallyHandleKeys ( ev ) ;
2005-10-20 19:44:56 +02:00
// else will return IN_PASS because we never used the button.
2004-10-14 12:09:26 +02:00
}
return ret ;
2003-11-03 17:22:45 +01:00
}
2004-07-24 21:09:12 +02:00
void CGUI : : TickObjects ( )
{
2020-12-09 15:39:14 +01:00
m_BaseObject - > RecurseObject ( & IGUIObject : : IsHiddenOrGhost , & IGUIObject : : Tick ) ;
2020-01-15 17:00:37 +01:00
SendEventToAll ( EventNameTick ) ;
2019-08-21 12:12:33 +02:00
m_Tooltip . Update ( FindObjectUnderMouse ( ) , m_MousePos , * this ) ;
2004-07-24 21:09:12 +02:00
}
2020-01-15 17:00:37 +01:00
void CGUI : : SendEventToAll ( const CStr & eventName )
2005-01-01 18:30:55 +01:00
{
2021-01-17 16:44:14 +01:00
std : : unordered_map < CStr , std : : vector < IGUIObject * > > : : iterator it = m_EventObjects . find ( eventName ) ;
if ( it = = m_EventObjects . end ( ) )
2020-11-14 22:54:17 +01:00
return ;
2021-01-17 16:44:14 +01:00
std : : vector < IGUIObject * > copy = it - > second ;
for ( IGUIObject * object : copy )
object - > ScriptEvent ( eventName ) ;
2019-07-19 23:15:04 +02:00
}
2020-01-15 17:00:37 +01:00
void CGUI : : SendEventToAll ( const CStr & eventName , const JS : : HandleValueArray & paramData )
2019-07-19 23:15:04 +02:00
{
2021-01-17 16:44:14 +01:00
std : : unordered_map < CStr , std : : vector < IGUIObject * > > : : iterator it = m_EventObjects . find ( eventName ) ;
if ( it = = m_EventObjects . end ( ) )
2020-11-14 22:54:17 +01:00
return ;
2021-01-17 16:44:14 +01:00
std : : vector < IGUIObject * > copy = it - > second ;
for ( IGUIObject * object : copy )
object - > ScriptEvent ( eventName , paramData ) ;
2005-01-01 18:30:55 +01:00
}
2003-11-03 17:22:45 +01:00
void CGUI : : Draw ( )
{
2021-04-11 13:27:53 +02:00
using Arena = Allocators : : DynamicArena < 128 * KiB > ;
using ObjectListAllocator = ProxyAllocator < VisibleObject , Arena > ;
Arena arena ;
std : : vector < VisibleObject , ObjectListAllocator > visibleObjects ( ( ObjectListAllocator ( arena ) ) ) ;
CollectVisibleObjectsRecursively ( m_BaseObject - > GetChildren ( ) , & visibleObjects ) ;
for ( VisibleObject & visibleObject : visibleObjects )
visibleObject . bufferedZ = visibleObject . object - > GetBufferedZ ( ) ;
std : : sort ( visibleObjects . begin ( ) , visibleObjects . end ( ) , [ ] ( const VisibleObject & visibleObject1 , const VisibleObject & visibleObject2 ) - > bool {
if ( visibleObject1 . bufferedZ ! = visibleObject2 . bufferedZ )
return visibleObject1 . bufferedZ < visibleObject2 . bufferedZ ;
return visibleObject1 . index < visibleObject2 . index ;
} ) ;
for ( const VisibleObject & visibleObject : visibleObjects )
visibleObject . object - > Draw ( ) ;
2003-11-03 17:22:45 +01:00
}
2021-03-27 12:38:34 +01:00
void CGUI : : DrawSprite ( const CGUISpriteInstance & Sprite , const float & Z , const CRect & Rect , const CRect & UNUSED ( Clipping ) )
2003-12-01 08:06:55 +01:00
{
2004-12-15 22:24:46 +01:00
// If the sprite doesn't exist (name == ""), don't bother drawing anything
2019-08-18 17:17:49 +02:00
if ( ! Sprite )
2003-12-01 08:06:55 +01:00
return ;
2004-07-24 16:04:40 +02:00
// TODO: Clipping?
2003-12-01 08:06:55 +01:00
2021-03-27 12:38:34 +01:00
Sprite . Draw ( * this , Rect , m_Sprites , Z ) ;
2003-12-01 08:06:55 +01:00
}
2003-11-24 18:13:37 +01:00
void CGUI : : UpdateResolution ( )
{
2020-12-09 15:39:14 +01:00
m_BaseObject - > RecurseObject ( nullptr , & IGUIObject : : UpdateCachedSize ) ;
2003-11-24 18:13:37 +01:00
}
2019-11-24 11:26:48 +01:00
IGUIObject * CGUI : : ConstructObject ( const CStr & str )
{
std : : map < CStr , ConstructObjectFunction > : : iterator it = m_ObjectTypes . find ( str ) ;
if ( it = = m_ObjectTypes . end ( ) )
return nullptr ;
return ( * it - > second ) ( * this ) ;
}
2019-10-11 18:20:50 +02:00
bool CGUI : : AddObject ( IGUIObject & parent , IGUIObject & child )
2003-11-03 17:22:45 +01:00
{
2019-10-11 18:20:50 +02:00
if ( child . m_Name . empty ( ) )
2003-11-03 17:22:45 +01:00
{
2019-10-11 18:20:50 +02:00
LOGERROR ( " Can't register an object without name! " ) ;
return false ;
2003-12-01 08:06:55 +01:00
}
2003-11-03 17:22:45 +01:00
2019-10-11 18:20:50 +02:00
if ( m_pAllObjects . find ( child . m_Name ) ! = m_pAllObjects . end ( ) )
2003-11-03 17:22:45 +01:00
{
2019-10-11 18:20:50 +02:00
LOGERROR ( " Can't register more than one object of the name %s " , child . m_Name . c_str ( ) ) ;
return false ;
2003-11-03 17:22:45 +01:00
}
2019-10-11 18:20:50 +02:00
m_pAllObjects [ child . m_Name ] = & child ;
2021-05-04 17:17:50 +02:00
parent . RegisterChild ( & child ) ;
2019-10-11 18:20:50 +02:00
return true ;
2003-11-03 17:22:45 +01:00
}
2020-12-09 15:39:14 +01:00
IGUIObject * CGUI : : GetBaseObject ( )
{
return m_BaseObject . get ( ) ;
} ;
2004-06-18 16:07:06 +02:00
bool CGUI : : ObjectExists ( const CStr & Name ) const
2003-11-03 17:22:45 +01:00
{
2019-09-23 01:28:25 +02:00
return m_pAllObjects . find ( Name ) ! = m_pAllObjects . end ( ) ;
2004-05-29 06:06:50 +02:00
}
2004-07-08 17:23:47 +02:00
IGUIObject * CGUI : : FindObjectByName ( const CStr & Name ) const
{
map_pObjects : : const_iterator it = m_pAllObjects . find ( Name ) ;
2019-09-23 01:28:25 +02:00
2004-07-08 17:23:47 +02:00
if ( it = = m_pAllObjects . end ( ) )
2019-09-23 01:28:25 +02:00
return nullptr ;
return it - > second ;
2004-07-08 17:23:47 +02:00
}
2019-09-18 22:51:45 +02:00
IGUIObject * CGUI : : FindObjectUnderMouse ( )
2010-08-22 01:58:08 +02:00
{
2019-09-23 01:28:25 +02:00
IGUIObject * pNearest = nullptr ;
2020-12-09 15:39:14 +01:00
m_BaseObject - > RecurseObject ( & IGUIObject : : IsHiddenOrGhost , & IGUIObject : : ChooseMouseOverAndClosest , pNearest ) ;
2010-08-22 01:58:08 +02:00
return pNearest ;
}
2010-08-13 15:50:03 +02:00
void CGUI : : SetFocusedObject ( IGUIObject * pObject )
{
if ( pObject = = m_FocusedObject )
return ;
if ( m_FocusedObject )
2011-04-28 22:42:11 +02:00
{
SGUIMessage msg ( GUIM_LOST_FOCUS ) ;
m_FocusedObject - > HandleMessage ( msg ) ;
}
2010-08-13 15:50:03 +02:00
m_FocusedObject = pObject ;
if ( m_FocusedObject )
2011-04-28 22:42:11 +02:00
{
SGUIMessage msg ( GUIM_GOT_FOCUS ) ;
m_FocusedObject - > HandleMessage ( msg ) ;
}
2010-08-13 15:50:03 +02:00
}
2004-07-08 17:23:47 +02:00
2020-11-27 03:30:42 +01:00
void CGUI : : SetObjectStyle ( IGUIObject * pObject , const CStr & styleName )
{
// If the style is not recognised (or an empty string) then ApplyStyle will
// emit an error message. Thus we don't need to handle it here.
2021-05-06 10:22:37 +02:00
pObject - > ApplyStyle ( styleName ) ;
2020-11-27 03:30:42 +01:00
}
void CGUI : : UnsetObjectStyle ( IGUIObject * pObject )
{
SetObjectStyle ( pObject , " default " ) ;
}
2019-09-04 17:29:36 +02:00
void CGUI : : SetObjectHotkey ( IGUIObject * pObject , const CStr & hotkeyTag )
{
if ( ! hotkeyTag . empty ( ) )
m_HotkeyObjects [ hotkeyTag ] . push_back ( pObject ) ;
}
void CGUI : : UnsetObjectHotkey ( IGUIObject * pObject , const CStr & hotkeyTag )
{
if ( hotkeyTag . empty ( ) )
return ;
std : : vector < IGUIObject * > & assignment = m_HotkeyObjects [ hotkeyTag ] ;
assignment . erase (
std : : remove_if (
assignment . begin ( ) ,
assignment . end ( ) ,
[ & pObject ] ( const IGUIObject * hotkeyObject )
{ return pObject = = hotkeyObject ; } ) ,
assignment . end ( ) ) ;
}
2020-05-26 23:47:03 +02:00
void CGUI : : SetGlobalHotkey ( const CStr & hotkeyTag , const CStr & eventName , JS : : HandleValue function )
2019-09-05 12:42:16 +02:00
{
Improve JS Exception handling.
- Check for pending exceptions after function calls and script
executions.
- Call LOGERROR instead of JS_ReportError when there is a conversion
error in FromJSVal, since that can only be called from C++ (where JS
errors don't really make sense). Instead, C++ callers of FromJSVal
should handle the failure and, themselves, either report an error or
simply do something else.
- Wrap JS_ReportError since that makes updating it later easier.
This isn't a systematical fix since ToJSVal also ought return a boolean
for failures, and we probably should trigger errors instead of warnings
on 'implicit' conversions, rather a preparation diff.
Part of the SM52 migration, stage: SM45 compatible (actually SM52
incompatible, too).
Based on a patch by: Itms
Comments by: Vladislavbelov, Stan`
Refs #742, #4893
Differential Revision: https://code.wildfiregames.com/D3093
This was SVN commit r24187.
2020-11-15 19:29:17 +01:00
ScriptRequest rq ( * m_ScriptInterface ) ;
2019-09-05 12:42:16 +02:00
if ( hotkeyTag . empty ( ) )
{
Improve JS Exception handling.
- Check for pending exceptions after function calls and script
executions.
- Call LOGERROR instead of JS_ReportError when there is a conversion
error in FromJSVal, since that can only be called from C++ (where JS
errors don't really make sense). Instead, C++ callers of FromJSVal
should handle the failure and, themselves, either report an error or
simply do something else.
- Wrap JS_ReportError since that makes updating it later easier.
This isn't a systematical fix since ToJSVal also ought return a boolean
for failures, and we probably should trigger errors instead of warnings
on 'implicit' conversions, rather a preparation diff.
Part of the SM52 migration, stage: SM45 compatible (actually SM52
incompatible, too).
Based on a patch by: Itms
Comments by: Vladislavbelov, Stan`
Refs #742, #4893
Differential Revision: https://code.wildfiregames.com/D3093
This was SVN commit r24187.
2020-11-15 19:29:17 +01:00
ScriptException : : Raise ( rq , " Cannot assign a function to an empty hotkey identifier! " ) ;
2019-09-05 12:42:16 +02:00
return ;
}
2020-05-26 23:47:03 +02:00
// Only support "Press", "Keydown" and "Release" events.
if ( eventName ! = EventNamePress & & eventName ! = EventNameKeyDown & & eventName ! = EventNameRelease )
{
Improve JS Exception handling.
- Check for pending exceptions after function calls and script
executions.
- Call LOGERROR instead of JS_ReportError when there is a conversion
error in FromJSVal, since that can only be called from C++ (where JS
errors don't really make sense). Instead, C++ callers of FromJSVal
should handle the failure and, themselves, either report an error or
simply do something else.
- Wrap JS_ReportError since that makes updating it later easier.
This isn't a systematical fix since ToJSVal also ought return a boolean
for failures, and we probably should trigger errors instead of warnings
on 'implicit' conversions, rather a preparation diff.
Part of the SM52 migration, stage: SM45 compatible (actually SM52
incompatible, too).
Based on a patch by: Itms
Comments by: Vladislavbelov, Stan`
Refs #742, #4893
Differential Revision: https://code.wildfiregames.com/D3093
This was SVN commit r24187.
2020-11-15 19:29:17 +01:00
ScriptException : : Raise ( rq , " Cannot assign a function to an unsupported event! " ) ;
2020-05-26 23:47:03 +02:00
return ;
}
2020-11-30 10:03:20 +01:00
if ( ! function . isObject ( ) | | ! JS_ObjectIsFunction ( & function . toObject ( ) ) )
2019-09-05 12:42:16 +02:00
{
Improve JS Exception handling.
- Check for pending exceptions after function calls and script
executions.
- Call LOGERROR instead of JS_ReportError when there is a conversion
error in FromJSVal, since that can only be called from C++ (where JS
errors don't really make sense). Instead, C++ callers of FromJSVal
should handle the failure and, themselves, either report an error or
simply do something else.
- Wrap JS_ReportError since that makes updating it later easier.
This isn't a systematical fix since ToJSVal also ought return a boolean
for failures, and we probably should trigger errors instead of warnings
on 'implicit' conversions, rather a preparation diff.
Part of the SM52 migration, stage: SM45 compatible (actually SM52
incompatible, too).
Based on a patch by: Itms
Comments by: Vladislavbelov, Stan`
Refs #742, #4893
Differential Revision: https://code.wildfiregames.com/D3093
This was SVN commit r24187.
2020-11-15 19:29:17 +01:00
ScriptException : : Raise ( rq , " Cannot assign non-function value to global hotkey '%s' " , hotkeyTag . c_str ( ) ) ;
2019-09-05 12:42:16 +02:00
return ;
}
2020-05-26 23:47:03 +02:00
UnsetGlobalHotkey ( hotkeyTag , eventName ) ;
2020-11-13 14:18:22 +01:00
m_GlobalHotkeys [ hotkeyTag ] [ eventName ] . init ( rq . cx , function ) ;
2019-09-05 12:42:16 +02:00
}
2020-05-26 23:47:03 +02:00
void CGUI : : UnsetGlobalHotkey ( const CStr & hotkeyTag , const CStr & eventName )
2019-09-05 12:42:16 +02:00
{
2020-05-26 23:47:03 +02:00
std : : map < CStr , std : : map < CStr , JS : : PersistentRootedValue > > : : iterator it = m_GlobalHotkeys . find ( hotkeyTag ) ;
if ( it = = m_GlobalHotkeys . end ( ) )
return ;
m_GlobalHotkeys [ hotkeyTag ] . erase ( eventName ) ;
if ( m_GlobalHotkeys . count ( hotkeyTag ) = = 0 )
m_GlobalHotkeys . erase ( it ) ;
2019-09-05 12:42:16 +02:00
}
2019-03-18 23:15:40 +01:00
const SGUIScrollBarStyle * CGUI : : GetScrollBarStyle ( const CStr & style ) const
{
MOVABLE idiom, const CGUI struct maps, in place move construction instead of copying temporaries during CGUI XML loading and GenerateText.
Introduce MOVABLE idiom indicating that a class can use move semantics.
Make values of CGUI struct maps holding XML data const to ensure at the
root that the data is not modified.
Use NONCOPYABLE and MOVABLE for SGUIIcon and SGUIStyle to enforce the
non-copy policy on the compiler level (until someone changes the GUI
design to make modifications needed).
As indicated by that:
Replace copy operations by in place move operations for these CGUI
struct maps in the CGUI Xeromyces XML loading functions.
Replace copy operations by const references for CSize and SGUIIcon in
CGUIString::GenerateTextCall and CGUI::GenerateText.
This avoids copying of non primitive members, such as the strings and
containers of strings.
Further related cleanup to be kept separated for auditability.
Differential Revision: https://code.wildfiregames.com/D2163
Few comments on IRC by: wraitii, Itms
Tested on: gcc 9, Jenkins, partially VS2015
Refs #1984,
NONCOPYABLE CGUISpriteInstances: 0a7d0ecdde, 8f4f8e240f, c19f3608a5
NONCOPYABLE Image, Sprite: fb8032043b
NONCOPYABLE GUI page: 94c57085e9
NONCOPYABLE GUIManager: 7c2e9027c2
NONCOPYABLE macro: 16ccae10cd
This was SVN commit r22637.
2019-08-09 19:25:55 +02:00
std : : map < CStr , const SGUIScrollBarStyle > : : const_iterator it = m_ScrollBarStyles . find ( style ) ;
2019-03-18 23:15:40 +01:00
if ( it = = m_ScrollBarStyles . end ( ) )
return nullptr ;
return & it - > second ;
}
2003-11-24 03:18:41 +01:00
/**
* @ callgraph
*/
2019-11-25 15:30:25 +01:00
void CGUI : : LoadXmlFile ( const VfsPath & Filename , std : : unordered_set < VfsPath > & Paths )
2003-11-03 17:22:45 +01:00
{
2009-12-03 21:17:22 +01:00
Paths . insert ( Filename ) ;
2003-11-03 17:22:45 +01:00
2021-05-04 17:17:50 +02:00
CXeromyces xeroFile ;
if ( xeroFile . Load ( g_VFS , Filename , " gui " ) ! = PSRETURN_OK )
2004-07-11 18:22:35 +02:00
return ;
2003-11-03 17:22:45 +01:00
2021-05-04 17:17:50 +02:00
XMBElement node = xeroFile . GetRoot ( ) ;
std : : string_view root_name ( xeroFile . GetElementStringView ( node . GetNodeName ( ) ) ) ;
2004-07-17 19:09:33 +02:00
2019-10-11 18:20:50 +02:00
if ( root_name = = " objects " )
2021-05-04 17:17:50 +02:00
Xeromyces_ReadRootObjects ( xeroFile , node , Paths ) ;
2019-10-11 18:20:50 +02:00
else if ( root_name = = " sprites " )
2021-05-04 17:17:50 +02:00
Xeromyces_ReadRootSprites ( xeroFile , node ) ;
2019-10-11 18:20:50 +02:00
else if ( root_name = = " styles " )
2021-05-04 17:17:50 +02:00
Xeromyces_ReadRootStyles ( xeroFile , node ) ;
2019-10-11 18:20:50 +02:00
else if ( root_name = = " setup " )
2021-05-04 17:17:50 +02:00
Xeromyces_ReadRootSetup ( xeroFile , node ) ;
2019-10-11 18:20:50 +02:00
else
XMB Improvements, parse JS into XMB, make strings more efficient.
XMB format is bumped to 4, invalidating all cached files. The
differences are:
- element/attribute names are stored after the elements themselves, and
not before. This allows writing XMB data in one pass instead of two.
- names themselves becomes offsets (instead of arbitrary integers),
making getting the string from the int name much more efficient.
XMBFile is renamed to XMBData to clarify that it does not, in fact,
refer to a file on disk.
XMBData::GetElementString is also changed to return a const char*, thus
not creating an std::string. A string_view version is added where
convenient.
The XML->XMB and JS->XMB conversion functions and the corresponding
storage are moved to `ps/XMB`, since that format doesn't particularly
relate to XML. CXeromyces becomes lighter and more focused as a result.
The XML->XMB conversion also benefits from the above streamlining.
Note that in a few cases, string_view gets printed to CLogger via
data(), which is generally not legal, but we know that the strings are
null-terminated here. Our libfmt (version 4) doesn't support
string_view, that would be v5.
Differential Revision: https://code.wildfiregames.com/D3909
This was SVN commit r25375.
2021-05-04 15:02:34 +02:00
LOGERROR ( " CGUI::LoadXmlFile encountered an unknown XML root node type: %s " , root_name . data ( ) ) ;
2019-10-11 18:20:50 +02:00
}
void CGUI : : LoadedXmlFiles ( )
{
2020-12-09 15:39:14 +01:00
m_BaseObject - > RecurseObject ( nullptr , & IGUIObject : : UpdateCachedSize ) ;
2019-10-11 18:20:50 +02:00
SGUIMessage msg ( GUIM_LOAD ) ;
2020-12-09 15:39:14 +01:00
m_BaseObject - > RecurseObject ( nullptr , & IGUIObject : : HandleMessage , msg ) ;
2019-10-11 18:20:50 +02:00
2020-01-15 17:00:37 +01:00
SendEventToAll ( EventNameLoad ) ;
2003-11-03 17:22:45 +01:00
}
//===================================================================
2004-07-08 17:23:47 +02:00
// XML Reading Xeromyces Specific Sub-Routines
2003-11-03 17:22:45 +01:00
//===================================================================
2021-05-04 17:17:50 +02:00
void CGUI : : Xeromyces_ReadRootObjects ( const XMBData & xmb , XMBElement element , std : : unordered_set < VfsPath > & Paths )
2003-11-03 17:22:45 +01:00
{
2021-05-04 17:17:50 +02:00
int el_script = xmb . GetElementID ( " script " ) ;
2004-07-11 18:22:35 +02:00
2010-02-28 22:36:25 +01:00
std : : vector < std : : pair < CStr , CStr > > subst ;
2003-11-03 17:22:45 +01:00
// Iterate main children
2004-07-11 18:22:35 +02:00
// they should all be <object> or <script> elements
2021-05-04 17:17:50 +02:00
for ( XMBElement child : element . GetChildNodes ( ) )
2003-11-03 17:22:45 +01:00
{
2007-05-02 14:07:08 +02:00
if ( child . GetNodeName ( ) = = el_script )
2004-07-11 18:22:35 +02:00
// Execute the inline script
2021-05-04 17:17:50 +02:00
Xeromyces_ReadScript ( xmb , child , Paths ) ;
2004-07-11 18:22:35 +02:00
else
// Read in this whole object into the GUI
2021-05-04 17:17:50 +02:00
Xeromyces_ReadObject ( xmb , child , m_BaseObject . get ( ) , subst , Paths , 0 ) ;
2003-11-03 17:22:45 +01:00
}
}
2021-05-04 17:17:50 +02:00
void CGUI : : Xeromyces_ReadRootSprites ( const XMBData & xmb , XMBElement element )
2003-11-03 17:22:45 +01:00
{
2021-05-04 17:17:50 +02:00
for ( XMBElement child : element . GetChildNodes ( ) )
Xeromyces_ReadSprite ( xmb , child ) ;
2003-11-03 17:22:45 +01:00
}
2021-05-04 17:17:50 +02:00
void CGUI : : Xeromyces_ReadRootStyles ( const XMBData & xmb , XMBElement element )
2003-12-01 08:06:55 +01:00
{
2021-05-04 17:17:50 +02:00
for ( XMBElement child : element . GetChildNodes ( ) )
Xeromyces_ReadStyle ( xmb , child ) ;
2003-12-01 08:06:55 +01:00
}
2021-05-04 17:17:50 +02:00
void CGUI : : Xeromyces_ReadRootSetup ( const XMBData & xmb , XMBElement element )
2003-12-27 07:26:03 +01:00
{
2021-05-04 17:17:50 +02:00
for ( XMBElement child : element . GetChildNodes ( ) )
2003-12-27 07:26:03 +01:00
{
2021-05-04 17:17:50 +02:00
std : : string_view name ( xmb . GetElementStringView ( child . GetNodeName ( ) ) ) ;
2004-07-10 22:33:42 +02:00
if ( name = = " scrollbar " )
2021-05-04 17:17:50 +02:00
Xeromyces_ReadScrollBarStyle ( xmb , child ) ;
2015-08-21 19:08:41 +02:00
else if ( name = = " icon " )
2021-05-04 17:17:50 +02:00
Xeromyces_ReadIcon ( xmb , child ) ;
2015-08-21 19:08:41 +02:00
else if ( name = = " tooltip " )
2021-05-04 17:17:50 +02:00
Xeromyces_ReadTooltip ( xmb , child ) ;
2015-08-21 19:08:41 +02:00
else if ( name = = " color " )
2021-05-04 17:17:50 +02:00
Xeromyces_ReadColor ( xmb , child ) ;
2005-07-25 21:06:18 +02:00
else
2009-11-03 22:46:35 +01:00
debug_warn ( L " Invalid data - DTD shouldn't allow this " ) ;
2003-12-27 07:26:03 +01:00
}
}
2021-05-04 17:17:50 +02:00
IGUIObject * CGUI : : Xeromyces_ReadObject ( const XMBData & xmb , XMBElement element , IGUIObject * pParent , std : : vector < std : : pair < CStr , CStr > > & NameSubst , std : : unordered_set < VfsPath > & Paths , u32 nesting_depth )
2003-11-03 17:22:45 +01:00
{
2011-04-30 15:01:45 +02:00
ENSURE ( pParent ) ;
2003-11-03 17:22:45 +01:00
2021-05-04 17:17:50 +02:00
XMBAttributeList attributes = element . GetAttributes ( ) ;
2004-07-08 17:23:47 +02:00
2021-05-04 17:17:50 +02:00
CStr type ( attributes . GetNamedItem ( xmb . GetAttributeID ( " type " ) ) ) ;
2009-03-23 22:35:26 +01:00
if ( type . empty ( ) )
type = " empty " ;
2003-11-03 17:22:45 +01:00
// Construct object from specified type
// henceforth, we need to do a rollback before aborting.
// i.e. releasing this object
2016-06-14 17:06:39 +02:00
IGUIObject * object = ConstructObject ( type ) ;
2003-11-03 17:22:45 +01:00
if ( ! object )
{
2015-01-22 21:36:24 +01:00
LOGERROR ( " GUI: Unrecognized object type \" %s \" " , type . c_str ( ) ) ;
2021-05-04 17:17:50 +02:00
return nullptr ;
2003-11-03 17:22:45 +01:00
}
2004-07-08 17:23:47 +02:00
// Cache some IDs for element attribute names, to avoid string comparisons
2021-05-04 17:17:50 +02:00
# define ELMT(x) int elmt_##x = xmb.GetElementID(#x)
# define ATTR(x) int attr_##x = xmb.GetAttributeID(#x)
2004-07-08 17:23:47 +02:00
ELMT ( object ) ;
ELMT ( action ) ;
2020-01-28 02:06:34 +01:00
ELMT ( script ) ;
2010-02-28 22:36:25 +01:00
ELMT ( repeat ) ;
2014-04-20 22:03:57 +02:00
ELMT ( translatableAttribute ) ;
ELMT ( translate ) ;
ELMT ( attribute ) ;
ELMT ( keep ) ;
2014-06-14 18:12:41 +02:00
ELMT ( include ) ;
2004-07-08 17:23:47 +02:00
ATTR ( style ) ;
ATTR ( type ) ;
ATTR ( name ) ;
ATTR ( z ) ;
ATTR ( on ) ;
2004-07-11 18:22:35 +02:00
ATTR ( file ) ;
2014-06-17 15:03:55 +02:00
ATTR ( directory ) ;
2014-04-20 22:03:57 +02:00
ATTR ( id ) ;
2014-05-01 00:33:08 +02:00
ATTR ( context ) ;
2004-07-08 17:23:47 +02:00
2003-12-01 08:06:55 +01:00
//
// Read Style and set defaults
//
2003-12-27 07:26:03 +01:00
// If the setting "style" is set, try loading that setting.
//
// Always load default (if it's available) first!
//
2020-11-27 03:30:42 +01:00
SetObjectStyle ( object , " default " ) ;
2003-12-27 07:26:03 +01:00
2020-11-27 03:30:42 +01:00
CStr argStyle ( attributes . GetNamedItem ( attr_style ) ) ;
2014-06-14 18:12:41 +02:00
if ( ! argStyle . empty ( ) )
2020-11-27 03:30:42 +01:00
SetObjectStyle ( object , argStyle ) ;
2003-12-27 07:26:03 +01:00
2003-11-03 17:22:45 +01:00
bool NameSet = false ;
2016-06-14 17:06:39 +02:00
bool ManuallySetZ = false ;
2003-11-03 17:22:45 +01:00
2014-06-11 19:14:35 +02:00
CStrW inclusionPath ;
2004-07-22 18:18:12 +02:00
2015-06-01 02:29:35 +02:00
for ( XMBAttribute attr : attributes )
2003-11-03 17:22:45 +01:00
{
2004-05-29 06:06:50 +02:00
// If value is "null", then it is equivalent as never being entered
2019-10-11 18:20:50 +02:00
if ( attr . Value = = " null " )
2004-05-29 06:06:50 +02:00
continue ;
2003-12-01 08:06:55 +01:00
// Ignore "type" and "style", we've already checked it
2004-07-17 19:09:33 +02:00
if ( attr . Name = = attr_type | | attr . Name = = attr_style )
2003-11-03 17:22:45 +01:00
continue ;
2004-07-08 17:23:47 +02:00
if ( attr . Name = = attr_name )
2003-11-03 17:22:45 +01:00
{
2014-06-11 19:14:35 +02:00
CStr name ( attr . Value ) ;
2010-02-28 22:36:25 +01:00
2021-05-06 10:22:37 +02:00
if ( name . Left ( 2 ) = = " __ " )
{
LOGERROR ( " GUI: Names starting with '__' are reserved for the engine (object: %s) " , name . c_str ( ) ) ;
continue ;
}
2015-08-21 19:08:41 +02:00
for ( const std : : pair < CStr , CStr > & sub : NameSubst )
name . Replace ( sub . first , sub . second ) ;
2010-02-28 22:36:25 +01:00
object - > SetName ( name ) ;
2003-11-03 17:22:45 +01:00
NameSet = true ;
continue ;
}
2004-07-08 17:23:47 +02:00
if ( attr . Name = = attr_z )
2003-12-27 07:26:03 +01:00
ManuallySetZ = true ;
2021-05-04 17:17:50 +02:00
object - > SetSettingFromString ( xmb . GetAttributeString ( attr . Name ) , attr . Value . FromUTF8 ( ) , false ) ;
2003-11-03 17:22:45 +01:00
}
2004-05-29 06:06:50 +02:00
// Check if name isn't set, generate an internal name in that case.
2003-11-03 17:22:45 +01:00
if ( ! NameSet )
{
2011-02-17 21:08:20 +01:00
object - > SetName ( " __internal( " + CStr : : FromInt ( m_InternalNameNumber ) + " ) " ) ;
2004-05-29 06:06:50 +02:00
+ + m_InternalNameNumber ;
2003-11-03 17:22:45 +01:00
}
2021-05-04 17:17:50 +02:00
CStrW caption ( element . GetText ( ) . FromUTF8 ( ) ) ;
2014-06-11 19:14:35 +02:00
if ( ! caption . empty ( ) )
Move static GUI<>::SetSetting operating on IGUIObject to a member IGUIObject::SetSetting.
Remove PSERROR codes from SetSetting (let std::map throw an out_of_range
if a caller wants to Set a setting that doesn't exist without having
checked with SettingExists, equal to GetSetting from 92b6cdfeab).
That also simplifies std::function SetSettingWrap construct from
0a7d0ecdde to void IGUIObject::SettingChanged.
Don't trigger debug_warn or exceptions in GUITooltip::ShowTooltip if the
XML author specified wrong tooltip input, and dodge another
dynamic_cast.
Rename existing IGUIObject::SetSetting to
IGUIObject::SetSettingFromString and comment that it is purposed for
parsing XML files.
Remove SetSetting default value, so that authors are made aware
explicitly of the need to decide the function broadcasting a message,
refs d87057b1c0, 719f2d7967, ...
Change const bool& SkipMessage to const bool SendMessage, so that a
positive value relates to a positive action.
Clean AddSettings whitespace and integer types.
Differential Revision: https://code.wildfiregames.com/D2231
Tested on: gcc 9.1.0, clang 8.0.1, Jenkins
Comments By: Philip on IRC on 2010-07-24 on GUIUtil being ugly, in case
that one counts
This was SVN commit r22796.
2019-08-28 13:21:11 +02:00
object - > SetSettingFromString ( " caption " , caption , false ) ;
2004-12-13 13:07:12 +01:00
2021-05-04 17:17:50 +02:00
for ( XMBElement child : element . GetChildNodes ( ) )
2003-11-03 17:22:45 +01:00
{
2004-07-08 17:23:47 +02:00
// Check what name the elements got
2007-05-02 14:07:08 +02:00
int element_name = child . GetNodeName ( ) ;
2003-11-03 17:22:45 +01:00
2004-07-08 17:23:47 +02:00
if ( element_name = = elmt_object )
{
// Call this function on the child
2021-05-04 17:17:50 +02:00
Xeromyces_ReadObject ( xmb , child , object , NameSubst , Paths , nesting_depth ) ;
2003-11-03 17:22:45 +01:00
}
2004-07-08 17:23:47 +02:00
else if ( element_name = = elmt_action )
2003-11-03 17:22:45 +01:00
{
2004-07-11 18:22:35 +02:00
// Scripted <action> element
// Check for a 'file' parameter
2014-06-11 19:14:35 +02:00
CStrW filename ( child . GetAttributes ( ) . GetNamedItem ( attr_file ) . FromUTF8 ( ) ) ;
2004-07-11 18:22:35 +02:00
CStr code ;
// If there is a file, open it and use it as the code
2014-06-11 19:14:35 +02:00
if ( ! filename . empty ( ) )
2004-07-11 18:22:35 +02:00
{
2009-12-03 21:17:22 +01:00
Paths . insert ( filename ) ;
2004-07-29 18:17:21 +02:00
CVFSFile scriptfile ;
2010-07-04 12:15:53 +02:00
if ( scriptfile . Load ( g_VFS , filename ) ! = PSRETURN_OK )
2004-07-11 18:22:35 +02:00
{
2015-01-22 21:37:38 +01:00
LOGERROR ( " Error opening GUI script action file '%s' " , utf8_from_wstring ( filename ) ) ;
2019-10-11 18:20:50 +02:00
continue ;
2004-07-17 19:09:33 +02:00
}
2004-07-11 18:22:35 +02:00
2012-05-04 23:48:46 +02:00
code = scriptfile . DecodeUTF8 ( ) ; // assume it's UTF-8
2004-07-11 18:22:35 +02:00
}
2014-04-20 22:03:57 +02:00
XMBElementList grandchildren = child . GetChildNodes ( ) ;
2015-06-01 02:29:35 +02:00
if ( ! grandchildren . empty ( ) ) // The <action> element contains <keep> and <translate> tags.
for ( XMBElement grandchild : grandchildren )
2014-04-20 22:03:57 +02:00
{
if ( grandchild . GetNodeName ( ) = = elmt_translate )
2014-10-15 18:04:37 +02:00
code + = g_L10n . Translate ( grandchild . GetText ( ) ) ;
2014-04-20 22:03:57 +02:00
else if ( grandchild . GetNodeName ( ) = = elmt_keep )
code + = grandchild . GetText ( ) ;
}
2021-01-15 11:07:36 +01:00
else // It's pure JavaScript code.
2014-04-20 22:03:57 +02:00
// Read the inline code (concatenating to the file code, if both are specified)
code + = CStr ( child . GetText ( ) ) ;
2004-07-11 18:22:35 +02:00
2020-01-15 17:00:37 +01:00
CStr eventName = child . GetAttributes ( ) . GetNamedItem ( attr_on ) ;
object - > RegisterScriptHandler ( eventName , code , * this ) ;
2003-11-03 17:22:45 +01:00
}
2020-01-28 02:06:34 +01:00
else if ( child . GetNodeName ( ) = = elmt_script )
{
2021-05-04 17:17:50 +02:00
Xeromyces_ReadScript ( xmb , child , Paths ) ;
2020-01-28 02:06:34 +01:00
}
2010-02-28 22:36:25 +01:00
else if ( element_name = = elmt_repeat )
{
2021-05-04 17:17:50 +02:00
Xeromyces_ReadRepeat ( xmb , child , object , NameSubst , Paths , nesting_depth ) ;
2010-02-28 22:36:25 +01:00
}
2014-04-20 22:03:57 +02:00
else if ( element_name = = elmt_translatableAttribute )
{
2021-01-15 11:07:36 +01:00
// This is an element in the form "<translatableAttribute id="attributeName">attributeValue</translatableAttribute>".
2014-04-20 22:03:57 +02:00
CStr attributeName ( child . GetAttributes ( ) . GetNamedItem ( attr_id ) ) ; // Read the attribute name.
2014-06-14 18:12:41 +02:00
if ( attributeName . empty ( ) )
2014-04-20 22:03:57 +02:00
{
2021-01-15 11:07:36 +01:00
LOGERROR ( " GUI: 'translatableAttribute' XML element with empty 'id' XML attribute found. (object: %s) " , object - > GetPresentableName ( ) . c_str ( ) ) ;
2014-06-14 18:12:41 +02:00
continue ;
2014-04-20 22:03:57 +02:00
}
2014-06-14 18:12:41 +02:00
CStr value ( child . GetText ( ) ) ;
if ( value . empty ( ) )
continue ;
CStr context ( child . GetAttributes ( ) . GetNamedItem ( attr_context ) ) ; // Read the context if any.
Move static GUI<>::SetSetting operating on IGUIObject to a member IGUIObject::SetSetting.
Remove PSERROR codes from SetSetting (let std::map throw an out_of_range
if a caller wants to Set a setting that doesn't exist without having
checked with SettingExists, equal to GetSetting from 92b6cdfeab).
That also simplifies std::function SetSettingWrap construct from
0a7d0ecdde to void IGUIObject::SettingChanged.
Don't trigger debug_warn or exceptions in GUITooltip::ShowTooltip if the
XML author specified wrong tooltip input, and dodge another
dynamic_cast.
Rename existing IGUIObject::SetSetting to
IGUIObject::SetSettingFromString and comment that it is purposed for
parsing XML files.
Remove SetSetting default value, so that authors are made aware
explicitly of the need to decide the function broadcasting a message,
refs d87057b1c0, 719f2d7967, ...
Change const bool& SkipMessage to const bool SendMessage, so that a
positive value relates to a positive action.
Clean AddSettings whitespace and integer types.
Differential Revision: https://code.wildfiregames.com/D2231
Tested on: gcc 9.1.0, clang 8.0.1, Jenkins
Comments By: Philip on IRC on 2010-07-24 on GUIUtil being ugly, in case
that one counts
This was SVN commit r22796.
2019-08-28 13:21:11 +02:00
CStr translatedValue = context . empty ( ) ?
g_L10n . Translate ( value ) :
g_L10n . TranslateWithContext ( context , value ) ;
object - > SetSettingFromString ( attributeName , translatedValue . FromUTF8 ( ) , false ) ;
2014-04-20 22:03:57 +02:00
}
else if ( element_name = = elmt_attribute )
{
2021-01-15 11:07:36 +01:00
// This is an element in the form "<attribute id="attributeName"><keep>Don't translate this part
// </keep><translate>but translate this one.</translate></attribute>".
2014-04-20 22:03:57 +02:00
CStr attributeName ( child . GetAttributes ( ) . GetNamedItem ( attr_id ) ) ; // Read the attribute name.
2014-06-14 18:12:41 +02:00
if ( attributeName . empty ( ) )
2014-04-20 22:03:57 +02:00
{
2021-01-15 11:07:36 +01:00
LOGERROR ( " GUI: 'attribute' XML element with empty 'id' XML attribute found. (object: %s) " , object - > GetPresentableName ( ) . c_str ( ) ) ;
2014-06-14 18:12:41 +02:00
continue ;
}
2014-04-20 22:03:57 +02:00
2014-06-14 18:12:41 +02:00
CStr translatedValue ;
2015-06-01 02:29:35 +02:00
for ( XMBElement grandchild : child . GetChildNodes ( ) )
2014-06-14 18:12:41 +02:00
{
if ( grandchild . GetNodeName ( ) = = elmt_translate )
2014-10-15 18:04:37 +02:00
translatedValue + = g_L10n . Translate ( grandchild . GetText ( ) ) ;
2014-06-14 18:12:41 +02:00
else if ( grandchild . GetNodeName ( ) = = elmt_keep )
translatedValue + = grandchild . GetText ( ) ;
2014-04-20 22:03:57 +02:00
}
Move static GUI<>::SetSetting operating on IGUIObject to a member IGUIObject::SetSetting.
Remove PSERROR codes from SetSetting (let std::map throw an out_of_range
if a caller wants to Set a setting that doesn't exist without having
checked with SettingExists, equal to GetSetting from 92b6cdfeab).
That also simplifies std::function SetSettingWrap construct from
0a7d0ecdde to void IGUIObject::SettingChanged.
Don't trigger debug_warn or exceptions in GUITooltip::ShowTooltip if the
XML author specified wrong tooltip input, and dodge another
dynamic_cast.
Rename existing IGUIObject::SetSetting to
IGUIObject::SetSettingFromString and comment that it is purposed for
parsing XML files.
Remove SetSetting default value, so that authors are made aware
explicitly of the need to decide the function broadcasting a message,
refs d87057b1c0, 719f2d7967, ...
Change const bool& SkipMessage to const bool SendMessage, so that a
positive value relates to a positive action.
Clean AddSettings whitespace and integer types.
Differential Revision: https://code.wildfiregames.com/D2231
Tested on: gcc 9.1.0, clang 8.0.1, Jenkins
Comments By: Philip on IRC on 2010-07-24 on GUIUtil being ugly, in case
that one counts
This was SVN commit r22796.
2019-08-28 13:21:11 +02:00
object - > SetSettingFromString ( attributeName , translatedValue . FromUTF8 ( ) , false ) ;
2014-06-14 18:12:41 +02:00
}
else if ( element_name = = elmt_include )
{
CStrW filename ( child . GetAttributes ( ) . GetNamedItem ( attr_file ) . FromUTF8 ( ) ) ;
2014-06-17 15:03:55 +02:00
CStrW directory ( child . GetAttributes ( ) . GetNamedItem ( attr_directory ) . FromUTF8 ( ) ) ;
if ( ! filename . empty ( ) )
2014-04-20 22:03:57 +02:00
{
2014-06-17 15:03:55 +02:00
if ( ! directory . empty ( ) )
2015-01-22 21:37:38 +01:00
LOGWARNING ( " GUI: Include element found with file name (%s) and directory name (%s). Only the file will be processed. " , utf8_from_wstring ( filename ) , utf8_from_wstring ( directory ) ) ;
2014-06-14 18:12:41 +02:00
2014-06-17 15:03:55 +02:00
Paths . insert ( filename ) ;
2014-06-14 18:12:41 +02:00
2021-05-04 17:17:50 +02:00
CXeromyces xeroIncluded ;
if ( xeroIncluded . Load ( g_VFS , filename , " gui " ) ! = PSRETURN_OK )
2014-06-17 15:03:55 +02:00
{
2015-01-22 21:37:38 +01:00
LOGERROR ( " GUI: Error reading included XML: '%s' " , utf8_from_wstring ( filename ) ) ;
2014-06-17 15:03:55 +02:00
continue ;
}
2014-06-14 18:12:41 +02:00
2021-05-04 17:17:50 +02:00
XMBElement node = xeroIncluded . GetRoot ( ) ;
if ( node . GetNodeName ( ) ! = xeroIncluded . GetElementID ( " object " ) )
2014-06-14 18:12:41 +02:00
{
2015-01-22 21:37:38 +01:00
LOGERROR ( " GUI: Error reading included XML: '%s', root element must have be of type 'object'. " , utf8_from_wstring ( filename ) ) ;
2014-06-14 18:12:41 +02:00
continue ;
}
2015-06-01 02:29:23 +02:00
if ( nesting_depth + 1 > = MAX_OBJECT_DEPTH )
{
LOGERROR ( " GUI: Too many nested GUI includes. Probably caused by a recursive include attribute. Abort rendering '%s'. " , utf8_from_wstring ( filename ) ) ;
continue ;
}
2021-05-04 17:17:50 +02:00
Xeromyces_ReadObject ( xeroIncluded , node , object , NameSubst , Paths , nesting_depth + 1 ) ;
2014-04-20 22:03:57 +02:00
}
2014-06-17 15:03:55 +02:00
else if ( ! directory . empty ( ) )
{
2015-06-01 02:29:23 +02:00
if ( nesting_depth + 1 > = MAX_OBJECT_DEPTH )
{
LOGERROR ( " GUI: Too many nested GUI includes. Probably caused by a recursive include attribute. Abort rendering '%s'. " , utf8_from_wstring ( directory ) ) ;
continue ;
}
2014-06-17 15:03:55 +02:00
VfsPaths pathnames ;
vfs : : GetPathnames ( g_VFS , directory , L " *.xml " , pathnames ) ;
2015-06-01 02:29:23 +02:00
for ( const VfsPath & path : pathnames )
2014-06-17 15:03:55 +02:00
{
// as opposed to loading scripts, don't care if it's loaded before
// one might use the same parts of the GUI in different situations
2015-06-01 02:29:23 +02:00
Paths . insert ( path ) ;
2021-05-04 17:17:50 +02:00
CXeromyces xeroIncluded ;
if ( xeroIncluded . Load ( g_VFS , path , " gui " ) ! = PSRETURN_OK )
2014-06-17 15:03:55 +02:00
{
2015-06-01 02:29:23 +02:00
LOGERROR ( " GUI: Error reading included XML: '%s' " , path . string8 ( ) ) ;
2014-06-17 15:03:55 +02:00
continue ;
}
2021-05-04 17:17:50 +02:00
XMBElement node = xeroIncluded . GetRoot ( ) ;
if ( node . GetNodeName ( ) ! = xeroIncluded . GetElementID ( " object " ) )
2014-06-17 15:03:55 +02:00
{
2015-06-01 02:29:23 +02:00
LOGERROR ( " GUI: Error reading included XML: '%s', root element must have be of type 'object'. " , path . string8 ( ) ) ;
2014-06-17 15:03:55 +02:00
continue ;
}
2021-05-04 17:17:50 +02:00
Xeromyces_ReadObject ( xeroIncluded , node , object , NameSubst , Paths , nesting_depth + 1 ) ;
2014-06-17 15:03:55 +02:00
}
}
else
2015-01-22 21:36:24 +01:00
LOGERROR ( " GUI: 'include' XML element must have valid 'file' or 'directory' attribute found. (object %s) " , object - > GetPresentableName ( ) . c_str ( ) ) ;
2014-04-20 22:03:57 +02:00
}
2005-04-07 11:13:10 +02:00
else
{
// Try making the object read the tag.
2021-05-04 17:17:50 +02:00
if ( ! object - > HandleAdditionalChildren ( xmb , child ) )
2015-01-22 21:36:24 +01:00
LOGERROR ( " GUI: (object: %s) Reading unknown children for its type " , object - > GetPresentableName ( ) . c_str ( ) ) ;
2005-04-07 11:13:10 +02:00
}
2015-08-21 19:08:41 +02:00
}
2003-11-03 17:22:45 +01:00
Change GUI Object Setting values to be members of the IGUIObject inheriting class, rename AddSetting to RegisterSetting.
Improves performance for Draw calls by 3-5% according to a shady
benchmark.
Improves memory layout, since the values are not on the heap anymore but
in the using class.
Reduces complexity of the implementation and increases type safety.
Allows specifying default values at setting value construction time,
refs D2242.
Inspired by Vladislav introducing members that cached GetSetting values
in c016a74309/D325, refs #4039, ee38f0db37/D763, refs 4225,
a1c4c23ce4/D474, D406, which were formerly proposed to be removed in
D2241.
Differential Revision: https://code.wildfiregames.com/D2313
Tested on: clang 8.0.1, gcc 9.1.0, Jenkins vs2015
Comments By: Vladislav
This was SVN commit r23005.
2019-09-27 14:49:59 +02:00
object - > AdditionalChildrenHandled ( ) ;
2003-12-27 07:26:03 +01:00
if ( ! ManuallySetZ )
{
// Set it automatically to 10 plus its parents
Change GUI Object Setting values to be members of the IGUIObject inheriting class, rename AddSetting to RegisterSetting.
Improves performance for Draw calls by 3-5% according to a shady
benchmark.
Improves memory layout, since the values are not on the heap anymore but
in the using class.
Reduces complexity of the implementation and increases type safety.
Allows specifying default values at setting value construction time,
refs D2242.
Inspired by Vladislav introducing members that cached GetSetting values
in c016a74309/D325, refs #4039, ee38f0db37/D763, refs 4225,
a1c4c23ce4/D474, D406, which were formerly proposed to be removed in
D2241.
Differential Revision: https://code.wildfiregames.com/D2313
Tested on: clang 8.0.1, gcc 9.1.0, Jenkins vs2015
Comments By: Vladislav
This was SVN commit r23005.
2019-09-27 14:49:59 +02:00
if ( object - > m_Absolute )
2015-06-01 02:29:23 +02:00
// If the object is absolute, we'll have to get the parent's Z buffered,
// and add to that!
2021-05-06 10:22:37 +02:00
object - > m_Z . Set ( pParent - > GetBufferedZ ( ) + 10.f , false ) ;
2003-12-27 07:26:03 +01:00
else
2015-06-01 02:29:23 +02:00
// If the object is relative, then we'll just store Z as "10"
2021-05-06 10:22:37 +02:00
object - > m_Z . Set ( 10.f , false ) ;
2003-12-27 07:26:03 +01:00
}
2019-10-11 18:20:50 +02:00
if ( ! AddObject ( * pParent , * object ) )
2021-05-04 17:17:50 +02:00
{
2019-10-11 18:20:50 +02:00
delete object ;
2021-05-04 17:17:50 +02:00
return nullptr ;
}
return object ;
2003-11-03 17:22:45 +01:00
}
2021-05-04 17:17:50 +02:00
void CGUI : : Xeromyces_ReadRepeat ( const XMBData & xmb , XMBElement element , IGUIObject * pParent , std : : vector < std : : pair < CStr , CStr > > & NameSubst , std : : unordered_set < VfsPath > & Paths , u32 nesting_depth )
2010-02-28 22:36:25 +01:00
{
2021-05-04 17:17:50 +02:00
# define ELMT(x) int elmt_##x = xmb.GetElementID(#x)
# define ATTR(x) int attr_##x = xmb.GetAttributeID(#x)
2010-02-28 22:36:25 +01:00
ELMT ( object ) ;
ATTR ( count ) ;
2014-12-10 21:10:02 +01:00
ATTR ( var ) ;
2010-02-28 22:36:25 +01:00
2021-05-04 17:17:50 +02:00
XMBAttributeList attributes = element . GetAttributes ( ) ;
2010-02-28 22:36:25 +01:00
int count = CStr ( attributes . GetNamedItem ( attr_count ) ) . ToInt ( ) ;
2014-12-10 21:10:02 +01:00
CStr var ( " [ " + attributes . GetNamedItem ( attr_var ) + " ] " ) ;
if ( var . size ( ) < 3 )
var = " [n] " ;
2010-02-28 22:36:25 +01:00
for ( int n = 0 ; n < count ; + + n )
{
2015-08-21 19:08:41 +02:00
NameSubst . emplace_back ( var , " [ " + CStr : : FromInt ( n ) + " ] " ) ;
2010-02-28 22:36:25 +01:00
2021-05-04 17:17:50 +02:00
XERO_ITER_EL ( element , child )
2010-02-28 22:36:25 +01:00
{
if ( child . GetNodeName ( ) = = elmt_object )
2021-05-04 17:17:50 +02:00
Xeromyces_ReadObject ( xmb , child , pParent , NameSubst , Paths , nesting_depth ) ;
2010-02-28 22:36:25 +01:00
}
2014-12-10 21:10:02 +01:00
NameSubst . pop_back ( ) ;
2010-02-28 22:36:25 +01:00
}
}
2021-05-04 17:17:50 +02:00
void CGUI : : Xeromyces_ReadScript ( const XMBData & xmb , XMBElement element , std : : unordered_set < VfsPath > & Paths )
2004-07-11 18:22:35 +02:00
{
// Check for a 'file' parameter
2021-05-04 17:17:50 +02:00
CStrW fileAttr ( element . GetAttributes ( ) . GetNamedItem ( xmb . GetAttributeID ( " file " ) ) . FromUTF8 ( ) ) ;
2004-07-11 18:22:35 +02:00
2004-07-11 20:18:54 +02:00
// If there is a file specified, open and execute it
2021-05-04 17:17:50 +02:00
if ( ! fileAttr . empty ( ) )
2009-12-03 21:17:22 +01:00
{
2021-05-04 17:17:50 +02:00
if ( ! VfsPath ( fileAttr ) . IsDirectory ( ) )
2020-01-20 10:58:03 +01:00
{
2021-05-04 17:17:50 +02:00
Paths . insert ( fileAttr ) ;
m_ScriptInterface - > LoadGlobalScriptFile ( fileAttr ) ;
2020-01-20 10:58:03 +01:00
}
else
2021-05-04 17:17:50 +02:00
LOGERROR ( " GUI: Script path %s is not a file path " , fileAttr . ToUTF8 ( ) . c_str ( ) ) ;
2009-12-03 21:17:22 +01:00
}
2004-07-11 18:22:35 +02:00
2014-06-11 19:14:35 +02:00
// If it has a directory attribute, read all JS files in that directory
2021-05-04 17:17:50 +02:00
CStrW directoryAttr ( element . GetAttributes ( ) . GetNamedItem ( xmb . GetAttributeID ( " directory " ) ) . FromUTF8 ( ) ) ;
if ( ! directoryAttr . empty ( ) )
2014-06-11 19:14:35 +02:00
{
2021-05-04 17:17:50 +02:00
if ( VfsPath ( directoryAttr ) . IsDirectory ( ) )
2014-06-11 19:14:35 +02:00
{
2020-01-20 10:58:03 +01:00
VfsPaths pathnames ;
2021-05-04 17:17:50 +02:00
vfs : : GetPathnames ( g_VFS , directoryAttr , L " *.js " , pathnames ) ;
2020-01-20 10:58:03 +01:00
for ( const VfsPath & path : pathnames )
{
// Only load new files (so when the insert succeeds)
if ( Paths . insert ( path ) . second )
m_ScriptInterface - > LoadGlobalScriptFile ( path ) ;
}
2014-06-11 19:14:35 +02:00
}
2020-01-20 10:58:03 +01:00
else
2021-05-04 17:17:50 +02:00
LOGERROR ( " GUI: Script path %s is not a directory path " , directoryAttr . ToUTF8 ( ) . c_str ( ) ) ;
2014-06-11 19:14:35 +02:00
}
2021-05-04 17:17:50 +02:00
CStr code ( element . GetText ( ) ) ;
2019-10-11 21:30:28 +02:00
if ( ! code . empty ( ) )
2020-11-30 10:03:20 +01:00
m_ScriptInterface - > LoadGlobalScript ( L " Some XML file " , code ) ;
2004-07-11 18:22:35 +02:00
}
2021-05-04 17:17:50 +02:00
void CGUI : : Xeromyces_ReadSprite ( const XMBData & xmb , XMBElement element )
2003-11-03 17:22:45 +01:00
{
2014-01-03 21:19:43 +01:00
CGUISprite * Sprite = new CGUISprite ;
2015-08-21 19:08:41 +02:00
2003-11-03 17:22:45 +01:00
// Get name, we know it exists because of DTD requirements
2021-05-04 17:17:50 +02:00
CStr name = element . GetAttributes ( ) . GetNamedItem ( xmb . GetAttributeID ( " name " ) ) ;
2003-11-03 17:22:45 +01:00
2004-12-21 14:37:24 +01:00
if ( m_Sprites . find ( name ) ! = m_Sprites . end ( ) )
2015-01-22 21:36:24 +01:00
LOGWARNING ( " GUI sprite name '%s' used more than once; first definition will be discarded " , name . c_str ( ) ) ;
2004-12-21 14:37:24 +01:00
2019-08-20 12:51:29 +02:00
// shared_ptr to link the effect to every image, faster than copy.
2019-08-17 13:52:57 +02:00
std : : shared_ptr < SGUIImageEffects > effects ;
2004-12-18 14:32:00 +01:00
2021-05-04 17:17:50 +02:00
for ( XMBElement child : element . GetChildNodes ( ) )
2003-11-03 17:22:45 +01:00
{
2021-05-04 17:17:50 +02:00
std : : string_view ElementName ( xmb . GetElementStringView ( child . GetNodeName ( ) ) ) ;
2004-12-18 14:32:00 +01:00
if ( ElementName = = " image " )
2021-05-04 17:17:50 +02:00
Xeromyces_ReadImage ( xmb , child , * Sprite ) ;
2004-12-18 14:32:00 +01:00
else if ( ElementName = = " effect " )
{
2009-12-03 21:17:22 +01:00
if ( effects )
2015-01-22 21:31:30 +01:00
LOGERROR ( " GUI <sprite> must not have more than one <effect> " ) ;
2009-12-03 21:17:22 +01:00
else
{
2019-08-17 13:52:57 +02:00
effects = std : : make_shared < SGUIImageEffects > ( ) ;
2021-05-04 17:17:50 +02:00
Xeromyces_ReadEffects ( xmb , child , * effects ) ;
2009-12-03 21:17:22 +01:00
}
2004-12-18 14:32:00 +01:00
}
else
2009-11-03 22:46:35 +01:00
debug_warn ( L " Invalid data - DTD shouldn't allow this " ) ;
2003-11-03 17:22:45 +01:00
}
2004-12-18 14:32:00 +01:00
// Apply the effects to every image (unless the image overrides it with
// different effects)
if ( effects )
2015-08-21 19:08:41 +02:00
for ( SGUIImage * const & img : Sprite - > m_Images )
if ( ! img - > m_Effects )
2019-08-17 13:52:57 +02:00
img - > m_Effects = effects ;
2004-12-18 14:32:00 +01:00
MOVABLE idiom, const CGUI struct maps, in place move construction instead of copying temporaries during CGUI XML loading and GenerateText.
Introduce MOVABLE idiom indicating that a class can use move semantics.
Make values of CGUI struct maps holding XML data const to ensure at the
root that the data is not modified.
Use NONCOPYABLE and MOVABLE for SGUIIcon and SGUIStyle to enforce the
non-copy policy on the compiler level (until someone changes the GUI
design to make modifications needed).
As indicated by that:
Replace copy operations by in place move operations for these CGUI
struct maps in the CGUI Xeromyces XML loading functions.
Replace copy operations by const references for CSize and SGUIIcon in
CGUIString::GenerateTextCall and CGUI::GenerateText.
This avoids copying of non primitive members, such as the strings and
containers of strings.
Further related cleanup to be kept separated for auditability.
Differential Revision: https://code.wildfiregames.com/D2163
Few comments on IRC by: wraitii, Itms
Tested on: gcc 9, Jenkins, partially VS2015
Refs #1984,
NONCOPYABLE CGUISpriteInstances: 0a7d0ecdde, 8f4f8e240f, c19f3608a5
NONCOPYABLE Image, Sprite: fb8032043b
NONCOPYABLE GUI page: 94c57085e9
NONCOPYABLE GUIManager: 7c2e9027c2
NONCOPYABLE macro: 16ccae10cd
This was SVN commit r22637.
2019-08-09 19:25:55 +02:00
m_Sprites . erase ( name ) ;
m_Sprites . emplace ( name , Sprite ) ;
2003-11-03 17:22:45 +01:00
}
2021-05-04 17:17:50 +02:00
void CGUI : : Xeromyces_ReadImage ( const XMBData & xmb , XMBElement element , CGUISprite & parent )
2003-11-03 17:22:45 +01:00
{
2019-10-01 17:06:13 +02:00
SGUIImage * Image = new SGUIImage ( ) ;
2015-08-21 19:08:41 +02:00
2003-12-27 07:26:03 +01:00
// TODO Gee: Setup defaults here (or maybe they are in the SGUIImage ctor)
2003-11-03 17:22:45 +01:00
2021-05-04 17:17:50 +02:00
for ( XMBAttribute attr : element . GetAttributes ( ) )
2003-11-03 17:22:45 +01:00
{
2021-05-04 17:17:50 +02:00
std : : string_view attr_name ( xmb . GetAttributeStringView ( attr . Name ) ) ;
2014-06-11 19:14:35 +02:00
CStrW attr_value ( attr . Value . FromUTF8 ( ) ) ;
2003-11-03 17:22:45 +01:00
2004-07-10 22:33:42 +02:00
if ( attr_name = = " texture " )
2003-11-03 17:22:45 +01:00
{
2014-01-03 21:19:43 +01:00
Image - > m_TextureName = VfsPath ( " art/textures/ui " ) / attr_value ;
2003-11-03 17:22:45 +01:00
}
2015-08-21 19:08:41 +02:00
else if ( attr_name = = " size " )
2003-11-03 17:22:45 +01:00
{
2019-10-01 17:06:13 +02:00
Image - > m_Size . FromString ( attr . Value ) ;
2003-12-01 08:06:55 +01:00
}
2015-08-21 19:08:41 +02:00
else if ( attr_name = = " texture_size " )
2004-07-14 00:48:53 +02:00
{
2019-10-01 17:06:13 +02:00
Image - > m_TextureSize . FromString ( attr . Value ) ;
2004-07-14 00:48:53 +02:00
}
2015-08-21 19:08:41 +02:00
else if ( attr_name = = " real_texture_placement " )
2004-09-02 05:02:32 +02:00
{
CRect rect ;
2019-08-29 11:07:29 +02:00
if ( ! ParseString < CRect > ( this , attr_value , rect ) )
2015-01-22 21:37:38 +01:00
LOGERROR ( " GUI: Error parsing '%s' ( \" %s \" ) " , attr_name , utf8_from_wstring ( attr_value ) ) ;
2015-08-21 19:08:41 +02:00
else
Image - > m_TexturePlacementInFile = rect ;
2004-09-02 05:02:32 +02:00
}
2015-08-21 19:08:41 +02:00
else if ( attr_name = = " fixed_h_aspect_ratio " )
2011-08-20 19:17:53 +02:00
{
float val ;
2019-08-29 11:07:29 +02:00
if ( ! ParseString < float > ( this , attr_value , val ) )
2015-01-22 21:37:38 +01:00
LOGERROR ( " GUI: Error parsing '%s' ( \" %s \" ) " , attr_name , utf8_from_wstring ( attr_value ) ) ;
2015-08-21 19:08:41 +02:00
else
Image - > m_FixedHAspectRatio = val ;
2011-08-20 19:17:53 +02:00
}
2015-08-21 19:08:41 +02:00
else if ( attr_name = = " round_coordinates " )
2011-08-20 19:17:53 +02:00
{
bool b ;
2019-08-29 11:07:29 +02:00
if ( ! ParseString < bool > ( this , attr_value , b ) )
2015-01-22 21:37:38 +01:00
LOGERROR ( " GUI: Error parsing '%s' ( \" %s \" ) " , attr_name , utf8_from_wstring ( attr_value ) ) ;
2015-08-21 19:08:41 +02:00
else
Image - > m_RoundCoordinates = b ;
2011-08-20 19:17:53 +02:00
}
2015-08-21 19:08:41 +02:00
else if ( attr_name = = " wrap_mode " )
2011-08-20 19:17:53 +02:00
{
if ( attr_value = = L " repeat " )
2014-01-03 21:19:43 +01:00
Image - > m_WrapMode = GL_REPEAT ;
2011-08-20 19:17:53 +02:00
else if ( attr_value = = L " mirrored_repeat " )
2014-01-03 21:19:43 +01:00
Image - > m_WrapMode = GL_MIRRORED_REPEAT ;
2011-08-20 19:17:53 +02:00
else if ( attr_value = = L " clamp_to_edge " )
2014-01-03 21:19:43 +01:00
Image - > m_WrapMode = GL_CLAMP_TO_EDGE ;
2011-08-20 19:17:53 +02:00
else
2015-01-22 21:37:38 +01:00
LOGERROR ( " GUI: Error parsing '%s' ( \" %s \" ) " , attr_name , utf8_from_wstring ( attr_value ) ) ;
2011-08-20 19:17:53 +02:00
}
2015-08-21 19:08:41 +02:00
else if ( attr_name = = " z_level " )
2004-05-29 06:06:50 +02:00
{
2004-09-03 07:48:47 +02:00
float z_level ;
2019-08-29 11:07:29 +02:00
if ( ! ParseString < float > ( this , attr_value , z_level ) )
2015-01-22 21:37:38 +01:00
LOGERROR ( " GUI: Error parsing '%s' ( \" %s \" ) " , attr_name , utf8_from_wstring ( attr_value ) ) ;
2015-08-21 19:08:41 +02:00
else
Image - > m_DeltaZ = z_level / 100.f ;
2004-05-29 06:06:50 +02:00
}
2015-08-21 19:08:41 +02:00
else if ( attr_name = = " backcolor " )
2003-12-01 08:06:55 +01:00
{
2019-08-29 11:07:29 +02:00
if ( ! ParseString < CGUIColor > ( this , attr_value , Image - > m_BackColor ) )
2015-01-22 21:37:38 +01:00
LOGERROR ( " GUI: Error parsing '%s' ( \" %s \" ) " , attr_name , utf8_from_wstring ( attr_value ) ) ;
2003-12-01 08:06:55 +01:00
}
2015-08-21 19:08:41 +02:00
else if ( attr_name = = " bordercolor " )
2004-09-06 04:21:21 +02:00
{
2019-08-29 11:07:29 +02:00
if ( ! ParseString < CGUIColor > ( this , attr_value , Image - > m_BorderColor ) )
2015-01-22 21:37:38 +01:00
LOGERROR ( " GUI: Error parsing '%s' ( \" %s \" ) " , attr_name , utf8_from_wstring ( attr_value ) ) ;
2004-09-06 04:21:21 +02:00
}
2015-08-21 19:08:41 +02:00
else if ( attr_name = = " border " )
2004-09-06 04:21:21 +02:00
{
bool b ;
2019-08-29 11:07:29 +02:00
if ( ! ParseString < bool > ( this , attr_value , b ) )
2015-01-22 21:37:38 +01:00
LOGERROR ( " GUI: Error parsing '%s' ( \" %s \" ) " , attr_name , utf8_from_wstring ( attr_value ) ) ;
2015-08-21 19:08:41 +02:00
else
Image - > m_Border = b ;
2004-09-06 04:21:21 +02:00
}
2004-12-18 14:32:00 +01:00
else
2009-11-03 22:46:35 +01:00
debug_warn ( L " Invalid data - DTD shouldn't allow this " ) ;
2004-12-18 14:32:00 +01:00
}
// Look for effects
2021-05-04 17:17:50 +02:00
for ( XMBElement child : element . GetChildNodes ( ) )
2004-12-18 14:32:00 +01:00
{
2021-05-04 17:17:50 +02:00
std : : string_view ElementName ( xmb . GetElementStringView ( child . GetNodeName ( ) ) ) ;
2004-12-18 14:32:00 +01:00
if ( ElementName = = " effect " )
{
2014-01-03 21:19:43 +01:00
if ( Image - > m_Effects )
2015-01-22 21:31:30 +01:00
LOGERROR ( " GUI <image> must not have more than one <effect> " ) ;
2009-12-03 21:17:22 +01:00
else
{
2019-08-17 13:52:57 +02:00
Image - > m_Effects = std : : make_shared < SGUIImageEffects > ( ) ;
2021-05-04 17:17:50 +02:00
Xeromyces_ReadEffects ( xmb , child , * Image - > m_Effects ) ;
2009-12-03 21:17:22 +01:00
}
2004-12-18 14:32:00 +01:00
}
else
2009-11-03 22:46:35 +01:00
debug_warn ( L " Invalid data - DTD shouldn't allow this " ) ;
2003-11-03 17:22:45 +01:00
}
2003-12-01 08:06:55 +01:00
2014-06-14 18:12:41 +02:00
parent . AddImage ( Image ) ;
2003-11-03 17:22:45 +01:00
}
2003-12-01 08:06:55 +01:00
2021-05-04 17:17:50 +02:00
void CGUI : : Xeromyces_ReadEffects ( const XMBData & xmb , XMBElement element , SGUIImageEffects & effects )
2004-12-18 14:32:00 +01:00
{
2021-05-04 17:17:50 +02:00
for ( XMBAttribute attr : element . GetAttributes ( ) )
2004-12-18 14:32:00 +01:00
{
2021-05-04 17:17:50 +02:00
std : : string_view attr_name ( xmb . GetAttributeStringView ( attr . Name ) ) ;
2012-02-08 21:43:38 +01:00
if ( attr_name = = " add_color " )
{
2019-08-21 12:12:33 +02:00
if ( ! effects . m_AddColor . ParseString ( * this , attr . Value , 0 ) )
2019-08-13 17:42:14 +02:00
LOGERROR ( " GUI: Error parsing '%s' ( \" %s \" ) " , attr_name , attr . Value ) ;
2012-02-08 21:43:38 +01:00
}
else if ( attr_name = = " grayscale " )
effects . m_Greyscale = true ;
2004-12-19 13:20:04 +01:00
else
2009-11-03 22:46:35 +01:00
debug_warn ( L " Invalid data - DTD shouldn't allow this " ) ;
2004-12-18 14:32:00 +01:00
}
}
2021-05-04 17:17:50 +02:00
void CGUI : : Xeromyces_ReadStyle ( const XMBData & xmb , XMBElement element )
2003-12-01 08:06:55 +01:00
{
SGUIStyle style ;
CStr name ;
2014-06-14 18:12:41 +02:00
2021-05-04 17:17:50 +02:00
for ( XMBAttribute attr : element . GetAttributes ( ) )
2003-12-01 08:06:55 +01:00
{
2021-05-04 17:17:50 +02:00
std : : string_view attr_name ( xmb . GetAttributeStringView ( attr . Name ) ) ;
2003-12-01 08:06:55 +01:00
// The "name" setting is actually the name of the style
// and not a new default
2004-07-10 22:33:42 +02:00
if ( attr_name = = " name " )
2011-02-17 21:08:20 +01:00
name = attr . Value ;
2003-12-01 08:06:55 +01:00
else
XMB Improvements, parse JS into XMB, make strings more efficient.
XMB format is bumped to 4, invalidating all cached files. The
differences are:
- element/attribute names are stored after the elements themselves, and
not before. This allows writing XMB data in one pass instead of two.
- names themselves becomes offsets (instead of arbitrary integers),
making getting the string from the int name much more efficient.
XMBFile is renamed to XMBData to clarify that it does not, in fact,
refer to a file on disk.
XMBData::GetElementString is also changed to return a const char*, thus
not creating an std::string. A string_view version is added where
convenient.
The XML->XMB and JS->XMB conversion functions and the corresponding
storage are moved to `ps/XMB`, since that format doesn't particularly
relate to XML. CXeromyces becomes lighter and more focused as a result.
The XML->XMB conversion also benefits from the above streamlining.
Note that in a few cases, string_view gets printed to CLogger via
data(), which is generally not legal, but we know that the strings are
null-terminated here. Our libfmt (version 4) doesn't support
string_view, that would be v5.
Differential Revision: https://code.wildfiregames.com/D3909
This was SVN commit r25375.
2021-05-04 15:02:34 +02:00
style . m_SettingsDefaults . emplace ( std : : string ( attr_name ) , attr . Value . FromUTF8 ( ) ) ;
2003-12-01 08:06:55 +01:00
}
MOVABLE idiom, const CGUI struct maps, in place move construction instead of copying temporaries during CGUI XML loading and GenerateText.
Introduce MOVABLE idiom indicating that a class can use move semantics.
Make values of CGUI struct maps holding XML data const to ensure at the
root that the data is not modified.
Use NONCOPYABLE and MOVABLE for SGUIIcon and SGUIStyle to enforce the
non-copy policy on the compiler level (until someone changes the GUI
design to make modifications needed).
As indicated by that:
Replace copy operations by in place move operations for these CGUI
struct maps in the CGUI Xeromyces XML loading functions.
Replace copy operations by const references for CSize and SGUIIcon in
CGUIString::GenerateTextCall and CGUI::GenerateText.
This avoids copying of non primitive members, such as the strings and
containers of strings.
Further related cleanup to be kept separated for auditability.
Differential Revision: https://code.wildfiregames.com/D2163
Few comments on IRC by: wraitii, Itms
Tested on: gcc 9, Jenkins, partially VS2015
Refs #1984,
NONCOPYABLE CGUISpriteInstances: 0a7d0ecdde, 8f4f8e240f, c19f3608a5
NONCOPYABLE Image, Sprite: fb8032043b
NONCOPYABLE GUI page: 94c57085e9
NONCOPYABLE GUIManager: 7c2e9027c2
NONCOPYABLE macro: 16ccae10cd
This was SVN commit r22637.
2019-08-09 19:25:55 +02:00
m_Styles . erase ( name ) ;
m_Styles . emplace ( name , std : : move ( style ) ) ;
2003-12-01 08:06:55 +01:00
}
2003-12-27 07:26:03 +01:00
2021-05-04 17:17:50 +02:00
void CGUI : : Xeromyces_ReadScrollBarStyle ( const XMBData & xmb , XMBElement element )
2003-12-27 07:26:03 +01:00
{
SGUIScrollBarStyle scrollbar ;
CStr name ;
2014-02-19 22:59:07 +01:00
// Setup some defaults.
scrollbar . m_MinimumBarSize = 0.f ;
2014-03-05 02:06:13 +01:00
// Using 1.0e10 as a substitute for infinity
scrollbar . m_MaximumBarSize = 1.0e10 ;
2014-02-19 22:59:07 +01:00
scrollbar . m_UseEdgeButtons = false ;
2021-05-04 17:17:50 +02:00
for ( XMBAttribute attr : element . GetAttributes ( ) )
2003-12-27 07:26:03 +01:00
{
2021-05-04 17:17:50 +02:00
std : : string_view attr_name ( xmb . GetAttributeStringView ( attr . Name ) ) ;
2015-08-21 19:08:41 +02:00
CStr attr_value ( attr . Value ) ;
2003-12-27 07:26:03 +01:00
2004-10-31 23:00:01 +01:00
if ( attr_value = = " null " )
2004-05-29 06:06:50 +02:00
continue ;
2004-07-10 22:33:42 +02:00
if ( attr_name = = " name " )
2003-12-27 07:26:03 +01:00
name = attr_value ;
2015-08-21 19:08:41 +02:00
else if ( attr_name = = " show_edge_buttons " )
2013-11-07 21:07:24 +01:00
{
bool b ;
2019-08-29 11:07:29 +02:00
if ( ! ParseString < bool > ( this , attr_value . FromUTF8 ( ) , b ) )
2015-01-22 21:37:38 +01:00
LOGERROR ( " GUI: Error parsing '%s' ( \" %s \" ) " , attr_name , attr_value ) ;
2013-11-07 21:07:24 +01:00
else
scrollbar . m_UseEdgeButtons = b ;
}
2015-08-21 19:08:41 +02:00
else if ( attr_name = = " width " )
2003-12-27 07:26:03 +01:00
{
2004-09-03 07:48:47 +02:00
float f ;
2019-08-29 11:07:29 +02:00
if ( ! ParseString < float > ( this , attr_value . FromUTF8 ( ) , f ) )
2015-01-22 21:37:38 +01:00
LOGERROR ( " GUI: Error parsing '%s' ( \" %s \" ) " , attr_name , attr_value ) ;
2009-12-03 21:17:22 +01:00
else
scrollbar . m_Width = f ;
2003-12-27 07:26:03 +01:00
}
2015-08-21 19:08:41 +02:00
else if ( attr_name = = " minimum_bar_size " )
2004-05-29 06:06:50 +02:00
{
2004-09-03 07:48:47 +02:00
float f ;
2019-08-29 11:07:29 +02:00
if ( ! ParseString < float > ( this , attr_value . FromUTF8 ( ) , f ) )
2015-01-22 21:37:38 +01:00
LOGERROR ( " GUI: Error parsing '%s' ( \" %s \" ) " , attr_name , attr_value ) ;
2009-12-03 21:17:22 +01:00
else
scrollbar . m_MinimumBarSize = f ;
2004-05-29 06:06:50 +02:00
}
2015-08-21 19:08:41 +02:00
else if ( attr_name = = " maximum_bar_size " )
2013-08-26 06:17:26 +02:00
{
float f ;
2019-08-29 11:07:29 +02:00
if ( ! ParseString < float > ( this , attr_value . FromUTF8 ( ) , f ) )
2015-01-22 21:37:38 +01:00
LOGERROR ( " GUI: Error parsing '%s' ( \" %s \" ) " , attr_name , attr_value ) ;
2013-08-26 06:17:26 +02:00
else
scrollbar . m_MaximumBarSize = f ;
}
2015-08-21 19:08:41 +02:00
else if ( attr_name = = " sprite_button_top " )
2004-05-29 06:06:50 +02:00
scrollbar . m_SpriteButtonTop = attr_value ;
2015-08-21 19:08:41 +02:00
else if ( attr_name = = " sprite_button_top_pressed " )
2004-05-29 06:06:50 +02:00
scrollbar . m_SpriteButtonTopPressed = attr_value ;
2015-08-21 19:08:41 +02:00
else if ( attr_name = = " sprite_button_top_disabled " )
2004-05-29 06:06:50 +02:00
scrollbar . m_SpriteButtonTopDisabled = attr_value ;
2015-08-21 19:08:41 +02:00
else if ( attr_name = = " sprite_button_top_over " )
2004-05-29 06:06:50 +02:00
scrollbar . m_SpriteButtonTopOver = attr_value ;
2015-08-21 19:08:41 +02:00
else if ( attr_name = = " sprite_button_bottom " )
2004-05-29 06:06:50 +02:00
scrollbar . m_SpriteButtonBottom = attr_value ;
2015-08-21 19:08:41 +02:00
else if ( attr_name = = " sprite_button_bottom_pressed " )
2004-05-29 06:06:50 +02:00
scrollbar . m_SpriteButtonBottomPressed = attr_value ;
2015-08-21 19:08:41 +02:00
else if ( attr_name = = " sprite_button_bottom_disabled " )
2004-05-29 06:06:50 +02:00
scrollbar . m_SpriteButtonBottomDisabled = attr_value ;
2015-08-21 19:08:41 +02:00
else if ( attr_name = = " sprite_button_bottom_over " )
2004-05-29 06:06:50 +02:00
scrollbar . m_SpriteButtonBottomOver = attr_value ;
2015-08-21 19:08:41 +02:00
else if ( attr_name = = " sprite_back_vertical " )
2004-05-29 06:06:50 +02:00
scrollbar . m_SpriteBackVertical = attr_value ;
2015-08-21 19:08:41 +02:00
else if ( attr_name = = " sprite_bar_vertical " )
2004-05-29 06:06:50 +02:00
scrollbar . m_SpriteBarVertical = attr_value ;
2015-08-21 19:08:41 +02:00
else if ( attr_name = = " sprite_bar_vertical_over " )
2004-05-29 06:06:50 +02:00
scrollbar . m_SpriteBarVerticalOver = attr_value ;
2015-08-21 19:08:41 +02:00
else if ( attr_name = = " sprite_bar_vertical_pressed " )
2004-05-29 06:06:50 +02:00
scrollbar . m_SpriteBarVerticalPressed = attr_value ;
2004-08-31 04:09:58 +02:00
}
2004-05-29 06:06:50 +02:00
MOVABLE idiom, const CGUI struct maps, in place move construction instead of copying temporaries during CGUI XML loading and GenerateText.
Introduce MOVABLE idiom indicating that a class can use move semantics.
Make values of CGUI struct maps holding XML data const to ensure at the
root that the data is not modified.
Use NONCOPYABLE and MOVABLE for SGUIIcon and SGUIStyle to enforce the
non-copy policy on the compiler level (until someone changes the GUI
design to make modifications needed).
As indicated by that:
Replace copy operations by in place move operations for these CGUI
struct maps in the CGUI Xeromyces XML loading functions.
Replace copy operations by const references for CSize and SGUIIcon in
CGUIString::GenerateTextCall and CGUI::GenerateText.
This avoids copying of non primitive members, such as the strings and
containers of strings.
Further related cleanup to be kept separated for auditability.
Differential Revision: https://code.wildfiregames.com/D2163
Few comments on IRC by: wraitii, Itms
Tested on: gcc 9, Jenkins, partially VS2015
Refs #1984,
NONCOPYABLE CGUISpriteInstances: 0a7d0ecdde, 8f4f8e240f, c19f3608a5
NONCOPYABLE Image, Sprite: fb8032043b
NONCOPYABLE GUI page: 94c57085e9
NONCOPYABLE GUIManager: 7c2e9027c2
NONCOPYABLE macro: 16ccae10cd
This was SVN commit r22637.
2019-08-09 19:25:55 +02:00
m_ScrollBarStyles . erase ( name ) ;
m_ScrollBarStyles . emplace ( name , std : : move ( scrollbar ) ) ;
2004-08-31 04:09:58 +02:00
}
2004-05-29 06:06:50 +02:00
2021-05-04 17:17:50 +02:00
void CGUI : : Xeromyces_ReadIcon ( const XMBData & xmb , XMBElement element )
2004-08-31 04:09:58 +02:00
{
SGUIIcon icon ;
CStr name ;
2004-05-29 06:06:50 +02:00
2021-05-04 17:17:50 +02:00
for ( XMBAttribute attr : element . GetAttributes ( ) )
2004-08-31 04:09:58 +02:00
{
2021-05-04 17:17:50 +02:00
std : : string_view attr_name ( xmb . GetAttributeStringView ( attr . Name ) ) ;
2014-06-11 19:14:35 +02:00
CStr attr_value ( attr . Value ) ;
2004-05-29 06:06:50 +02:00
2004-10-31 23:00:01 +01:00
if ( attr_value = = " null " )
2004-08-31 04:09:58 +02:00
continue ;
2004-05-29 06:06:50 +02:00
2004-08-31 04:09:58 +02:00
if ( attr_name = = " name " )
name = attr_value ;
2014-06-14 18:12:41 +02:00
else if ( attr_name = = " sprite " )
2005-07-25 02:52:03 +02:00
icon . m_SpriteName = attr_value ;
2014-06-14 18:12:41 +02:00
else if ( attr_name = = " size " )
2004-08-31 04:09:58 +02:00
{
2021-03-27 17:08:06 +01:00
CSize2D size ;
if ( ! ParseString < CSize2D > ( this , attr_value . FromUTF8 ( ) , size ) )
2015-01-22 21:37:38 +01:00
LOGERROR ( " Error parsing '%s' ( \" %s \" ) inside <icon>. " , attr_name , attr_value ) ;
2009-12-03 21:17:22 +01:00
else
icon . m_Size = size ;
2004-08-31 04:09:58 +02:00
}
2004-12-18 14:32:00 +01:00
else
2009-11-03 22:46:35 +01:00
debug_warn ( L " Invalid data - DTD shouldn't allow this " ) ;
2003-12-27 07:26:03 +01:00
}
MOVABLE idiom, const CGUI struct maps, in place move construction instead of copying temporaries during CGUI XML loading and GenerateText.
Introduce MOVABLE idiom indicating that a class can use move semantics.
Make values of CGUI struct maps holding XML data const to ensure at the
root that the data is not modified.
Use NONCOPYABLE and MOVABLE for SGUIIcon and SGUIStyle to enforce the
non-copy policy on the compiler level (until someone changes the GUI
design to make modifications needed).
As indicated by that:
Replace copy operations by in place move operations for these CGUI
struct maps in the CGUI Xeromyces XML loading functions.
Replace copy operations by const references for CSize and SGUIIcon in
CGUIString::GenerateTextCall and CGUI::GenerateText.
This avoids copying of non primitive members, such as the strings and
containers of strings.
Further related cleanup to be kept separated for auditability.
Differential Revision: https://code.wildfiregames.com/D2163
Few comments on IRC by: wraitii, Itms
Tested on: gcc 9, Jenkins, partially VS2015
Refs #1984,
NONCOPYABLE CGUISpriteInstances: 0a7d0ecdde, 8f4f8e240f, c19f3608a5
NONCOPYABLE Image, Sprite: fb8032043b
NONCOPYABLE GUI page: 94c57085e9
NONCOPYABLE GUIManager: 7c2e9027c2
NONCOPYABLE macro: 16ccae10cd
This was SVN commit r22637.
2019-08-09 19:25:55 +02:00
m_Icons . erase ( name ) ;
m_Icons . emplace ( name , std : : move ( icon ) ) ;
2003-12-27 07:26:03 +01:00
}
2004-12-21 14:37:24 +01:00
2021-05-04 17:17:50 +02:00
void CGUI : : Xeromyces_ReadTooltip ( const XMBData & xmb , XMBElement element )
2004-12-21 14:37:24 +01:00
{
2019-08-21 12:12:33 +02:00
IGUIObject * object = new CTooltip ( * this ) ;
2004-12-21 14:37:24 +01:00
2021-05-04 17:17:50 +02:00
for ( XMBAttribute attr : element . GetAttributes ( ) )
2004-12-21 14:37:24 +01:00
{
2021-05-04 17:17:50 +02:00
std : : string_view attr_name ( xmb . GetAttributeStringView ( attr . Name ) ) ;
2014-06-11 19:14:35 +02:00
CStr attr_value ( attr . Value ) ;
2004-12-21 14:37:24 +01:00
if ( attr_name = = " name " )
2004-12-23 14:56:34 +01:00
object - > SetName ( " __tooltip_ " + attr_value ) ;
2004-12-21 14:37:24 +01:00
else
XMB Improvements, parse JS into XMB, make strings more efficient.
XMB format is bumped to 4, invalidating all cached files. The
differences are:
- element/attribute names are stored after the elements themselves, and
not before. This allows writing XMB data in one pass instead of two.
- names themselves becomes offsets (instead of arbitrary integers),
making getting the string from the int name much more efficient.
XMBFile is renamed to XMBData to clarify that it does not, in fact,
refer to a file on disk.
XMBData::GetElementString is also changed to return a const char*, thus
not creating an std::string. A string_view version is added where
convenient.
The XML->XMB and JS->XMB conversion functions and the corresponding
storage are moved to `ps/XMB`, since that format doesn't particularly
relate to XML. CXeromyces becomes lighter and more focused as a result.
The XML->XMB conversion also benefits from the above streamlining.
Note that in a few cases, string_view gets printed to CLogger via
data(), which is generally not legal, but we know that the strings are
null-terminated here. Our libfmt (version 4) doesn't support
string_view, that would be v5.
Differential Revision: https://code.wildfiregames.com/D3909
This was SVN commit r25375.
2021-05-04 15:02:34 +02:00
object - > SetSettingFromString ( std : : string ( attr_name ) , attr_value . FromUTF8 ( ) , true ) ;
2004-12-21 14:37:24 +01:00
}
2020-12-09 15:39:14 +01:00
if ( ! AddObject ( * m_BaseObject , * object ) )
2019-10-11 18:20:50 +02:00
delete object ;
2004-12-21 14:37:24 +01:00
}
2005-07-25 21:06:18 +02:00
2021-05-04 17:17:50 +02:00
void CGUI : : Xeromyces_ReadColor ( const XMBData & xmb , XMBElement element )
2005-07-25 21:06:18 +02:00
{
2021-05-04 17:17:50 +02:00
XMBAttributeList attributes = element . GetAttributes ( ) ;
CStr name = attributes . GetNamedItem ( xmb . GetAttributeID ( " name " ) ) ;
2005-07-25 21:06:18 +02:00
2015-08-21 19:08:41 +02:00
// Try parsing value
2021-05-04 17:17:50 +02:00
CStr value ( element . GetText ( ) ) ;
2015-08-21 19:08:41 +02:00
if ( value . empty ( ) )
return ;
Stop copying color every draw call for every GUI object using colors.
Avoid color copies for rendering Draw calls in
GUIRenderer::UpdateDrawCallCache, CButton::Draw, CChart::DrawAxes,
CDropDown::Draw, CList::DrawList, COList::DrawList, refs #1984,
8f4f8e240f, 3028551b91, a905fbbc98.
Avoid color copies during XML loading in CGUI::Xeromyces_ReadImage,
CGUI::Xeromyces_ReadEffects, COList::HandleAdditionalChildren.
Add CGUI::HasPreDefinedColor and mark m_PreDefinedColors,
CGUI::GetPreDefinedColor, IGUIButtonBehavior::ChooseColor() as const for
consistency with the other "databases", refs 3028551b91.
Mark CGUIColor as NONCOPYABLE to add compiler errors if there is an
unexplicit copy, refs 3028551b91.
Explicit ugly copy in CGUI::Xeromyces_ReadColor and
CGUIColor::ParseString.
Deregister copying <CGUIColor>GetSetting functions, refs 8f4f8e240f.
Uses the const ref GetSetting from 3dfa23cd25.
This was SVN commit r22694.
2019-08-19 14:53:58 +02:00
CColor color ;
if ( color . ParseString ( value ) )
2005-07-25 21:06:18 +02:00
{
Stop copying color every draw call for every GUI object using colors.
Avoid color copies for rendering Draw calls in
GUIRenderer::UpdateDrawCallCache, CButton::Draw, CChart::DrawAxes,
CDropDown::Draw, CList::DrawList, COList::DrawList, refs #1984,
8f4f8e240f, 3028551b91, a905fbbc98.
Avoid color copies during XML loading in CGUI::Xeromyces_ReadImage,
CGUI::Xeromyces_ReadEffects, COList::HandleAdditionalChildren.
Add CGUI::HasPreDefinedColor and mark m_PreDefinedColors,
CGUI::GetPreDefinedColor, IGUIButtonBehavior::ChooseColor() as const for
consistency with the other "databases", refs 3028551b91.
Mark CGUIColor as NONCOPYABLE to add compiler errors if there is an
unexplicit copy, refs 3028551b91.
Explicit ugly copy in CGUI::Xeromyces_ReadColor and
CGUIColor::ParseString.
Deregister copying <CGUIColor>GetSetting functions, refs 8f4f8e240f.
Uses the const ref GetSetting from 3dfa23cd25.
This was SVN commit r22694.
2019-08-19 14:53:58 +02:00
m_PreDefinedColors . erase ( name ) ;
m_PreDefinedColors . emplace (
std : : piecewise_construct ,
std : : forward_as_tuple ( name ) ,
std : : forward_as_tuple ( color . r , color . g , color . b , color . a ) ) ;
2005-07-25 21:06:18 +02:00
}
Stop copying color every draw call for every GUI object using colors.
Avoid color copies for rendering Draw calls in
GUIRenderer::UpdateDrawCallCache, CButton::Draw, CChart::DrawAxes,
CDropDown::Draw, CList::DrawList, COList::DrawList, refs #1984,
8f4f8e240f, 3028551b91, a905fbbc98.
Avoid color copies during XML loading in CGUI::Xeromyces_ReadImage,
CGUI::Xeromyces_ReadEffects, COList::HandleAdditionalChildren.
Add CGUI::HasPreDefinedColor and mark m_PreDefinedColors,
CGUI::GetPreDefinedColor, IGUIButtonBehavior::ChooseColor() as const for
consistency with the other "databases", refs 3028551b91.
Mark CGUIColor as NONCOPYABLE to add compiler errors if there is an
unexplicit copy, refs 3028551b91.
Explicit ugly copy in CGUI::Xeromyces_ReadColor and
CGUIColor::ParseString.
Deregister copying <CGUIColor>GetSetting functions, refs 8f4f8e240f.
Uses the const ref GetSetting from 3dfa23cd25.
This was SVN commit r22694.
2019-08-19 14:53:58 +02:00
else
LOGERROR ( " GUI: Unable to create custom color '%s'. Invalid color syntax. " , name . c_str ( ) ) ;
2005-07-25 21:06:18 +02:00
}