1
0
forked from 0ad/0ad

Fix Lobby/MP Gamesetup chat lag with many messages.

CText requires re-rendering every message when adding a new one, which
quickly becomes very slow.
Use CList and a custom method to work around this.

These classes are in need of a more complete refactoring.

Based on a patch by: nani
Differential Revision: https://code.wildfiregames.com/D1781
This was SVN commit r24306.
This commit is contained in:
wraitii 2020-12-01 09:42:05 +00:00
parent dcc73561c2
commit f78d3ddf71
11 changed files with 164 additions and 28 deletions

View File

@ -102,7 +102,6 @@
scroll_bottom="true"
textcolor="white"
text_align="left"
text_valign="center"
/>
<style name="TutorialPanel"

View File

@ -32,7 +32,7 @@ class ChatMessagesPanel
text = this.timestampWrapper.format(text);
this.chatHistory += this.chatHistory ? "\n" + text : text;
this.chatText.caption = this.chatHistory;
this.chatText.addItem(text);
}
addStatusMessage(text)

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<object name="chatPanel" type="image" sprite="ModernDarkBoxGold">
<object name="chatText" type="text" size="2 2 100%-2 100%-28" style="ChatPanel"/>
<object name="chatText" type="list" size="2 2 100%-2 100%-28" style="ChatPanel"/>
<object name="chatInput" type="input" size="4 100%-26 100%-96 100%-4" style="ModernInput" max_length="1024"/>

View File

