diff --git a/tools/chatgui/CMakeLists.txt b/tools/chatgui/CMakeLists.txt index 3a9b0b4d..0a64777f 100644 --- a/tools/chatgui/CMakeLists.txt +++ b/tools/chatgui/CMakeLists.txt @@ -105,6 +105,7 @@ add_executable(vibechat src/channelsettingsdialog.cpp src/chatview.cpp src/messagedelegate.cpp + src/message_links.cpp src/messagelist.cpp src/accountlist.cpp src/accountdelegate.cpp @@ -232,7 +233,7 @@ target_link_libraries(test_emoji_playback PRIVATE ${QT_LIBS} ${QT_TEST_LIB} rlot add_test(NAME test_emoji_playback COMMAND test_emoji_playback) set_tests_properties(test_emoji_playback PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=offscreen" TIMEOUT 30) -add_executable(test_invite_links tests/test_invite_links.cpp src/messagedelegate.cpp src/chatview.cpp +add_executable(test_invite_links tests/test_invite_links.cpp src/message_links.cpp src/messagedelegate.cpp src/chatview.cpp src/accountdelegate.cpp src/flagpainter.cpp src/animtimer.cpp src/lottieicon.cpp src/emoji.cpp src/voiceplayback.cpp src/sound_manager.cpp src/voice_audio_io.cpp src/audio_device.cpp transport/miniaudio_impl.c transport/audio_diagnostics.cpp) diff --git a/tools/chatgui/src/message_links.cpp b/tools/chatgui/src/message_links.cpp new file mode 100644 index 00000000..cbe988e5 --- /dev/null +++ b/tools/chatgui/src/message_links.cpp @@ -0,0 +1,28 @@ +#include "message_links.h" +#include +#include + +/* Отделить адрес от текста, сохраняя сбалансированные скобки внутри пути/query. */ +QVector messageLinks(const QString& text) { + static const QRegularExpression pattern(QStringLiteral( + "utun://[A-Za-z0-9+/]+={0,2}|(?i:https?://)[^\\s\\p{Z}\\p{Cc}<>\"'`«»“”‘’]+")); + QVector links; + auto matches = pattern.globalMatch(text); + while (matches.hasNext()) { + const auto match = matches.next(); + QString url = match.captured(); + if (!url.startsWith(QStringLiteral("utun://"))) { + while (!url.isEmpty()) { + const QChar last = url.back(); + const int closing = QStringLiteral(")]}").indexOf(last); + const bool unmatched = closing >= 0 && url.count(last) > url.count(QStringLiteral("([{").at(closing)); + if (!QStringLiteral(".,;:!?").contains(last) && !unmatched) break; + url.chop(1); + } + const QUrl parsed(url, QUrl::StrictMode); + if (!parsed.isValid() || parsed.host().isEmpty()) continue; + } + links.append({(int)match.capturedStart(), (int)url.length(), url}); + } + return links; +} diff --git a/tools/chatgui/src/message_links.h b/tools/chatgui/src/message_links.h new file mode 100644 index 00000000..d3fee140 --- /dev/null +++ b/tools/chatgui/src/message_links.h @@ -0,0 +1,14 @@ +/* Ссылки в обычном тексте сообщения: границы UTF-16 и полный адрес для подсветки и нажатия. + * Поддерживает utun:// и HTTP/HTTPS; внешняя пунктуация не входит в адрес. + * Разбор не меняет сообщение и не открывает адреса. Некорректные URL остаются обычным текстом. */ +#pragma once +#include +#include + +struct MessageLink { + int start; + int length; + QString url; +}; + +QVector messageLinks(const QString& text);