mirror of
https://port.numenaute.org/aleajactaest/khanat-opennel-code.git
synced 2024-11-17 04:51:52 +00:00
Added: Clickable urls in chat text (issue 282)
This commit is contained in:
parent
b9ab826feb
commit
2e5bcdf248
4 changed files with 335 additions and 27 deletions
|
@ -537,3 +537,17 @@ function game:openWebIGBrowserHeader()
|
||||||
ui.w = ui_webig_browser_w;
|
ui.w = ui_webig_browser_w;
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------------------------------------
|
||||||
|
local SavedUrl = "";
|
||||||
|
function game:chatUrl(url)
|
||||||
|
SavedUrl = url
|
||||||
|
runAH(nil, "active_menu", "menu=ui:interface:chat_uri_action_menu");
|
||||||
|
end
|
||||||
|
function game:chatUrlCopy()
|
||||||
|
runAH(nil, "copy_to_clipboard", SavedUrl)
|
||||||
|
end
|
||||||
|
function game:chatUrlBrowse()
|
||||||
|
runAH(nil, "browse", "name=ui:interface:webig:content:html|url=" .. SavedUrl)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
|
@ -3127,4 +3127,19 @@
|
||||||
<action handler="set"
|
<action handler="set"
|
||||||
params="dblink=UI:SAVE:ISDETACHED:DYNAMIC_CHAT@0|value=0" />
|
params="dblink=UI:SAVE:ISDETACHED:DYNAMIC_CHAT@0|value=0" />
|
||||||
</proc>
|
</proc>
|
||||||
|
|
||||||
|
<!-- there seems to be no way to pass url to menu, use lua script for middleman -->
|
||||||
|
<group type="menu"
|
||||||
|
id="chat_uri_action_menu"
|
||||||
|
extends="base_menu">
|
||||||
|
<action id="copy"
|
||||||
|
name="uiCopy"
|
||||||
|
handler="lua"
|
||||||
|
params="game:chatUrlCopy()" />
|
||||||
|
<action id="openig"
|
||||||
|
name="uiBrowse"
|
||||||
|
handler="lua"
|
||||||
|
params="game:chatUrlBrowse()" />
|
||||||
|
</group>
|
||||||
|
|
||||||
</interface_config>
|
</interface_config>
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
// client
|
// client
|
||||||
#include "chat_text_manager.h"
|
#include "chat_text_manager.h"
|
||||||
#include "nel/gui/view_text.h"
|
#include "nel/gui/view_text.h"
|
||||||
|
#include "nel/gui/group_paragraph.h"
|
||||||
#include "interface_manager.h"
|
#include "interface_manager.h"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
@ -149,34 +150,165 @@ static CInterfaceGroup *buildLineWithCommand(CInterfaceGroup *commandGroup, CVie
|
||||||
return group;
|
return group;
|
||||||
}
|
}
|
||||||
|
|
||||||
//=================================================================================
|
static inline bool isUrlTag(const ucstring &s, ucstring::size_type index, ucstring::size_type textSize)
|
||||||
CViewBase *CChatTextManager::createMsgText(const ucstring &cstMsg, NLMISC::CRGBA col, bool justified /*=false*/)
|
|
||||||
{
|
{
|
||||||
ucstring msg = cstMsg;
|
// Format http://, https://
|
||||||
CInterfaceGroup *commandGroup = parseCommandTag(msg);
|
// or markdown style (title)[http://..]
|
||||||
CViewText *vt = new CViewText(CViewText::TCtorParam());
|
if(textSize > index+7)
|
||||||
// get parameters from config.xml
|
{
|
||||||
vt->setShadow(isTextShadowed());
|
bool markdown = false;
|
||||||
vt->setShadowOutline(false);
|
ucstring::size_type i = index;
|
||||||
vt->setFontSize(getTextFontSize());
|
// advance index to url section if markdown style link is detected
|
||||||
vt->setMultiLine(true);
|
if (s[i] == '(')
|
||||||
vt->setTextMode(justified ? CViewText::Justified : CViewText::DontClipWord);
|
{
|
||||||
vt->setMultiLineSpace(getTextMultiLineSpace());
|
// scan for ')[http://'
|
||||||
vt->setModulateGlobalColor(false);
|
while(i < textSize-9)
|
||||||
|
{
|
||||||
|
if (s[i] == ')' && s[i+1] == '[')
|
||||||
|
{
|
||||||
|
i += 2;
|
||||||
|
markdown = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if (s[i] == ')')
|
||||||
|
{
|
||||||
|
i += 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ucstring cur_time;
|
if (textSize > i + 7)
|
||||||
if (showTimestamps())
|
|
||||||
{
|
{
|
||||||
|
bool isUrl = (toLower(s.substr(i, 7)) == ucstring("http://") || toLower(s.substr(i, 8)) == ucstring("https://"));
|
||||||
|
// match "text http://" and not "texthttp://"
|
||||||
|
if (isUrl && i > 0 && !markdown)
|
||||||
|
{
|
||||||
|
// '}' is in the list because of color tags, ie "@{FFFF}http://..."
|
||||||
|
const ucchar chars[] = {' ', '"', '\'', '(', '[', '}'};
|
||||||
|
isUrl = std::find(std::begin(chars), std::end(chars), s[i - 1]) != std::end(chars);
|
||||||
|
}
|
||||||
|
return isUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ***************************************************************************
|
||||||
|
// isUrlTag must match
|
||||||
|
static inline void getUrlTag(const ucstring &s, ucstring::size_type &index, ucstring &url, ucstring &title)
|
||||||
|
{
|
||||||
|
bool isMarkdown = false;
|
||||||
|
ucstring::size_type textSize = s.size();
|
||||||
|
ucstring::size_type pos;
|
||||||
|
|
||||||
|
// see if we have markdown format
|
||||||
|
if (s[index] == '(')
|
||||||
|
{
|
||||||
|
index++;
|
||||||
|
pos = index;
|
||||||
|
while(pos < textSize-9)
|
||||||
|
{
|
||||||
|
if (s[pos] == ')' && s[pos + 1] == '[')
|
||||||
|
{
|
||||||
|
isMarkdown = true;
|
||||||
|
title = s.substr(index, pos - index);
|
||||||
|
index = pos + 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (s[pos] == ')')
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ucchar chOpen = ' ';
|
||||||
|
ucchar chClose = ' ';
|
||||||
|
if (isMarkdown)
|
||||||
|
{
|
||||||
|
chOpen = '[';
|
||||||
|
chClose = ']';
|
||||||
|
}
|
||||||
|
else if (index > 0)
|
||||||
|
{
|
||||||
|
chOpen = s[index - 1];
|
||||||
|
if (chOpen == '\'') chClose = '\'';
|
||||||
|
else if (chOpen == '"') chClose = '"';
|
||||||
|
else if (chOpen == '(') chClose = ')';
|
||||||
|
else if (chOpen == '[') chClose = ']';
|
||||||
|
else chClose = ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chOpen == chClose)
|
||||||
|
{
|
||||||
|
pos = s.find_first_of(chClose, index);
|
||||||
|
|
||||||
|
// handle common special case: 'text http://.../, text'
|
||||||
|
if (pos != ucstring::npos && index > 0)
|
||||||
|
{
|
||||||
|
if (s[index-1] == ' ' && (s[pos-1] == ',' || s[pos-1] == '.'))
|
||||||
|
{
|
||||||
|
pos--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// scan for nested open/close tags
|
||||||
|
pos = index;
|
||||||
|
sint nested = 0;
|
||||||
|
while(pos < textSize)
|
||||||
|
{
|
||||||
|
if (s[pos] == chOpen)
|
||||||
|
{
|
||||||
|
nested++;
|
||||||
|
}
|
||||||
|
else if (s[pos] == chClose)
|
||||||
|
{
|
||||||
|
if (nested == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nested--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fallback to full string length as we did match http:// already and url spans to the end probably
|
||||||
|
if (pos == ucstring::npos)
|
||||||
|
{
|
||||||
|
pos = textSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
url = s.substr(index, pos - index);
|
||||||
|
index = pos;
|
||||||
|
|
||||||
|
// skip ']' closing char
|
||||||
|
if (isMarkdown) index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
//=================================================================================
|
||||||
|
static void prependTimestamp(ucstring &msg)
|
||||||
|
{
|
||||||
|
ucstring cur_time;
|
||||||
CCDBNodeLeaf *node = NLGUI::CDBManager::getInstance()->getDbProp("UI:SAVE:SHOW_CLOCK_12H", false);
|
CCDBNodeLeaf *node = NLGUI::CDBManager::getInstance()->getDbProp("UI:SAVE:SHOW_CLOCK_12H", false);
|
||||||
if (node && node->getValueBool())
|
if (node && node->getValueBool())
|
||||||
cur_time = CInterfaceManager::getTimestampHuman("[%I:%M:%S %p] ");
|
cur_time = CInterfaceManager::getTimestampHuman("[%I:%M:%S %p] ");
|
||||||
else
|
else
|
||||||
cur_time = CInterfaceManager::getTimestampHuman();
|
cur_time = CInterfaceManager::getTimestampHuman();
|
||||||
}
|
|
||||||
|
|
||||||
// if text contain any color code, set the text formated and white,
|
ucstring::size_type codePos = msg.find(ucstring("@{"));
|
||||||
// otherwise, set text normal and apply global color
|
|
||||||
size_t codePos = msg.find(ucstring("@{"));
|
|
||||||
if (codePos != ucstring::npos)
|
if (codePos != ucstring::npos)
|
||||||
{
|
{
|
||||||
// Prepend the current time (do it after the color if the color at first position.
|
// Prepend the current time (do it after the color if the color at first position.
|
||||||
|
@ -189,13 +321,49 @@ CViewBase *CChatTextManager::createMsgText(const ucstring &cstMsg, NLMISC::CRGBA
|
||||||
{
|
{
|
||||||
msg = cur_time + msg;
|
msg = cur_time + msg;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
msg = cur_time + msg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//=================================================================================
|
||||||
|
CViewBase *CChatTextManager::createMsgText(const ucstring &cstMsg, NLMISC::CRGBA col, bool justified /*=false*/, bool plaintext /*=false*/)
|
||||||
|
{
|
||||||
|
ucstring msg = cstMsg;
|
||||||
|
CInterfaceGroup *commandGroup = parseCommandTag(msg);
|
||||||
|
|
||||||
|
if (showTimestamps())
|
||||||
|
prependTimestamp(msg);
|
||||||
|
|
||||||
|
// must wrap all lines to CGroupParagraph because CGroupList will calculate
|
||||||
|
// width from previous line which ends up as CViewText otherwise
|
||||||
|
return createMsgTextComplex(msg, col, justified, plaintext, commandGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
//=================================================================================
|
||||||
|
CViewBase *CChatTextManager::createMsgTextSimple(const ucstring &msg, NLMISC::CRGBA col, bool justified, CInterfaceGroup *commandGroup)
|
||||||
|
{
|
||||||
|
CViewText *vt = new CViewText(CViewText::TCtorParam());
|
||||||
|
// get parameters from config.xml
|
||||||
|
vt->setShadow(isTextShadowed());
|
||||||
|
vt->setShadowOutline(false);
|
||||||
|
vt->setFontSize(getTextFontSize());
|
||||||
|
vt->setMultiLine(true);
|
||||||
|
vt->setTextMode(justified ? CViewText::Justified : CViewText::DontClipWord);
|
||||||
|
vt->setMultiLineSpace(getTextMultiLineSpace());
|
||||||
|
vt->setModulateGlobalColor(false);
|
||||||
|
|
||||||
|
// if text contain any color code, set the text formated and white,
|
||||||
|
// otherwise, set text normal and apply global color
|
||||||
|
if (msg.find(ucstring("@{")) != ucstring::npos)
|
||||||
|
{
|
||||||
vt->setTextFormatTaged(msg);
|
vt->setTextFormatTaged(msg);
|
||||||
vt->setColor(NLMISC::CRGBA::White);
|
vt->setColor(NLMISC::CRGBA::White);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
msg = cur_time + msg;
|
|
||||||
vt->setText(msg);
|
vt->setText(msg);
|
||||||
vt->setColor(col);
|
vt->setColor(col);
|
||||||
}
|
}
|
||||||
|
@ -210,6 +378,112 @@ CViewBase *CChatTextManager::createMsgText(const ucstring &cstMsg, NLMISC::CRGBA
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//=================================================================================
|
||||||
|
CViewBase *CChatTextManager::createMsgTextComplex(const ucstring &msg, NLMISC::CRGBA col, bool justified, bool plaintext, CInterfaceGroup *commandGroup)
|
||||||
|
{
|
||||||
|
ucstring::size_type textSize = msg.size();
|
||||||
|
|
||||||
|
CGroupParagraph *para = new CGroupParagraph(CViewBase::TCtorParam());
|
||||||
|
para->setId("line");
|
||||||
|
para->setSizeRef("w");
|
||||||
|
para->setResizeFromChildH(true);
|
||||||
|
|
||||||
|
if (plaintext)
|
||||||
|
{
|
||||||
|
CViewBase *vt = createMsgTextSimple(msg, col, justified, NULL);
|
||||||
|
vt->setId("text");
|
||||||
|
para->addChild(vt);
|
||||||
|
|
||||||
|
return para;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// quickly check if text has links or not
|
||||||
|
bool hasUrl;
|
||||||
|
{
|
||||||
|
ucstring s = toLower(msg);
|
||||||
|
hasUrl = (s.find(ucstring("http://")) || s.find(ucstring("https://")));
|
||||||
|
}
|
||||||
|
|
||||||
|
ucstring::size_type pos = 0;
|
||||||
|
for (ucstring::size_type i = 0; i< textSize;)
|
||||||
|
{
|
||||||
|
if (hasUrl && isUrlTag(msg, i, textSize))
|
||||||
|
{
|
||||||
|
if (pos != i)
|
||||||
|
{
|
||||||
|
CViewBase *vt = createMsgTextSimple(msg.substr(pos, i - pos), col, justified, NULL);
|
||||||
|
para->addChild(vt);
|
||||||
|
}
|
||||||
|
|
||||||
|
ucstring url;
|
||||||
|
ucstring title;
|
||||||
|
getUrlTag(msg, i, url, title);
|
||||||
|
if (url.size() > 0)
|
||||||
|
{
|
||||||
|
CViewLink *vt = new CViewLink(CViewBase::TCtorParam());
|
||||||
|
vt->setId("link");
|
||||||
|
vt->setUnderlined(true);
|
||||||
|
vt->setShadow(isTextShadowed());
|
||||||
|
vt->setShadowOutline(false);
|
||||||
|
vt->setFontSize(getTextFontSize());
|
||||||
|
vt->setMultiLine(true);
|
||||||
|
vt->setTextMode(justified ? CViewText::Justified : CViewText::DontClipWord);
|
||||||
|
vt->setMultiLineSpace(getTextMultiLineSpace());
|
||||||
|
vt->setModulateGlobalColor(false);
|
||||||
|
|
||||||
|
//NLMISC::CRGBA color;
|
||||||
|
//color.blendFromui(col, CRGBA(255, 153, 0, 255), 100);
|
||||||
|
//vt->setColor(color);
|
||||||
|
vt->setColor(col);
|
||||||
|
|
||||||
|
if (title.size() > 0)
|
||||||
|
{
|
||||||
|
vt->LinkTitle = title.toUtf8();
|
||||||
|
vt->setText(title);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
vt->LinkTitle = url.toUtf8();
|
||||||
|
vt->setText(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (url.find_first_of('\'') != string::npos)
|
||||||
|
{
|
||||||
|
ucstring clean;
|
||||||
|
for(string::size_type i = 0; i< url.size(); ++i)
|
||||||
|
{
|
||||||
|
if (url[i] == '\'')
|
||||||
|
clean += ucstring("%27");
|
||||||
|
else
|
||||||
|
clean += url[i];
|
||||||
|
}
|
||||||
|
url = clean;
|
||||||
|
}
|
||||||
|
vt->setActionOnLeftClick("lua");
|
||||||
|
vt->setParamsOnLeftClick("game:chatUrl('" + url.toUtf8() + "')");
|
||||||
|
|
||||||
|
para->addChildLink(vt);
|
||||||
|
|
||||||
|
pos = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pos < textSize)
|
||||||
|
{
|
||||||
|
CViewBase *vt = createMsgTextSimple(msg.substr(pos, textSize - pos), col, justified, NULL);
|
||||||
|
vt->setId("text");
|
||||||
|
para->addChild(vt);
|
||||||
|
}
|
||||||
|
|
||||||
|
return para;
|
||||||
|
}
|
||||||
|
|
||||||
//=================================================================================
|
//=================================================================================
|
||||||
CChatTextManager &CChatTextManager::getInstance()
|
CChatTextManager &CChatTextManager::getInstance()
|
||||||
{
|
{
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
namespace NLGUI
|
namespace NLGUI
|
||||||
{
|
{
|
||||||
class CViewBase;
|
class CViewBase;
|
||||||
|
class CInterfaceGroup;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ucstring;
|
class ucstring;
|
||||||
|
@ -49,8 +50,9 @@ public:
|
||||||
* \param msg the actual text
|
* \param msg the actual text
|
||||||
* \param col the color of the text
|
* \param col the color of the text
|
||||||
* \param justified Should be true for justified text (stretch spaces of line to fill the full width)
|
* \param justified Should be true for justified text (stretch spaces of line to fill the full width)
|
||||||
|
* \param plaintext Text will not be parsed for uri markup links
|
||||||
*/
|
*/
|
||||||
NLGUI::CViewBase *createMsgText(const ucstring &msg, NLMISC::CRGBA col, bool justified = false);
|
NLGUI::CViewBase *createMsgText(const ucstring &msg, NLMISC::CRGBA col, bool justified = false, bool plaintext = false);
|
||||||
// Singleton access
|
// Singleton access
|
||||||
static CChatTextManager &getInstance();
|
static CChatTextManager &getInstance();
|
||||||
|
|
||||||
|
@ -73,6 +75,9 @@ private:
|
||||||
~CChatTextManager();
|
~CChatTextManager();
|
||||||
|
|
||||||
bool showTimestamps() const;
|
bool showTimestamps() const;
|
||||||
|
|
||||||
|
NLGUI::CViewBase *createMsgTextSimple(const ucstring &msg, NLMISC::CRGBA col, bool justified, NLGUI::CInterfaceGroup *commandGroup);
|
||||||
|
NLGUI::CViewBase *createMsgTextComplex(const ucstring &msg, NLMISC::CRGBA col, bool justified, bool plaintext, NLGUI::CInterfaceGroup *commandGroup);
|
||||||
};
|
};
|
||||||
|
|
||||||
// shortcut to get text manager instance
|
// shortcut to get text manager instance
|
||||||
|
|
Loading…
Reference in a new issue