@ -24,6 +24,7 @@ class ChatMessagesPanel
text = this.timestampWrapper.format(timestamp, text);
this.chatHistory += this.chatHistory ? "\n" + text : text;
this.chatText.addItem(text);
if (!this.hasUpdate)
{
@ -38,7 +39,6 @@ class ChatMessagesPanel
{
if (this.hasUpdate)
{
this.chatText.caption = this.chatHistory;
this.hasUpdate = false;
this.xmppMessages.unregisterMessageBatchProcessedHandler(this.flushEvent);
}

View File

@ -6,7 +6,7 @@
<script directory="gui/lobby/LobbyPage/Chat/StatusMessages/"/>
<script directory="gui/lobby/LobbyPage/Chat/SystemMessages/"/>
<object name="chatText" size="0 0 100% 100%-28" type="text" style="ChatPanel" font="sans-13"/>
<object name="chatText" size="0 0 100% 100%-28" type="list" style="ChatPanel" font="sans-13"/>
<object name="chatInput" size="0 100%-26 100%-72 100%-4" type="input" style="ModernInput" font="sans-13" max_length="1024"/>
<object name="chatSubmit" size="100%-72 100%-26 100%-4 100%-4" type="button" style="StoneButton">
<translatableAttribute id="caption">Send</translatableAttribute>

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2019 Wildfire Games.
/* Copyright (C) 2020 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -38,6 +38,7 @@ void CGUI::AddObjectTypes()
{
m_ProxyData.insert(JSI_GUIProxy<IGUIObject>::CreateData(*m_ScriptInterface));
m_ProxyData.insert(JSI_GUIProxy<CText>::CreateData(*m_ScriptInterface));
m_ProxyData.insert(JSI_GUIProxy<CList>::CreateData(*m_ScriptInterface));
AddObjectType("button", &CButton::ConstructObject);
AddObjectType("chart", &CChart::ConstructObject);

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2019 Wildfire Games.
/* Copyright (C) 2020 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -23,6 +23,7 @@
#include "gui/CGUIScrollBarVertical.h"
#include "gui/SettingTypes/CGUIColor.h"
#include "gui/SettingTypes/CGUIList.h"
#include "gui/Scripting/JSInterface_GUIProxy.h"
#include "lib/external_libraries/libsdl.h"
#include "lib/timer.h"
@ -60,6 +61,7 @@ CList::CList(CGUI& pGUI)
RegisterSetting("font", m_Font);
RegisterSetting("scrollbar", m_ScrollBar);
RegisterSetting("scrollbar_style", m_ScrollBarStyle);
RegisterSetting("scroll_bottom", m_ScrollBottom);
RegisterSetting("sound_disabled", m_SoundDisabled);
RegisterSetting("sound_selected", m_SoundSelected);
RegisterSetting("sprite", m_Sprite);
@ -92,25 +94,40 @@ CList::~CList()
}
void CList::SetupText()
{
SetupText(false);
}
void CList::SetupText(bool append)
{
m_Modified = true;
m_ItemsYPositions.resize(m_List.m_Items.size() + 1);
// Delete all generated texts. Some could probably be saved,
// but this is easier, and this function will never be called
// continuously, or even often, so it'll probably be okay.
m_GeneratedTexts.clear();
if (!append)
// Delete all generated texts.
// TODO: try to be cleverer if we want to update items before the end.
m_GeneratedTexts.clear();
float width = GetListRect().GetWidth();
// remove scrollbar if applicable
bool bottom = false;
if (m_ScrollBar && GetScrollBar(0).GetStyle())
{
if (m_ScrollBottom && GetScrollBar(0).GetPos() > GetScrollBar(0).GetMaxPos() - 1.5f)
bottom = true;
// remove scrollbar if applicable
width -= GetScrollBar(0).GetStyle()->m_Width;
}
// Generate texts
float buffered_y = 0.f;
for (size_t i = 0; i < m_List.m_Items.size(); ++i)
if (append)
buffered_y = m_ItemsYPositions[m_List.m_Items.size() - 1];
for (size_t i = append ? m_List.m_Items.size() - 1 : 0; i < m_List.m_Items.size(); ++i)
{
CGUIText* text;
@ -141,6 +158,9 @@ void CList::SetupText()
GetScrollBar(0).SetY(rect.top);
GetScrollBar(0).SetZ(GetBufferedZ());
GetScrollBar(0).SetLength(rect.bottom - rect.top);
if (bottom)
GetScrollBar(0).SetPos(GetScrollBar(0).GetMaxPos());
}
}
@ -380,18 +400,11 @@ void CList::DrawList(const int& selected, const CGUISpriteInstance& sprite, cons
}
}
void CList::AddItem(const CStrW& str, const CStrW& data)
void CList::AddItem(const CGUIString& str, const CGUIString& data)
{
CGUIString gui_string;
gui_string.SetValue(str);
// Do not send a settings-changed message
m_List.m_Items.push_back(gui_string);
CGUIString data_string;
data_string.SetValue(data);
m_ListData.m_Items.push_back(data_string);
m_List.m_Items.push_back(str);
m_ListData.m_Items.push_back(data);
// TODO Temp
SetupText();
@ -403,7 +416,9 @@ bool CList::HandleAdditionalChildren(const XMBElement& child, CXeromyces* pFile)
if (child.GetNodeName() == elmt_item)
{
AddItem(child.GetText().FromUTF8(), child.GetText().FromUTF8());
CGUIString vlist;
vlist.SetValue(child.GetText().FromUTF8());
AddItem(vlist, vlist);
return true;
}
@ -485,3 +500,33 @@ int CList::GetHoveredItem()
return -1;
}
void CList::CreateJSObject()
{
ScriptRequest rq(m_pGUI.GetScriptInterface());
js::ProxyOptions options;
options.setClass(&JSI_GUIProxy<CList>::ClassDefinition());
JS::RootedValue cppObj(rq.cx), data(rq.cx);
cppObj.get().setPrivate(this);
data.get().setPrivate(GetGUI().GetProxyData(&JSI_GUIProxy<CList>::Singleton()));
m_JSObject.init(rq.cx, js::NewProxyObject(rq.cx, &JSI_GUIProxy<CList>::Singleton(), cppObj, nullptr, options));
js::SetProxyReservedSlot(m_JSObject, 0, data);
}
bool CList::Script_AddItem(JSContext* cx, uint argc, JS::Value* vp)
{
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
CList* e = static_cast<CList*>(js::GetProxyPrivate(args.thisv().toObjectOrNull()).toPrivate());
if (!e)
return false;
ScriptInterface& scriptInterface = *ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface;
ScriptRequest rq(scriptInterface);
CGUIString text;
if (!ScriptInterface::FromJSVal(rq, args.get(0), text))
return false;
e->AddItem(text, text);
return true;
}

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2019 Wildfire Games.
/* Copyright (C) 2020 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -38,6 +38,8 @@ class CList : public IGUIObject, public IGUIScrollBarOwner, public IGUITextOwner
{
GUI_OBJECT(CList)
friend JSI_GUIProxy<CList>;
public:
CList(CGUI& pGUI);
virtual ~CList();
@ -55,15 +57,19 @@ public:
/**
* Adds an item last to the list.
*/
virtual void AddItem(const CStrW& str, const CStrW& data);
virtual void AddItem(const CGUIString& str, const CGUIString& data);
protected:
static bool Script_AddItem(JSContext* cx, uint argc, JS::Value* vp);
/**
* Sets up text, should be called every time changes has been
* made that can change the visual.
* @param append - if true, we assume we only need to render the new element at the end of the list.
*/
virtual void SetupText();
virtual void SetupText(bool append);
/**
* @see IGUIObject#HandleMessage()
@ -80,6 +86,8 @@ protected:
*/
virtual void Draw();
virtual void CreateJSObject();
/**
* Easy select elements functions
*/
@ -123,6 +131,7 @@ protected:
CStrW m_Font;
bool m_ScrollBar;
CStr m_ScrollBarStyle;
bool m_ScrollBottom;
CStrW m_SoundDisabled;
CStrW m_SoundSelected;
CGUISpriteInstance m_Sprite;

View File

@ -192,7 +192,9 @@ bool COList::HandleAdditionalChildren(const XMBElement& child, CXeromyces* pFile
if (child.GetNodeName() == elmt_item)
{
AddItem(child.GetText().FromUTF8(), child.GetText().FromUTF8());
CGUIString vlist;
vlist.SetValue(child.GetText().FromUTF8());
AddItem(vlist, vlist);
return true;
}
else if (child.GetNodeName() == elmt_column)

View File

@ -259,7 +259,7 @@ void CText::CreateJSObject()
ScriptRequest rq(m_pGUI.GetScriptInterface());
js::ProxyOptions options;
options.setClass(&JSI_GUIProxy<IGUIObject>::ClassDefinition());
options.setClass(&JSI_GUIProxy<CText>::ClassDefinition());
JS::RootedValue cppObj(rq.cx), data(rq.cx);
cppObj.get().setPrivate(this);

View File

@ -0,0 +1,80 @@
/* Copyright (C) 2020 Wildfire Games.
* 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/>.
*/
#include "precompiled.h"
#include "JSInterface_GUIProxy.h"
#include "gui/CGUI.h"
#include "gui/CGUISetting.h"
#include "gui/ObjectBases/IGUIObject.h"
#include "gui/ObjectTypes/CList.h"
#include "ps/CLogger.h"
#include "scriptinterface/ScriptExtraHeaders.h"
#include "scriptinterface/ScriptInterface.h"
#include <string>
// Include the definition of the generic templates.
#include "JSInterface_GUIProxy_impl.h"
namespace {
struct SData
{
JS::PersistentRootedObject m_ToString;
JS::PersistentRootedObject m_Focus;
JS::PersistentRootedObject m_Blur;
JS::PersistentRootedObject m_GetComputedSize;
JS::PersistentRootedObject m_AddItem;
};
}
template <>
bool JSI_GUIProxy<CList>::funcGetter(JS::HandleObject proxy, const std::string& propName, JS::MutableHandleValue vp) const
{
const SData& data = *static_cast<const SData*>(js::GetProxyReservedSlot(proxy, 0).toPrivate());
if (propName == "toString")
return vp.setObjectOrNull(data.m_ToString), true;
if (propName == "focus")
return vp.setObjectOrNull(data.m_Focus), true;
if (propName == "blur")
return vp.setObjectOrNull(data.m_Blur), true;
if (propName == "getComputedSize")
return vp.setObjectOrNull(data.m_GetComputedSize), true;
if (propName == "addItem")
return vp.setObjectOrNull(data.m_AddItem), true;
return false;
}
template <>
std::pair<const js::BaseProxyHandler*, void*> JSI_GUIProxy<CList>::CreateData(ScriptInterface& scriptInterface)
{
SData* data = new SData();
ScriptRequest rq(scriptInterface);
#define func(class, func) &apply_to<CList, class, &class::func>
data->m_ToString.init(rq.cx, JS_GetFunctionObject(JS_NewFunction(rq.cx, func(IGUIObject, toString), 0, 0, "toString")));
data->m_Focus.init(rq.cx, JS_GetFunctionObject(JS_NewFunction(rq.cx, func(IGUIObject, focus), 0, 0, "focus")));
data->m_Blur.init(rq.cx, JS_GetFunctionObject(JS_NewFunction(rq.cx, func(IGUIObject, blur), 0, 0, "blur")));
data->m_GetComputedSize.init(rq.cx, JS_GetFunctionObject(JS_NewFunction(rq.cx, func(IGUIObject, getComputedSize), 0, 0, "getComputedSize")));
#undef func
data->m_AddItem.init(rq.cx, JS_GetFunctionObject(JS_NewFunction(rq.cx, CList::Script_AddItem, 0, 0, "addItem")));
return { &Singleton(), data };
}
template class JSI_GUIProxy<CList>;