mirror of
https://github.com/qTox/qTox.git
synced 2024-03-22 14:00:36 +08:00
Merge branch 'pr2832'
Markdown support!
This commit is contained in:
commit
c3caba2e3f
|
@ -4,6 +4,7 @@
|
|||
* [User Profile](#user-profile)
|
||||
* [Settings](#settings)
|
||||
* [Groupchats](#groupchats)
|
||||
* [Message Styling](#message-styling)
|
||||
* [Multi Window Mode](#multi-window-mode)
|
||||
* [Keyboard Shortcuts](#keyboard-shortcuts)
|
||||
|
||||
|
@ -119,6 +120,23 @@ NoSpam is a feature of Tox that prevents a malicious user from spamming you with
|
|||
|
||||
Groupchats are a way to talk with multiple friends at the same time, like when you are standing together in a group. To create a groupchat click the groupchat icon in the bottom left corner and set a name. Now you can invite your contacts by right-clicking on the contact and selecting "Invite to group". Currently, if the last person leaves the chat, it is closed and you have to create a new one. Videochats and file transfers are currently unsupported in groupchats.
|
||||
|
||||
## Message Styling
|
||||
|
||||
Similar to other messaging applications, qTox supports stylized text formatting. Formatting follows [Markdown syntax](https://daringfireball.net/projects/markdown/syntax), thus:
|
||||
|
||||
* For **Bold**, surround text in double asterisks or underscores: `**text**` or `__text__`
|
||||
* For **Italics**, surround text in single asterisks or underscores: `*text*` or `_text_`
|
||||
* For **Strikethrough**, surround text in single tilde's: `~text~`
|
||||
* For **Underline**, surround text in single dashes: `-text-`
|
||||
|
||||
Additionally, qTox supports three modes of Markdown parsing:
|
||||
|
||||
* `Plaintext`: No text is stylized
|
||||
* `Show Formatting Characters`: Stylize text while showing formatting characters (Default)
|
||||
* `Don't Show Formatting Characters`: Stylize text without showing formatting characters
|
||||
|
||||
*Note that any change in Markdown preference will require a restart.*
|
||||
|
||||
## Multi Window Mode
|
||||
|
||||
In this mode, qTox will separate its main window into a single contact list and one or multiple chat windows, which allows you to have multiple conversations on your screen at the same time. Additionally you can manually group chats into a window by dragging and dropping them onto each other. This mode can be activated and configured in [settings](#settings).
|
||||
|
|
|
@ -26,6 +26,8 @@
|
|||
#include "content/image.h"
|
||||
#include "content/notificationicon.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include "src/persistence/settings.h"
|
||||
#include "src/persistence/smileypack.h"
|
||||
#include "src/widget/style.h"
|
||||
|
@ -54,6 +56,10 @@ ChatMessage::Ptr ChatMessage::createChatMessage(const QString &sender, const QSt
|
|||
//quotes (green text)
|
||||
text = detectQuotes(detectAnchors(text), type);
|
||||
|
||||
//markdown
|
||||
if (Settings::getInstance().getMarkdownPreference() != MarkdownType::NONE)
|
||||
text = detectMarkdown(text);
|
||||
|
||||
switch(type)
|
||||
{
|
||||
case ACTION:
|
||||
|
@ -175,6 +181,72 @@ void ChatMessage::hideDate()
|
|||
c->hide();
|
||||
}
|
||||
|
||||
QString ChatMessage::detectMarkdown(const QString &str)
|
||||
{
|
||||
QString out;
|
||||
|
||||
// Create regex for certain markdown syntax
|
||||
QRegExp exp("(\\*\\*)([^\\*\\*]{2,})(\\*\\*)" // Bold **text**
|
||||
"|(\\*)([^\\*]{2,})(\\*)" // Italics *text*
|
||||
"|(\\_)([^\\_]{2,})(\\_)" // Italics _text_
|
||||
"|(\\_\\_)([^\\_\\_]{2,})(\\_\\_)" // Bold __text__
|
||||
"|(\\-)([^\\-]{2,})(\\-)" // Underline -text-
|
||||
"|(\\~)([^\\~]{2,})(\\~)" // Strike ~text~
|
||||
"|(\\~~)([^\\~\\~]{2,})(\\~~)" // Strike ~~text~~
|
||||
);
|
||||
|
||||
// Support for multi-line text
|
||||
QStringList messageLines = str.split("\n");
|
||||
QStringList outLines;
|
||||
for (int i = 0; i < messageLines.size(); ++i)
|
||||
{
|
||||
out = messageLines.at(i);
|
||||
int offset = 0;
|
||||
while ((offset = exp.indexIn(out, offset)) != -1)
|
||||
{
|
||||
QString snipCheck = out.mid(offset-1,exp.cap(0).length()+2);
|
||||
QString snippet = exp.cap(0).trimmed();
|
||||
|
||||
QString htmledSnippet;
|
||||
|
||||
// Check for surrounding spaces and/or beginning/end of line
|
||||
if (!((snipCheck.startsWith(' ') || offset == 0) && (snipCheck.endsWith(' ') || offset + snipCheck.trimmed().length() == out.length())))
|
||||
{
|
||||
offset += snippet.length();
|
||||
continue;
|
||||
}
|
||||
|
||||
int mul = 0; // Determines how many characters to strip from markdown text
|
||||
|
||||
// Set mul depending on markdownPreference
|
||||
if (Settings::getInstance().getMarkdownPreference() == 2)
|
||||
mul = 2;
|
||||
|
||||
// Match captured string to corresponding md format
|
||||
if (exp.cap(1) == "**") // Bold **text**
|
||||
htmledSnippet = QString(" <b>%1</b> ").arg(snippet.mid(mul,snippet.length()-2*mul));
|
||||
else if (exp.cap(4) == "*" && snippet.length() > 2) // Italics *text*
|
||||
htmledSnippet = QString(" <i>%1</i> ").arg(snippet.mid(mul/2,snippet.length()-mul));
|
||||
else if (exp.cap(7) == "_" && snippet.length() > 2) // Italics _text_
|
||||
htmledSnippet = QString(" <i>%1</i> ").arg(snippet.mid(mul/2,snippet.length()-mul));
|
||||
else if (exp.cap(10) == "__"&& snippet.length() > 4) // Bold __text__
|
||||
htmledSnippet = QString(" <b>%1</b> ").arg(snippet.mid(mul,snippet.length()-2*mul));
|
||||
else if (exp.cap(13) == "-" && snippet.length() > 2) // Underline -text-
|
||||
htmledSnippet = QString(" <u>%1</u> ").arg(snippet.mid(mul/2,snippet.length()-mul));
|
||||
else if (exp.cap(16) == "~" && snippet.length() > 2) // Strikethrough ~text~
|
||||
htmledSnippet = QString(" <s>%1</s> ").arg(snippet.mid(mul/2,snippet.length()-mul));
|
||||
else if (exp.cap(19) == "~~" && snippet.length() > 4) // Strikethrough ~~text~~
|
||||
htmledSnippet = QString(" <s>%1</s> ").arg(snippet.mid(mul,snippet.length()-2*mul));
|
||||
else
|
||||
htmledSnippet = snippet;
|
||||
out.replace(offset, exp.cap().length(), htmledSnippet);
|
||||
offset += htmledSnippet.length();
|
||||
}
|
||||
outLines.push_back(out);
|
||||
}
|
||||
return outLines.join("\n");
|
||||
}
|
||||
|
||||
QString ChatMessage::detectAnchors(const QString &str)
|
||||
{
|
||||
QString out;
|
||||
|
|
|
@ -45,6 +45,13 @@ public:
|
|||
ALERT,
|
||||
};
|
||||
|
||||
enum MarkdownType
|
||||
{
|
||||
NONE,
|
||||
WITH_CHARS,
|
||||
WITHOUT_CHARS,
|
||||
};
|
||||
|
||||
ChatMessage();
|
||||
|
||||
static ChatMessage::Ptr createChatMessage(const QString& sender, const QString& rawMessage, MessageType type, bool isMe, const QDateTime& date = QDateTime());
|
||||
|
@ -61,6 +68,7 @@ public:
|
|||
void hideDate();
|
||||
|
||||
protected:
|
||||
static QString detectMarkdown(const QString& str);
|
||||
static QString detectAnchors(const QString& str);
|
||||
static QString detectQuotes(const QString& str, MessageType type);
|
||||
static QString wrapDiv(const QString& str, const QString& div);
|
||||
|
|
0
src/chatlog/createChatMessage
Normal file
0
src/chatlog/createChatMessage
Normal file
|
@ -180,6 +180,7 @@ void Settings::loadGlobal()
|
|||
separateWindow = s.value("separateWindow", false).toBool();
|
||||
dontGroupWindows = s.value("dontGroupWindows", true).toBool();
|
||||
groupchatPosition = s.value("groupchatPosition", true).toBool();
|
||||
markdownPreference = static_cast<MarkdownType>(s.value("markdownPreference", 1).toInt());
|
||||
s.endGroup();
|
||||
|
||||
s.beginGroup("Advanced");
|
||||
|
@ -245,7 +246,7 @@ void Settings::loadGlobal()
|
|||
camVideoFPS = s.value("camVideoFPS", 0).toUInt();
|
||||
s.endGroup();
|
||||
|
||||
// Read the embedded DHT bootsrap nodes list if needed
|
||||
// Read the embedded DHT bootstrap nodes list if needed
|
||||
if (dhtServerList.isEmpty())
|
||||
{
|
||||
QSettings rcs(":/conf/settings.ini", QSettings::IniFormat);
|
||||
|
@ -400,6 +401,7 @@ void Settings::saveGlobal()
|
|||
s.setValue("groupchatPosition", groupchatPosition);
|
||||
s.setValue("autoSaveEnabled", autoSaveEnabled);
|
||||
s.setValue("globalAutoAcceptDir", globalAutoAcceptDir);
|
||||
s.setValue("markdownPreference", static_cast<int>(markdownPreference));
|
||||
s.endGroup();
|
||||
|
||||
s.beginGroup("Advanced");
|
||||
|
@ -1056,6 +1058,18 @@ void Settings::setDateFormat(const QString &format)
|
|||
dateFormat = format;
|
||||
}
|
||||
|
||||
MarkdownType Settings::getMarkdownPreference() const
|
||||
{
|
||||
QMutexLocker locker{&bigLock};
|
||||
return markdownPreference;
|
||||
}
|
||||
|
||||
void Settings::setMarkdownPreference(MarkdownType newValue)
|
||||
{
|
||||
QMutexLocker locker{&bigLock};
|
||||
markdownPreference = newValue;
|
||||
}
|
||||
|
||||
QByteArray Settings::getWindowGeometry() const
|
||||
{
|
||||
QMutexLocker locker{&bigLock};
|
||||
|
|
|
@ -35,6 +35,8 @@ namespace Db { enum class syncType; }
|
|||
|
||||
enum ProxyType {ptNone, ptSOCKS5, ptHTTP};
|
||||
|
||||
enum MarkdownType {NONE, WITH_CHARS, WITHOUT_CHARS};
|
||||
|
||||
class Settings : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
@ -178,6 +180,9 @@ public:
|
|||
int getThemeColor() const;
|
||||
void setThemeColor(const int& value);
|
||||
|
||||
MarkdownType getMarkdownPreference() const;
|
||||
void setMarkdownPreference(MarkdownType newValue);
|
||||
|
||||
bool isCurstomEmojiFont() const;
|
||||
void setCurstomEmojiFont(bool value);
|
||||
|
||||
|
@ -368,6 +373,7 @@ private:
|
|||
bool showSystemTray;
|
||||
|
||||
// ChatView
|
||||
MarkdownType markdownPreference;
|
||||
int firstColumnHandlePos;
|
||||
int secondColumnHandlePosFromRight;
|
||||
QString timestampFormat;
|
||||
|
|
|
@ -86,6 +86,9 @@ static QStringList langs = {"Български",
|
|||
"Українська",
|
||||
"Arabic",
|
||||
"简体中文"};
|
||||
static QStringList mdPrefs = {"Plaintext",
|
||||
"Show Formatting Characters",
|
||||
"Don't Show Formatting Characters"};
|
||||
|
||||
static QStringList timeFormats = {"hh:mm AP", "hh:mm", "hh:mm:ss AP", "hh:mm:ss"};
|
||||
// http://doc.qt.io/qt-4.8/qdate.html#fromString
|
||||
|
@ -107,6 +110,10 @@ GeneralForm::GeneralForm(SettingsWidget *myParent) :
|
|||
bodyUI->transComboBox->insertItem(i, langs[i]);
|
||||
|
||||
bodyUI->transComboBox->setCurrentIndex(locales.indexOf(Settings::getInstance().getTranslation()));
|
||||
for (int i = 0; i < mdPrefs.size(); i++)
|
||||
bodyUI->markdownComboBox->insertItem(i, mdPrefs[i]);
|
||||
|
||||
bodyUI->markdownComboBox->setCurrentIndex(Settings::getInstance().getMarkdownPreference());
|
||||
bodyUI->cbAutorun->setChecked(Settings::getInstance().getAutorun());
|
||||
|
||||
bool showSystemTray = Settings::getInstance().getShowSystemTray();
|
||||
|
@ -205,6 +212,7 @@ GeneralForm::GeneralForm(SettingsWidget *myParent) :
|
|||
connect(bodyUI->showWindow, &QCheckBox::stateChanged, this, &GeneralForm::onShowWindowChanged);
|
||||
connect(bodyUI->showInFront, &QCheckBox::stateChanged, this, &GeneralForm::onSetShowInFront);
|
||||
connect(bodyUI->notifySound, &QCheckBox::stateChanged, this, &GeneralForm::onSetNotifySound);
|
||||
connect(bodyUI->markdownComboBox, &QComboBox::currentTextChanged, this, &GeneralForm::onMarkdownUpdated);
|
||||
connect(bodyUI->groupAlwaysNotify, &QCheckBox::stateChanged, this, &GeneralForm::onSetGroupAlwaysNotify);
|
||||
connect(bodyUI->autoacceptFiles, &QCheckBox::stateChanged, this, &GeneralForm::onAutoAcceptFileChange);
|
||||
connect(bodyUI->autoSaveFilesDir, SIGNAL(clicked()), this, SLOT(onAutoSaveDirChange()));
|
||||
|
@ -231,7 +239,7 @@ GeneralForm::GeneralForm(SettingsWidget *myParent) :
|
|||
|
||||
// prevent stealing mouse wheel scroll
|
||||
// scrolling event won't be transmitted to comboboxes or qspinboxes when scrolling
|
||||
// you can scroll through general settings without accidentially chaning theme/skin/icons etc.
|
||||
// you can scroll through general settings without accidentially changing theme/skin/icons etc.
|
||||
// @see GeneralForm::eventFilter(QObject *o, QEvent *e) at the bottom of this file for more
|
||||
for (QComboBox *cb : findChildren<QComboBox*>())
|
||||
{
|
||||
|
@ -365,6 +373,11 @@ void GeneralForm::onUseEmoticonsChange()
|
|||
bodyUI->smileyPackBrowser->setEnabled(bodyUI->useEmoticons->isChecked());
|
||||
}
|
||||
|
||||
void GeneralForm::onMarkdownUpdated()
|
||||
{
|
||||
Settings::getInstance().setMarkdownPreference(static_cast<MarkdownType>(bodyUI->markdownComboBox->currentIndex()));
|
||||
}
|
||||
|
||||
void GeneralForm::onSetStatusChange()
|
||||
{
|
||||
Settings::getInstance().setStatusChangeNotificationEnabled(bodyUI->statusChanges->isChecked());
|
||||
|
|
|
@ -53,6 +53,7 @@ private slots:
|
|||
void onStyleSelected(QString style);
|
||||
void onTimestampSelected(int index);
|
||||
void onDateFormatSelected(int index);
|
||||
void onMarkdownUpdated();
|
||||
void onSetStatusChange();
|
||||
void onAutoAwayChanged();
|
||||
void onUseEmoticonsChange();
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>671</width>
|
||||
<width>1312</width>
|
||||
<height>1098</height>
|
||||
</rect>
|
||||
</property>
|
||||
|
@ -14,7 +14,16 @@
|
|||
<string notr="true">Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<property name="margin">
|
||||
<property name="leftMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<item>
|
||||
|
@ -30,8 +39,8 @@
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>631</width>
|
||||
<height>1141</height>
|
||||
<width>1277</width>
|
||||
<height>1272</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4" stretch="0,0,0,1">
|
||||
|
@ -227,6 +236,9 @@ instead of closing itself.</string>
|
|||
<property name="toolTip">
|
||||
<string>Set to 0 to disable</string>
|
||||
</property>
|
||||
<property name="showGroupSeparator" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="suffix">
|
||||
<string notr="true"> min</string>
|
||||
</property>
|
||||
|
@ -236,9 +248,6 @@ instead of closing itself.</string>
|
|||
<property name="maximum">
|
||||
<number>2147483647</number>
|
||||
</property>
|
||||
<property name="showGroupSeparator" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
|
@ -350,6 +359,46 @@ instead of closing itself.</string>
|
|||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="generalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="transLabel_2">
|
||||
<property name="toolTip">
|
||||
<string>New markdown preference may not load until qTox restarts.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Markdown:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="markdownComboBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Select Markdown preference.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="generalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="statusChanges">
|
||||
<property name="text">
|
||||
|
|
Loading…
Reference in New Issue
Block a user