mirror of
https://port.numenaute.org/aleajactaest/khanat-opennel-code.git
synced 2024-12-23 17:38:44 +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;
|
||||
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"
|
||||
params="dblink=UI:SAVE:ISDETACHED:DYNAMIC_CHAT@0|value=0" />
|
||||
</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>
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
// client
|
||||
#include "chat_text_manager.h"
|
||||
#include "nel/gui/view_text.h"
|
||||
#include "nel/gui/group_paragraph.h"
|
||||
#include "interface_manager.h"
|
||||
|
||||
using namespace std;
|
||||
|
@ -149,34 +150,165 @@ static CInterfaceGroup *buildLineWithCommand(CInterfaceGroup *commandGroup, CVie
|
|||
return group;
|
||||
}
|
||||
|
||||
//=================================================================================
|
||||
CViewBase *CChatTextManager::createMsgText(const ucstring &cstMsg, NLMISC::CRGBA col, bool justified /*=false*/)
|
||||
static inline bool isUrlTag(const ucstring &s, ucstring::size_type index, ucstring::size_type textSize)
|
||||
{
|
||||
ucstring msg = cstMsg;
|
||||
CInterfaceGroup *commandGroup = parseCommandTag(msg);
|
||||
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);
|
||||
|
||||
ucstring cur_time;
|
||||
if (showTimestamps())
|
||||
// Format http://, https://
|
||||
// or markdown style (title)[http://..]
|
||||
if(textSize > index+7)
|
||||
{
|
||||
CCDBNodeLeaf *node = NLGUI::CDBManager::getInstance()->getDbProp("UI:SAVE:SHOW_CLOCK_12H", false);
|
||||
if (node && node->getValueBool())
|
||||
cur_time = CInterfaceManager::getTimestampHuman("[%I:%M:%S %p] ");
|
||||
else
|
||||
cur_time = CInterfaceManager::getTimestampHuman();
|
||||
bool markdown = false;
|
||||
ucstring::size_type i = index;
|
||||
// advance index to url section if markdown style link is detected
|
||||
if (s[i] == '(')
|
||||
{
|
||||
// scan for ')[http://'
|
||||
while(i < textSize-9)
|
||||
{
|
||||
if (s[i] == ')' && s[i+1] == '[')
|
||||
{
|
||||
i += 2;
|
||||
markdown = true;
|
||||
break;
|
||||
}
|
||||
else
|
||||
if (s[i] == ')')
|
||||
{
|
||||
i += 1;
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
if (textSize > i + 7)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// if text contain any color code, set the text formated and white,
|
||||
// otherwise, set text normal and apply global color
|
||||
size_t codePos = msg.find(ucstring("@{"));
|
||||
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);
|
||||
if (node && node->getValueBool())
|
||||
cur_time = CInterfaceManager::getTimestampHuman("[%I:%M:%S %p] ");
|
||||
else
|
||||
cur_time = CInterfaceManager::getTimestampHuman();
|
||||
|
||||
ucstring::size_type codePos = msg.find(ucstring("@{"));
|
||||
if (codePos != ucstring::npos)
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
|
||||
}
|
||||
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->setColor(NLMISC::CRGBA::White);
|
||||
}
|
||||
else
|
||||
{
|
||||
msg = cur_time + msg;
|
||||
vt->setText(msg);
|
||||
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()
|
||||
{
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
namespace NLGUI
|
||||
{
|
||||
class CViewBase;
|
||||
class CInterfaceGroup;
|
||||
}
|
||||
|
||||
class ucstring;
|
||||
|
@ -49,8 +50,9 @@ public:
|
|||
* \param msg the actual 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 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
|
||||
static CChatTextManager &getInstance();
|
||||
|
||||
|
@ -73,6 +75,9 @@ private:
|
|||
~CChatTextManager();
|
||||
|
||||
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
|
||||
|
|
Loading…
Reference in a new issue