Compare commits
36 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
620639ef83 | ||
|
|
9329ce9059 | ||
|
|
42d4fdb89f | ||
|
|
fad7996e63 | ||
|
|
039cad21a5 | ||
|
|
3fdd6848c5 | ||
|
|
c0043d56ea | ||
|
|
ddbd36e446 | ||
|
|
d09ece4203 | ||
|
|
2b39da483b | ||
|
|
d9711f8ebd | ||
|
|
ede7ad1a4c | ||
|
|
55167ea95b | ||
|
|
7dffc6e912 | ||
|
|
f2867df340 | ||
|
|
a21b6d7416 | ||
|
|
07f07c5eeb | ||
|
|
b179e5332a | ||
|
|
5cc1871f2f | ||
|
|
39777f6149 | ||
|
|
6660206e61 | ||
|
|
9592e7dfc8 | ||
|
|
4432863612 | ||
|
|
b8018f5a7f | ||
|
|
44c24f9fff | ||
|
|
204a08df14 | ||
|
|
0881e5b20d | ||
|
|
3bd34fcff0 | ||
|
|
60f91ebce4 | ||
|
|
12a77cffd8 | ||
|
|
03c2fc2c48 | ||
|
|
b7d7ba82f8 | ||
|
|
ad54fc6459 | ||
|
|
101ba05ce3 | ||
|
|
6ab31219ed | ||
|
|
8afc245422 |
@@ -9,7 +9,7 @@
|
||||
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
|
||||
ProcessorArchitecture="ARCHITECTURE"
|
||||
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
|
||||
Version="2.4.5.0" />
|
||||
Version="2.4.7.0" />
|
||||
<Properties>
|
||||
<DisplayName>Telegram Desktop</DisplayName>
|
||||
<PublisherDisplayName>Telegram FZ-LLC</PublisherDisplayName>
|
||||
|
||||
@@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 2,4,5,0
|
||||
PRODUCTVERSION 2,4,5,0
|
||||
FILEVERSION 2,4,7,0
|
||||
PRODUCTVERSION 2,4,7,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -62,10 +62,10 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Telegram FZ-LLC"
|
||||
VALUE "FileDescription", "Telegram Desktop"
|
||||
VALUE "FileVersion", "2.4.5.0"
|
||||
VALUE "FileVersion", "2.4.7.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2020"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "2.4.5.0"
|
||||
VALUE "ProductVersion", "2.4.7.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 2,4,5,0
|
||||
PRODUCTVERSION 2,4,5,0
|
||||
FILEVERSION 2,4,7,0
|
||||
PRODUCTVERSION 2,4,7,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -53,10 +53,10 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Telegram FZ-LLC"
|
||||
VALUE "FileDescription", "Telegram Desktop Updater"
|
||||
VALUE "FileVersion", "2.4.5.0"
|
||||
VALUE "FileVersion", "2.4.7.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2020"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "2.4.5.0"
|
||||
VALUE "ProductVersion", "2.4.7.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -4230,20 +4230,18 @@ void ApiWrap::sendFiles(
|
||||
auto tasks = std::vector<std::unique_ptr<Task>>();
|
||||
tasks.reserve(list.files.size());
|
||||
for (auto &file : list.files) {
|
||||
if (album) {
|
||||
if (file.type == Ui::PreparedFile::Type::Photo
|
||||
&& type != SendMediaType::File) {
|
||||
type = SendMediaType::Photo;
|
||||
} else {
|
||||
type = SendMediaType::File;
|
||||
}
|
||||
}
|
||||
const auto uploadWithType = !album
|
||||
? type
|
||||
: (file.type == Ui::PreparedFile::Type::Photo
|
||||
&& type != SendMediaType::File)
|
||||
? SendMediaType::Photo
|
||||
: SendMediaType::File;
|
||||
tasks.push_back(std::make_unique<FileLoadTask>(
|
||||
&session(),
|
||||
file.path,
|
||||
file.content,
|
||||
std::move(file.information),
|
||||
type,
|
||||
uploadWithType,
|
||||
to,
|
||||
caption,
|
||||
album));
|
||||
|
||||
@@ -77,8 +77,12 @@ std::vector<not_null<PeerData*>> PrivacyExceptionsBoxController::getResult() con
|
||||
}
|
||||
|
||||
void PrivacyExceptionsBoxController::rowClicked(not_null<PeerListRow*> row) {
|
||||
const auto peer = row->peer();
|
||||
|
||||
// This call may delete row, if it was a search result row.
|
||||
delegate()->peerListSetRowChecked(row, !row->checked());
|
||||
if (const auto channel = row->peer()->asChannel()) {
|
||||
|
||||
if (const auto channel = peer->asChannel()) {
|
||||
if (!channel->membersCountKnown()) {
|
||||
channel->updateFull();
|
||||
}
|
||||
|
||||
@@ -533,6 +533,9 @@ void SendFilesBox::pushBlock(int from, int till) {
|
||||
block.takeWidget(),
|
||||
QMargins(0, _inner->count() ? st::sendMediaRowSkip : 0, 0, 0));
|
||||
|
||||
const auto preventDelete =
|
||||
widget->lifetime().make_state<rpl::event_stream<int>>();
|
||||
|
||||
block.itemDeleteRequest(
|
||||
) | rpl::filter([=] {
|
||||
return !_removingIndex;
|
||||
@@ -543,12 +546,19 @@ void SendFilesBox::pushBlock(int from, int till) {
|
||||
if (index < 0 || index >= _list.files.size()) {
|
||||
return;
|
||||
}
|
||||
// Prevent item delete if it is the only one.
|
||||
if (_list.files.size() == 1) {
|
||||
preventDelete->fire_copy(0);
|
||||
return;
|
||||
}
|
||||
_list.files.erase(_list.files.begin() + index);
|
||||
refreshAllAfterChanges(from);
|
||||
});
|
||||
}, widget->lifetime());
|
||||
|
||||
block.itemReplaceRequest(
|
||||
rpl::merge(
|
||||
block.itemReplaceRequest(),
|
||||
preventDelete->events()
|
||||
) | rpl::start_with_next([=](int index) {
|
||||
const auto replace = [=](Ui::PreparedList list) {
|
||||
if (list.files.empty()) {
|
||||
@@ -591,6 +601,7 @@ void SendFilesBox::pushBlock(int from, int till) {
|
||||
void SendFilesBox::refreshControls() {
|
||||
refreshTitleText();
|
||||
updateSendWayControlsVisibility();
|
||||
updateCaptionPlaceholder();
|
||||
}
|
||||
|
||||
void SendFilesBox::setupSendWayControls() {
|
||||
@@ -924,6 +935,22 @@ void SendFilesBox::setInnerFocus() {
|
||||
}
|
||||
}
|
||||
|
||||
void SendFilesBox::saveSendWaySettings() {
|
||||
auto way = _sendWay.current();
|
||||
auto oldWay = Core::App().settings().sendFilesWay();
|
||||
if (_groupFiles->isHidden()) {
|
||||
way.setGroupFiles(oldWay.groupFiles());
|
||||
}
|
||||
if (_list.overrideSendImagesAsPhotos == way.sendImagesAsPhotos()
|
||||
|| _sendImagesAsPhotos->isHidden()) {
|
||||
way.setSendImagesAsPhotos(oldWay.sendImagesAsPhotos());
|
||||
}
|
||||
if (way != oldWay) {
|
||||
Core::App().settings().setSendFilesWay(way);
|
||||
Core::App().saveSettingsDelayed();
|
||||
}
|
||||
}
|
||||
|
||||
void SendFilesBox::send(
|
||||
Api::SendOptions options,
|
||||
bool ctrlShiftEnter) {
|
||||
@@ -939,19 +966,7 @@ void SendFilesBox::send(
|
||||
return;
|
||||
}
|
||||
|
||||
auto way = _sendWay.current();
|
||||
auto oldWay = Core::App().settings().sendFilesWay();
|
||||
if (_groupFiles->isHidden()) {
|
||||
way.setGroupFiles(oldWay.groupFiles());
|
||||
}
|
||||
if (_list.overrideSendImagesAsPhotos == way.sendImagesAsPhotos()
|
||||
|| _sendImagesAsPhotos->isHidden()) {
|
||||
way.setSendImagesAsPhotos(oldWay.sendImagesAsPhotos());
|
||||
}
|
||||
if (way != oldWay) {
|
||||
Core::App().settings().setSendFilesWay(way);
|
||||
Core::App().saveSettingsDelayed();
|
||||
}
|
||||
saveSendWaySettings();
|
||||
|
||||
for (auto &block : _blocks) {
|
||||
block.applyAlbumOrder();
|
||||
@@ -963,7 +978,7 @@ void SendFilesBox::send(
|
||||
: TextWithTags();
|
||||
_confirmedCallback(
|
||||
std::move(_list),
|
||||
way,
|
||||
_sendWay.current(),
|
||||
std::move(caption),
|
||||
options,
|
||||
ctrlShiftEnter);
|
||||
|
||||
@@ -135,6 +135,7 @@ private:
|
||||
void sendSilent();
|
||||
void sendScheduled();
|
||||
void captionResized();
|
||||
void saveSendWaySettings();
|
||||
|
||||
void setupDragArea();
|
||||
void refreshTitleText();
|
||||
|
||||
@@ -21,26 +21,6 @@ namespace {
|
||||
|
||||
std::map<int, const char*> BetaLogs() {
|
||||
return {
|
||||
{
|
||||
1009020,
|
||||
"- Fix crash in shared links search.\n"
|
||||
|
||||
"- Fix blurred thumbnails in albums with video files.\n"
|
||||
|
||||
"- Fix a possible crash in animated stickers rendering."
|
||||
},
|
||||
{
|
||||
1009022,
|
||||
"- Organize chats into Chat Folders if you have too many chats.\n"
|
||||
},
|
||||
{
|
||||
2000001,
|
||||
"- Switch between folders using Ctrl+1, ..., Ctrl+8.\n"
|
||||
|
||||
"- Fix crash when a pinned in folder chat was added to archive.\n"
|
||||
|
||||
"- Fix font issues in Linux version."
|
||||
},
|
||||
{
|
||||
2001008,
|
||||
"- Add support for full group message history export.\n"
|
||||
@@ -95,6 +75,16 @@ std::map<int, const char*> BetaLogs() {
|
||||
|
||||
"- Enjoy dark native window frame for Telegram night mode on Windows.\n"
|
||||
},
|
||||
{
|
||||
2004006,
|
||||
"- Fix image compression option when sending files with drag-n-drop.\n"
|
||||
|
||||
"- Fix caption text selection in media albums.\n"
|
||||
|
||||
"- Fix drafts display in personal chats in the chats list.\n"
|
||||
|
||||
"- Bug fixes and other minor improvements.\n"
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -25,14 +25,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
#include <QtGui/QGuiApplication>
|
||||
|
||||
namespace {
|
||||
|
||||
bool UrlRequiresConfirmation(const QUrl &url) {
|
||||
using namespace qthelp;
|
||||
return !regex_match(qsl("(^|\\.)(telegram\\.org|telegra\\.ph|telesco\\.pe)$"), url.host(), RegExOption::CaseInsensitive);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
return !regex_match(
|
||||
"(^|\\.)(telegram\\.(org|me|dog)|t\\.me|telegra\\.ph|telesco\\.pe)$",
|
||||
url.host(),
|
||||
RegExOption::CaseInsensitive);
|
||||
}
|
||||
|
||||
void HiddenUrlClickHandler::Open(QString url, QVariant context) {
|
||||
url = Core::TryConvertUrlToLocal(url);
|
||||
@@ -48,7 +48,7 @@ void HiddenUrlClickHandler::Open(QString url, QVariant context) {
|
||||
open();
|
||||
} else {
|
||||
const auto parsedUrl = QUrl::fromUserInput(url);
|
||||
if (UrlRequiresConfirmation(url)
|
||||
if (UrlRequiresConfirmation(parsedUrl)
|
||||
&& QGuiApplication::keyboardModifiers() != Qt::ControlModifier) {
|
||||
Core::App().hideMediaView();
|
||||
const auto displayed = parsedUrl.isValid()
|
||||
|
||||
@@ -13,6 +13,8 @@ namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
|
||||
[[nodiscard]] bool UrlRequiresConfirmation(const QUrl &url);
|
||||
|
||||
class HiddenUrlClickHandler : public UrlClickHandler {
|
||||
public:
|
||||
HiddenUrlClickHandler(QString url) : UrlClickHandler(url, false) {
|
||||
|
||||
@@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "core/update_checker.h"
|
||||
|
||||
#include "platform/platform_specific.h"
|
||||
#include "base/platform/base_platform_info.h"
|
||||
#include "base/platform/base_platform_file_utilities.h"
|
||||
#include "base/timer.h"
|
||||
@@ -1575,9 +1576,16 @@ void UpdateApplication() {
|
||||
return "https://www.microsoft.com/en-us/store/p/telegram-desktop/9nztwsqntd0s";
|
||||
#elif defined OS_MAC_STORE // OS_WIN_STORE
|
||||
return "https://itunes.apple.com/ae/app/telegram-desktop/id946399090";
|
||||
#else // OS_WIN_STORE || OS_MAC_STORE
|
||||
#elif defined Q_OS_UNIX && !defined Q_OS_MAC // OS_WIN_STORE || OS_MAC_STORE
|
||||
if (Platform::InFlatpak()) {
|
||||
return "https://flathub.org/apps/details/org.telegram.desktop";
|
||||
} else if (Platform::InSnap()) {
|
||||
return "https://snapcraft.io/telegram-desktop";
|
||||
}
|
||||
return "https://desktop.telegram.org";
|
||||
#endif // OS_WIN_STORE || OS_MAC_STORE
|
||||
#else // OS_WIN_STORE || OS_MAC_STORE || (defined Q_OS_UNIX && !defined Q_OS_MAC)
|
||||
return "https://desktop.telegram.org";
|
||||
#endif // OS_WIN_STORE || OS_MAC_STORE || (defined Q_OS_UNIX && !defined Q_OS_MAC)
|
||||
}();
|
||||
UrlClickHandler::Open(url);
|
||||
} else {
|
||||
|
||||
@@ -22,7 +22,7 @@ constexpr auto AppId = "{53F49750-6209-4FBF-9CA8-7A333C87D1ED}"_cs;
|
||||
constexpr auto AppNameOld = "Telegram Win (Unofficial)"_cs;
|
||||
constexpr auto AppName = "Telegram Desktop"_cs;
|
||||
constexpr auto AppFile = "Telegram"_cs;
|
||||
constexpr auto AppVersion = 2004005;
|
||||
constexpr auto AppVersionStr = "2.4.5";
|
||||
constexpr auto AppVersion = 2004007;
|
||||
constexpr auto AppVersionStr = "2.4.7";
|
||||
constexpr auto AppBetaVersion = false;
|
||||
constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION;
|
||||
|
||||
@@ -368,8 +368,8 @@ void paintRow(
|
||||
|
||||
p.setFont(st::dialogsTextFont);
|
||||
auto &color = active ? st::dialogsTextFgServiceActive : (selected ? st::dialogsTextFgServiceOver : st::dialogsTextFgService);
|
||||
if (ShowSendActionInDialogs(history)
|
||||
&& !history->sendActionPainter()->paint(p, nameleft, texttop, availableWidth, fullWidth, color, ms)) {
|
||||
if (!ShowSendActionInDialogs(history)
|
||||
|| !history->sendActionPainter()->paint(p, nameleft, texttop, availableWidth, fullWidth, color, ms)) {
|
||||
if (history->cloudDraftTextCache.isEmpty()) {
|
||||
auto draftWrapped = textcmdLink(1, tr::lng_dialogs_text_from_wrapped(tr::now, lt_from, tr::lng_from_draft(tr::now)));
|
||||
auto draftText = supportMode
|
||||
@@ -396,8 +396,8 @@ void paintRow(
|
||||
|
||||
auto &color = active ? st::dialogsTextFgServiceActive : (selected ? st::dialogsTextFgServiceOver : st::dialogsTextFgService);
|
||||
p.setFont(st::dialogsTextFont);
|
||||
if (ShowSendActionInDialogs(history)
|
||||
&& !history->sendActionPainter()->paint(p, nameleft, texttop, availableWidth, fullWidth, color, ms)) {
|
||||
if (!ShowSendActionInDialogs(history)
|
||||
|| !history->sendActionPainter()->paint(p, nameleft, texttop, availableWidth, fullWidth, color, ms)) {
|
||||
// Empty history
|
||||
}
|
||||
} else if (!item->isEmpty()) {
|
||||
|
||||
@@ -1795,10 +1795,7 @@ void Widget::onCancelSearchInChat() {
|
||||
}
|
||||
setSearchInChat(Key());
|
||||
}
|
||||
_inner->clearFilter();
|
||||
_filter->clear();
|
||||
_filter->updatePlaceholder();
|
||||
applyFilterUpdate();
|
||||
applyFilterUpdate(true);
|
||||
if (!Adaptive::OneColumn() && !controller()->selectingPeer()) {
|
||||
emit cancelled();
|
||||
}
|
||||
|
||||
@@ -289,45 +289,6 @@ void HistoryInner::enumerateItemsInHistory(History *history, int historytop, Met
|
||||
|
||||
// Binary search should've skipped all the items that are above / below the visible area.
|
||||
if (TopToBottom) {
|
||||
if (itembottom <= _visibleAreaTop) {
|
||||
QStringList debug;
|
||||
for (const auto &logBlock : history->blocks) {
|
||||
QStringList debugItems;
|
||||
for (const auto &logItem : logBlock->messages) {
|
||||
debugItems.push_back(QString("%1,%2"
|
||||
).arg(logItem->y()
|
||||
).arg(logItem->height()
|
||||
));
|
||||
}
|
||||
debug.push_back(QString("b(%1,%2:%3)"
|
||||
).arg(logBlock->y()
|
||||
).arg(logBlock->height()
|
||||
).arg(debugItems.join(';')
|
||||
));
|
||||
}
|
||||
CrashReports::SetAnnotation(
|
||||
"geometry",
|
||||
QString("height:%1 "
|
||||
).arg(history->height()
|
||||
) + debug.join(';'));
|
||||
CrashReports::SetAnnotation(
|
||||
"info",
|
||||
QString("block:%1(%2,%3), "
|
||||
"item:%4(%5,%6), "
|
||||
"limits:%7,%8, "
|
||||
"has:%9"
|
||||
).arg(blockIndex
|
||||
).arg(block->y()
|
||||
).arg(block->height()
|
||||
).arg(itemIndex
|
||||
).arg(view->y()
|
||||
).arg(view->height()
|
||||
).arg(_visibleAreaTop
|
||||
).arg(_visibleAreaBottom
|
||||
).arg(Logs::b(history->hasPendingResizedItems())
|
||||
));
|
||||
Unexpected("itembottom > _visibleAreaTop");
|
||||
}
|
||||
Assert(itembottom > _visibleAreaTop);
|
||||
} else {
|
||||
Assert(itemtop < _visibleAreaBottom);
|
||||
@@ -415,8 +376,7 @@ void HistoryInner::enumerateUserpics(Method method) {
|
||||
auto userpicCallback = [&](not_null<Element*> view, int itemtop, int itembottom) {
|
||||
// Skip all service messages.
|
||||
const auto item = view->data();
|
||||
const auto message = item->toHistoryMessage();
|
||||
if (!message) return true;
|
||||
if (view->isHidden() || !item->toHistoryMessage()) return true;
|
||||
|
||||
if (lowestAttachedItemTop < 0 && view->isAttachedToNext()) {
|
||||
lowestAttachedItemTop = itemtop + view->marginTop();
|
||||
|
||||
@@ -3230,6 +3230,9 @@ void HistoryWidget::doneShow() {
|
||||
} else {
|
||||
handlePendingHistoryUpdate();
|
||||
}
|
||||
// If we show pinned bar here, we don't want it to change the
|
||||
// calculated and prepared scrollTop of the messages history.
|
||||
_preserveScrollTop = true;
|
||||
preloadHistoryIfNeeded();
|
||||
updatePinnedViewer();
|
||||
if (_pinnedBar) {
|
||||
@@ -3237,6 +3240,7 @@ void HistoryWidget::doneShow() {
|
||||
}
|
||||
checkHistoryActivation();
|
||||
App::wnd()->setInnerFocus();
|
||||
_preserveScrollTop = false;
|
||||
}
|
||||
|
||||
void HistoryWidget::finishAnimating() {
|
||||
@@ -5330,7 +5334,7 @@ void HistoryWidget::checkPinnedBarState() {
|
||||
_pinnedBarHeight = 0;
|
||||
_pinnedBar->heightValue(
|
||||
) | rpl::start_with_next([=](int height) {
|
||||
_topDelta = (height - _pinnedBarHeight);
|
||||
_topDelta = _preserveScrollTop ? 0 : (height - _pinnedBarHeight);
|
||||
_pinnedBarHeight = height;
|
||||
updateHistoryGeometry();
|
||||
updateControlsGeometry();
|
||||
|
||||
@@ -599,6 +599,7 @@ private:
|
||||
std::unique_ptr<HistoryView::PinnedTracker> _pinnedTracker;
|
||||
std::unique_ptr<Ui::PinnedBar> _pinnedBar;
|
||||
int _pinnedBarHeight = 0;
|
||||
bool _preserveScrollTop = false;
|
||||
FullMsgId _pinnedClickedId;
|
||||
std::optional<FullMsgId> _minPinnedId;
|
||||
|
||||
|
||||
@@ -493,6 +493,8 @@ void Element::recountAttachToPreviousInBlocks() {
|
||||
if (isHidden() || data()->isEmpty()) {
|
||||
if (const auto next = nextDisplayedInBlocks()) {
|
||||
next->recountAttachToPreviousInBlocks();
|
||||
} else if (const auto previous = previousDisplayedInBlocks()) {
|
||||
previous->setAttachToNext(false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1268,8 +1268,9 @@ void ListWidget::elementStartStickerLoop(not_null<const Element*> view) {
|
||||
}
|
||||
|
||||
void ListWidget::elementShowPollResults(
|
||||
not_null<PollData*> poll,
|
||||
FullMsgId context) {
|
||||
not_null<PollData*> poll,
|
||||
FullMsgId context) {
|
||||
_controller->showPollResults(poll, context);
|
||||
}
|
||||
|
||||
void ListWidget::elementShowTooltip(
|
||||
|
||||
@@ -135,8 +135,10 @@ QSize Contact::countOptimalSize() {
|
||||
auto minHeight = st.padding.top() + st.thumbSize + st.padding.bottom();
|
||||
if (_userId) {
|
||||
const auto msgsigned = item->Get<HistoryMessageSigned>();
|
||||
const auto views = item->Get<HistoryMessageViews>();
|
||||
if ((msgsigned && !msgsigned->isAnonymousRank)
|
||||
|| item->Has<HistoryMessageViews>()) {
|
||||
|| (views
|
||||
&& (views->views.count >= 0 || views->replies.count > 0))) {
|
||||
minHeight += st::msgDateFont->height - st::msgDateDelta.y();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,18 +129,18 @@ void Document::createComponents(bool caption) {
|
||||
if (const auto thumbed = Get<HistoryDocumentThumbed>()) {
|
||||
thumbed->_linksavel = std::make_shared<DocumentSaveClickHandler>(
|
||||
_data,
|
||||
_parent->data()->fullId());
|
||||
_realParent->fullId());
|
||||
thumbed->_linkopenwithl = std::make_shared<DocumentOpenWithClickHandler>(
|
||||
_data,
|
||||
_parent->data()->fullId());
|
||||
_realParent->fullId());
|
||||
thumbed->_linkcancell = std::make_shared<DocumentCancelClickHandler>(
|
||||
_data,
|
||||
_parent->data()->fullId());
|
||||
_realParent->fullId());
|
||||
}
|
||||
if (const auto voice = Get<HistoryDocumentVoice>()) {
|
||||
voice->_seekl = std::make_shared<VoiceSeekClickHandler>(
|
||||
_data,
|
||||
_parent->data()->fullId());
|
||||
_realParent->fullId());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,8 +195,10 @@ QSize Document::countOptimalSize() {
|
||||
|
||||
auto minHeight = st.padding.top() + st.thumbSize + st.padding.bottom();
|
||||
const auto msgsigned = item->Get<HistoryMessageSigned>();
|
||||
const auto views = item->Get<HistoryMessageViews>();
|
||||
if (!captioned && ((msgsigned && !msgsigned->isAnonymousRank)
|
||||
|| item->Has<HistoryMessageViews>()
|
||||
|| (views
|
||||
&& (views->views.count >= 0 || views->replies.count > 0))
|
||||
|| _parent->displayEditedBadge())) {
|
||||
minHeight += st::msgDateFont->height - st::msgDateDelta.y();
|
||||
}
|
||||
@@ -259,7 +261,7 @@ void Document::draw(
|
||||
const auto cornerDownload = downloadInCorner();
|
||||
|
||||
if (!_dataMedia->canBePlayed()) {
|
||||
_dataMedia->automaticLoad(_realParent->fullId(), _parent->data());
|
||||
_dataMedia->automaticLoad(_realParent->fullId(), _realParent);
|
||||
}
|
||||
bool loaded = dataLoaded(), displayLoading = _data->displayLoading();
|
||||
bool selected = (selection == FullSelection);
|
||||
@@ -451,7 +453,7 @@ void Document::draw(
|
||||
auto activew = qRound(availw * progress);
|
||||
if (!outbg
|
||||
&& !voice->_playback
|
||||
&& _parent->data()->hasUnreadMediaFlag()) {
|
||||
&& _realParent->hasUnreadMediaFlag()) {
|
||||
activew = availw;
|
||||
}
|
||||
auto bar_count = qMin(availw / (st::msgWaveformBar + st::msgWaveformSkip), wf_size);
|
||||
@@ -505,7 +507,7 @@ void Document::draw(
|
||||
p.setPen(status);
|
||||
p.drawTextLeft(nameleft, statustop, width, statusText);
|
||||
|
||||
if (_parent->data()->hasUnreadMediaFlag()) {
|
||||
if (_realParent->hasUnreadMediaFlag()) {
|
||||
auto w = st::normalFont->width(statusText);
|
||||
if (w + st::mediaUnreadSkip + st::mediaUnreadSize <= statuswidth) {
|
||||
p.setPen(Qt::NoPen);
|
||||
@@ -547,7 +549,7 @@ bool Document::downloadInCorner() const {
|
||||
return _data->isAudioFile()
|
||||
&& _data->canBeStreamed()
|
||||
&& !_data->inappPlaybackFailed()
|
||||
&& IsServerMsgId(_parent->data()->id);
|
||||
&& IsServerMsgId(_realParent->id);
|
||||
}
|
||||
|
||||
void Document::drawCornerDownload(Painter &p, bool selected, LayoutMode mode) const {
|
||||
@@ -685,7 +687,7 @@ TextState Document::textState(
|
||||
auto waveformbottom = st.padding.top() - topMinus + st::msgWaveformMax + st::msgWaveformMin;
|
||||
if (QRect(nameleft, nametop, namewidth, waveformbottom - nametop).contains(point)) {
|
||||
const auto state = ::Media::Player::instance()->getState(AudioMsgId::Type::Voice);
|
||||
if (state.id == AudioMsgId(_data, _parent->data()->fullId(), state.id.externalPlayId())
|
||||
if (state.id == AudioMsgId(_data, _realParent->fullId(), state.id.externalPlayId())
|
||||
&& !::Media::Player::IsStoppedOrStopping(state.state)) {
|
||||
if (!voice->seeking()) {
|
||||
voice->setSeekingStart((point.x() - nameleft) / float64(namewidth));
|
||||
@@ -812,7 +814,7 @@ bool Document::updateStatusText() const {
|
||||
|
||||
if (_data->isVoiceMessage()) {
|
||||
const auto state = ::Media::Player::instance()->getState(AudioMsgId::Type::Voice);
|
||||
if (state.id == AudioMsgId(_data, _parent->data()->fullId(), state.id.externalPlayId())
|
||||
if (state.id == AudioMsgId(_data, _realParent->fullId(), state.id.externalPlayId())
|
||||
&& !::Media::Player::IsStoppedOrStopping(state.state)) {
|
||||
if (auto voice = Get<HistoryDocumentVoice>()) {
|
||||
bool was = (voice->_playback != nullptr);
|
||||
@@ -838,19 +840,19 @@ bool Document::updateStatusText() const {
|
||||
voice->checkPlaybackFinished();
|
||||
}
|
||||
}
|
||||
if (!showPause && (state.id == AudioMsgId(_data, _parent->data()->fullId(), state.id.externalPlayId()))) {
|
||||
if (!showPause && (state.id == AudioMsgId(_data, _realParent->fullId(), state.id.externalPlayId()))) {
|
||||
showPause = ::Media::Player::instance()->isSeeking(AudioMsgId::Type::Voice);
|
||||
}
|
||||
} else if (_data->isAudioFile()) {
|
||||
const auto state = ::Media::Player::instance()->getState(AudioMsgId::Type::Song);
|
||||
if (state.id == AudioMsgId(_data, _parent->data()->fullId(), state.id.externalPlayId())
|
||||
if (state.id == AudioMsgId(_data, _realParent->fullId(), state.id.externalPlayId())
|
||||
&& !::Media::Player::IsStoppedOrStopping(state.state)) {
|
||||
statusSize = -1 - (state.position / state.frequency);
|
||||
realDuration = (state.length / state.frequency);
|
||||
showPause = ::Media::Player::ShowPauseIcon(state.state);
|
||||
} else {
|
||||
}
|
||||
if (!showPause && (state.id == AudioMsgId(_data, _parent->data()->fullId(), state.id.externalPlayId()))) {
|
||||
if (!showPause && (state.id == AudioMsgId(_data, _realParent->fullId(), state.id.externalPlayId()))) {
|
||||
showPause = ::Media::Player::instance()->isSeeking(AudioMsgId::Type::Song);
|
||||
}
|
||||
}
|
||||
@@ -960,7 +962,7 @@ void Document::clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed
|
||||
} else if (!pressed && voice->seeking()) {
|
||||
const auto type = AudioMsgId::Type::Voice;
|
||||
const auto state = ::Media::Player::instance()->getState(type);
|
||||
if (state.id == AudioMsgId(_data, _parent->data()->fullId(), state.id.externalPlayId()) && state.length) {
|
||||
if (state.id == AudioMsgId(_data, _realParent->fullId(), state.id.externalPlayId()) && state.length) {
|
||||
const auto currentProgress = voice->seekingCurrent();
|
||||
::Media::Player::instance()->finishSeeking(
|
||||
AudioMsgId::Type::Voice,
|
||||
|
||||
@@ -145,6 +145,16 @@ QSize GroupedMedia::countOptimalSize() {
|
||||
if (isBubbleBottom()) {
|
||||
minHeight += st::msgPadding.bottom();
|
||||
}
|
||||
} else if (_mode == Mode::Column && _parts.back().item->emptyText()) {
|
||||
const auto item = _parent->data();
|
||||
const auto msgsigned = item->Get<HistoryMessageSigned>();
|
||||
const auto views = item->Get<HistoryMessageViews>();
|
||||
if ((msgsigned && !msgsigned->isAnonymousRank)
|
||||
|| (views
|
||||
&& (views->views.count >= 0 || views->replies.count > 0))
|
||||
|| displayedEditBadge()) {
|
||||
minHeight += st::msgDateFont->height - st::msgDateDelta.y();
|
||||
}
|
||||
}
|
||||
|
||||
const auto groupPadding = groupedPadding();
|
||||
@@ -205,6 +215,16 @@ QSize GroupedMedia::countCurrentSize(int newWidth) {
|
||||
if (isBubbleBottom()) {
|
||||
newHeight += st::msgPadding.bottom();
|
||||
}
|
||||
} else if (_mode == Mode::Column && _parts.back().item->emptyText()) {
|
||||
const auto item = _parent->data();
|
||||
const auto msgsigned = item->Get<HistoryMessageSigned>();
|
||||
const auto views = item->Get<HistoryMessageViews>();
|
||||
if ((msgsigned && !msgsigned->isAnonymousRank)
|
||||
|| (views
|
||||
&& (views->views.count >= 0 || views->replies.count > 0))
|
||||
|| displayedEditBadge()) {
|
||||
newHeight += st::msgDateFont->height - st::msgDateDelta.y();
|
||||
}
|
||||
}
|
||||
|
||||
const auto groupPadding = groupedPadding();
|
||||
@@ -255,7 +275,8 @@ void GroupedMedia::draw(
|
||||
crl::time ms) const {
|
||||
const auto groupPadding = groupedPadding();
|
||||
const auto fullSelection = (selection == FullSelection);
|
||||
const auto textSelection = !fullSelection
|
||||
const auto textSelection = (_mode == Mode::Column)
|
||||
&& !fullSelection
|
||||
&& !IsSubGroupSelection(selection);
|
||||
for (auto i = 0, count = int(_parts.size()); i != count; ++i) {
|
||||
const auto &part = _parts[i];
|
||||
|
||||
@@ -8,7 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "info/profile/info_profile_values.h"
|
||||
|
||||
#include "core/application.h"
|
||||
#include "core/local_url_handlers.h"
|
||||
#include "core/click_handler_types.h"
|
||||
#include "main/main_session.h"
|
||||
#include "ui/wrap/slide_wrap.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
@@ -50,7 +50,7 @@ auto PlainUsernameValue(not_null<PeerData*> peer) {
|
||||
|
||||
void StripExternalLinks(TextWithEntities &text) {
|
||||
const auto local = [](const QString &url) {
|
||||
return Core::TryConvertUrlToLocal(url).startsWith(qstr("tg://"));
|
||||
return !UrlRequiresConfirmation(QUrl::fromUserInput(url));
|
||||
};
|
||||
const auto notLocal = [&](const EntityInText &entity) {
|
||||
if (entity.type() == EntityType::CustomUrl) {
|
||||
|
||||
@@ -34,6 +34,7 @@ namespace {
|
||||
constexpr auto kSuppressRatioAll = 0.2;
|
||||
constexpr auto kSuppressRatioSong = 0.05;
|
||||
constexpr auto kWaveformCounterBufferSize = 256 * 1024;
|
||||
constexpr auto kEffectDestructionDelay = crl::time(1000);
|
||||
|
||||
QMutex AudioMutex;
|
||||
ALCdevice *AudioDevice = nullptr;
|
||||
@@ -179,7 +180,7 @@ void ClosePlaybackDevice(not_null<Instance*> instance) {
|
||||
LOG(("Audio Info: Closing audio playback device."));
|
||||
|
||||
if (Player::mixer()) {
|
||||
Player::mixer()->detachTracks();
|
||||
Player::mixer()->prepareToCloseDevice();
|
||||
}
|
||||
instance->detachTracks();
|
||||
|
||||
@@ -320,6 +321,9 @@ void Mixer::Track::createStream(AudioMsgId::Type type) {
|
||||
alSourcei(stream.source, AL_LOOPING, 0);
|
||||
alSourcei(stream.source, AL_SOURCE_RELATIVE, 1);
|
||||
alSourcei(stream.source, AL_ROLLOFF_FACTOR, 0);
|
||||
if (alIsExtensionPresent("AL_SOFT_direct_channels_remix")) {
|
||||
alSourcei(stream.source, alGetEnumValue("AL_DIRECT_CHANNELS_SOFT"), 2);
|
||||
}
|
||||
alGenBuffers(3, stream.buffers);
|
||||
if (speedEffect) {
|
||||
applySourceSpeedEffect();
|
||||
@@ -383,9 +387,11 @@ void Mixer::Track::resetSpeedEffect() {
|
||||
if (isStreamCreated()) {
|
||||
removeSourceSpeedEffect();
|
||||
}
|
||||
OpenAL::alDeleteEffects(1, &speedEffect->effect);
|
||||
OpenAL::alDeleteAuxiliaryEffectSlots(1, &speedEffect->effectSlot);
|
||||
OpenAL::alDeleteFilters(1, &speedEffect->filter);
|
||||
if (Player::mixer()) {
|
||||
// Don't destroy effect slot immediately.
|
||||
// See https://github.com/kcat/openal-soft/issues/486
|
||||
Player::mixer()->scheduleEffectDestruction(*speedEffect);
|
||||
}
|
||||
}
|
||||
speedEffect->effect = speedEffect->effectSlot = speedEffect->filter = 0;
|
||||
}
|
||||
@@ -560,6 +566,7 @@ Mixer::Track::~Track() = default;
|
||||
|
||||
Mixer::Mixer(not_null<Audio::Instance*> instance)
|
||||
: _instance(instance)
|
||||
, _effectsDestructionTimer([=] { destroyStaleEffectsSafe(); })
|
||||
, _volumeVideo(kVolumeRound)
|
||||
, _volumeSong(kVolumeRound)
|
||||
, _fader(new Fader(&_faderThread))
|
||||
@@ -622,6 +629,60 @@ void Mixer::onUpdated(const AudioMsgId &audio) {
|
||||
Media::Player::Updated().notify(audio);
|
||||
}
|
||||
|
||||
// Thread: Any. Must be locked: AudioMutex.
|
||||
void Mixer::scheduleEffectDestruction(const SpeedEffect &effect) {
|
||||
_effectsForDestruction.emplace_back(
|
||||
crl::now() + kEffectDestructionDelay,
|
||||
effect);
|
||||
scheduleEffectsDestruction();
|
||||
}
|
||||
|
||||
// Thread: Any. Must be locked: AudioMutex.
|
||||
void Mixer::scheduleEffectsDestruction() {
|
||||
if (_effectsForDestruction.empty()) {
|
||||
return;
|
||||
}
|
||||
InvokeQueued(this, [=] {
|
||||
if (!_effectsDestructionTimer.isActive()) {
|
||||
_effectsDestructionTimer.callOnce(kEffectDestructionDelay + 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Thread: Main. Locks: AudioMutex.
|
||||
void Mixer::destroyStaleEffectsSafe() {
|
||||
QMutexLocker lock(&AudioMutex);
|
||||
destroyStaleEffects();
|
||||
}
|
||||
|
||||
// Thread: Main. Must be locked: AudioMutex.
|
||||
void Mixer::destroyStaleEffects() {
|
||||
const auto now = crl::now();
|
||||
const auto checkAndDestroy = [&](
|
||||
const std::pair<crl::time, SpeedEffect> &pair) {
|
||||
const auto &[when, effect] = pair;
|
||||
if (when && when > now) {
|
||||
return false;
|
||||
}
|
||||
OpenAL::alDeleteEffects(1, &effect.effect);
|
||||
OpenAL::alDeleteAuxiliaryEffectSlots(1, &effect.effectSlot);
|
||||
OpenAL::alDeleteFilters(1, &effect.filter);
|
||||
return true;
|
||||
};
|
||||
_effectsForDestruction.erase(
|
||||
ranges::remove_if(_effectsForDestruction, checkAndDestroy),
|
||||
end(_effectsForDestruction));
|
||||
scheduleEffectsDestruction();
|
||||
}
|
||||
|
||||
// Thread: Main. Must be locked: AudioMutex.
|
||||
void Mixer::destroyEffectsOnClose() {
|
||||
for (auto &[when, effect] : _effectsForDestruction) {
|
||||
when = 0;
|
||||
}
|
||||
destroyStaleEffects();
|
||||
}
|
||||
|
||||
void Mixer::onError(const AudioMsgId &audio) {
|
||||
emit stoppedOnError(audio);
|
||||
|
||||
@@ -823,6 +884,7 @@ void Mixer::forceToBufferExternal(const AudioMsgId &audioId) {
|
||||
_loader->forceToBufferExternal(audioId);
|
||||
}
|
||||
|
||||
// Thread: Main. Locks: AudioMutex.
|
||||
void Mixer::setSpeedFromExternal(const AudioMsgId &audioId, float64 speed) {
|
||||
QMutexLocker lock(&AudioMutex);
|
||||
const auto track = trackForType(audioId.type());
|
||||
@@ -1160,12 +1222,14 @@ void Mixer::setStoppedState(Track *current, State state) {
|
||||
}
|
||||
|
||||
// Thread: Main. Must be locked: AudioMutex.
|
||||
void Mixer::detachTracks() {
|
||||
void Mixer::prepareToCloseDevice() {
|
||||
for (auto i = 0; i != kTogetherLimit; ++i) {
|
||||
trackForType(AudioMsgId::Type::Voice, i)->detach();
|
||||
trackForType(AudioMsgId::Type::Song, i)->detach();
|
||||
}
|
||||
_videoTrack.detach();
|
||||
|
||||
destroyEffectsOnClose();
|
||||
}
|
||||
|
||||
// Thread: Main. Must be locked: AudioMutex.
|
||||
|
||||
@@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/chat/attach/attach_prepare.h"
|
||||
#include "core/file_location.h"
|
||||
#include "base/bytes.h"
|
||||
#include "base/timer.h"
|
||||
|
||||
#include <QtCore/QTimer>
|
||||
|
||||
@@ -145,7 +146,10 @@ public:
|
||||
// External player audio stream interface.
|
||||
void feedFromExternal(ExternalSoundPart &&part);
|
||||
void forceToBufferExternal(const AudioMsgId &audioId);
|
||||
|
||||
// Thread: Main. Locks: AudioMutex.
|
||||
void setSpeedFromExternal(const AudioMsgId &audioId, float64 speed);
|
||||
|
||||
Streaming::TimePoint getExternalSyncTimePoint(
|
||||
const AudioMsgId &audio) const;
|
||||
crl::time getExternalCorrectedTime(
|
||||
@@ -158,7 +162,7 @@ public:
|
||||
TrackState currentState(AudioMsgId::Type type);
|
||||
|
||||
// Thread: Main. Must be locked: AudioMutex.
|
||||
void detachTracks();
|
||||
void prepareToCloseDevice();
|
||||
|
||||
// Thread: Main. Must be locked: AudioMutex.
|
||||
void reattachIfNeeded();
|
||||
@@ -193,11 +197,13 @@ signals:
|
||||
void suppressAll(qint64 duration);
|
||||
|
||||
private:
|
||||
bool fadedStop(AudioMsgId::Type type, bool *fadedStart = 0);
|
||||
void resetFadeStartPosition(AudioMsgId::Type type, int positionInBuffered = -1);
|
||||
bool checkCurrentALError(AudioMsgId::Type type);
|
||||
|
||||
void externalSoundProgress(const AudioMsgId &audio);
|
||||
struct SpeedEffect {
|
||||
uint32 effect = 0;
|
||||
uint32 effectSlot = 0;
|
||||
uint32 filter = 0;
|
||||
int coarseTune = 0;
|
||||
float64 speed = 1.;
|
||||
};
|
||||
|
||||
class Track {
|
||||
public:
|
||||
@@ -206,8 +212,10 @@ private:
|
||||
// Thread: Any. Must be locked: AudioMutex.
|
||||
void reattach(AudioMsgId::Type type);
|
||||
|
||||
// Thread: Main. Must be locked: AudioMutex.
|
||||
void detach();
|
||||
void clear();
|
||||
|
||||
void started();
|
||||
|
||||
bool isStreamCreated() const;
|
||||
@@ -215,6 +223,7 @@ private:
|
||||
|
||||
int getNotQueuedBufferIndex();
|
||||
|
||||
// Thread: Main. Must be locked: AudioMutex.
|
||||
void setExternalData(std::unique_ptr<ExternalSoundData> data);
|
||||
void changeSpeedEffect(float64 speed);
|
||||
|
||||
@@ -242,13 +251,6 @@ private:
|
||||
Stream stream;
|
||||
std::unique_ptr<ExternalSoundData> externalData;
|
||||
|
||||
struct SpeedEffect {
|
||||
uint32 effect = 0;
|
||||
uint32 effectSlot = 0;
|
||||
uint32 filter = 0;
|
||||
int coarseTune = 0;
|
||||
float64 speed = 1.;
|
||||
};
|
||||
std::unique_ptr<SpeedEffect> speedEffect;
|
||||
crl::time lastUpdateWhen = 0;
|
||||
crl::time lastUpdatePosition = 0;
|
||||
@@ -263,6 +265,12 @@ private:
|
||||
|
||||
};
|
||||
|
||||
bool fadedStop(AudioMsgId::Type type, bool *fadedStart = 0);
|
||||
void resetFadeStartPosition(AudioMsgId::Type type, int positionInBuffered = -1);
|
||||
bool checkCurrentALError(AudioMsgId::Type type);
|
||||
|
||||
void externalSoundProgress(const AudioMsgId &audio);
|
||||
|
||||
// Thread: Any. Must be locked: AudioMutex.
|
||||
void setStoppedState(Track *current, State state = State::Stopped);
|
||||
|
||||
@@ -271,7 +279,18 @@ private:
|
||||
int *currentIndex(AudioMsgId::Type type);
|
||||
const int *currentIndex(AudioMsgId::Type type) const;
|
||||
|
||||
not_null<Audio::Instance*> _instance;
|
||||
// Thread: Any. Must be locked: AudioMutex.
|
||||
void scheduleEffectDestruction(const SpeedEffect &effect);
|
||||
void scheduleEffectsDestruction();
|
||||
|
||||
// Thread: Main. Must be locked: AudioMutex.
|
||||
void destroyStaleEffects();
|
||||
void destroyEffectsOnClose();
|
||||
|
||||
// Thread: Main. Locks: AudioMutex.
|
||||
void destroyStaleEffectsSafe();
|
||||
|
||||
const not_null<Audio::Instance*> _instance;
|
||||
|
||||
int _audioCurrent = 0;
|
||||
Track _audioTracks[kTogetherLimit];
|
||||
@@ -281,6 +300,9 @@ private:
|
||||
|
||||
Track _videoTrack;
|
||||
|
||||
std::vector<std::pair<crl::time, SpeedEffect>> _effectsForDestruction;
|
||||
base::Timer _effectsDestructionTimer;
|
||||
|
||||
QAtomicInt _volumeVideo;
|
||||
QAtomicInt _volumeSong;
|
||||
|
||||
|
||||
@@ -93,13 +93,15 @@ bool Panel::preventAutoHide() const {
|
||||
}
|
||||
|
||||
void Panel::updateControlsGeometry() {
|
||||
auto scrollTop = contentTop();
|
||||
auto width = contentWidth();
|
||||
auto scrollHeight = qMax(height() - scrollTop - contentBottom() - scrollMarginBottom(), 0);
|
||||
const auto scrollTop = contentTop();
|
||||
const auto width = contentWidth();
|
||||
const auto scrollHeight = qMax(
|
||||
height() - scrollTop - contentBottom() - scrollMarginBottom(),
|
||||
0);
|
||||
if (scrollHeight > 0) {
|
||||
_scroll->setGeometryToRight(contentRight(), scrollTop, width, scrollHeight);
|
||||
}
|
||||
if (auto widget = static_cast<TWidget*>(_scroll->widget())) {
|
||||
if (const auto widget = static_cast<TWidget*>(_scroll->widget())) {
|
||||
widget->resizeToWidth(width);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -706,29 +706,35 @@ void PipPanel::mouseMoveEvent(QMouseEvent *e) {
|
||||
if (!_dragState
|
||||
&& (point - _pressPoint).manhattanLength() > distance
|
||||
&& !_dragDisabled) {
|
||||
if (Platform::IsWayland()) {
|
||||
const auto stateEdges = RectPartToQtEdges(*_pressState);
|
||||
if (stateEdges) {
|
||||
if (!Platform::StartSystemResize(windowHandle(), stateEdges)) {
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) || defined DESKTOP_APP_QT_PATCHED
|
||||
windowHandle()->startSystemResize(stateEdges);
|
||||
#endif // Qt >= 5.15 || DESKTOP_APP_QT_PATCHED
|
||||
}
|
||||
} else {
|
||||
if (!Platform::StartSystemMove(windowHandle())) {
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) || defined DESKTOP_APP_QT_PATCHED
|
||||
windowHandle()->startSystemMove();
|
||||
#endif // Qt >= 5.15 || DESKTOP_APP_QT_PATCHED
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
_dragState = _pressState;
|
||||
updateDecorations();
|
||||
_dragStartGeometry = geometry().marginsRemoved(_padding);
|
||||
}
|
||||
if (_dragState) {
|
||||
processDrag(point);
|
||||
if (Platform::IsWayland()) {
|
||||
startSystemDrag();
|
||||
} else {
|
||||
processDrag(point);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PipPanel::startSystemDrag() {
|
||||
Expects(_dragState.has_value());
|
||||
|
||||
const auto stateEdges = RectPartToQtEdges(*_dragState);
|
||||
if (stateEdges) {
|
||||
if (!Platform::StartSystemResize(windowHandle(), stateEdges)) {
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) || defined DESKTOP_APP_QT_PATCHED
|
||||
windowHandle()->startSystemResize(stateEdges);
|
||||
#endif // Qt >= 5.15 || DESKTOP_APP_QT_PATCHED
|
||||
}
|
||||
} else {
|
||||
if (!Platform::StartSystemMove(windowHandle())) {
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) || defined DESKTOP_APP_QT_PATCHED
|
||||
windowHandle()->startSystemMove();
|
||||
#endif // Qt >= 5.15 || DESKTOP_APP_QT_PATCHED
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -783,6 +789,9 @@ void PipPanel::finishDrag(QPoint point) {
|
||||
const auto position = pos();
|
||||
const auto clamped = [&] {
|
||||
auto result = position;
|
||||
if (Platform::IsWayland()) {
|
||||
return result;
|
||||
}
|
||||
if (result.x() > screen.x() + screen.width() - inner.width()) {
|
||||
result.setX(screen.x() + screen.width() - inner.width());
|
||||
}
|
||||
|
||||
@@ -83,6 +83,7 @@ private:
|
||||
void setPositionOnScreen(Position position, QRect available);
|
||||
|
||||
QScreen *myScreen() const;
|
||||
void startSystemDrag();
|
||||
void processDrag(QPoint point);
|
||||
void finishDrag(QPoint point);
|
||||
void updatePositionAnimated();
|
||||
|
||||
@@ -65,7 +65,7 @@ QByteArray DnsUserAgent() {
|
||||
static const auto kResult = QByteArray(
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
|
||||
"AppleWebKit/537.36 (KHTML, like Gecko) "
|
||||
"Chrome/86.0.4240.75 Safari/537.36");
|
||||
"Chrome/86.0.4240.111 Safari/537.36");
|
||||
return kResult;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "mtproto/mtproto_rpc_sender.h"
|
||||
#include "mtproto/mtproto_dc_options.h"
|
||||
#include "mtproto/connection_abstract.h"
|
||||
#include "platform/platform_specific.h"
|
||||
#include "base/openssl_help.h"
|
||||
#include "base/qthelp_url.h"
|
||||
#include "base/unixtime.h"
|
||||
@@ -633,13 +634,24 @@ void SessionPrivate::tryToSend() {
|
||||
: _instance->systemVersion();
|
||||
#if defined OS_MAC_STORE
|
||||
const auto appVersion = QString::fromLatin1(AppVersionStr)
|
||||
+ " mac store";
|
||||
+ " Mac App Store";
|
||||
#elif defined OS_WIN_STORE // OS_MAC_STORE
|
||||
const auto appVersion = QString::fromLatin1(AppVersionStr)
|
||||
+ " win store";
|
||||
#else // OS_MAC_STORE || OS_WIN_STORE
|
||||
+ " Microsoft Store";
|
||||
#elif defined Q_OS_UNIX && !defined Q_OS_MAC // OS_MAC_STORE || OS_WIN_STORE
|
||||
const auto appVersion = [] {
|
||||
if (Platform::InFlatpak()) {
|
||||
return QString::fromLatin1(AppVersionStr)
|
||||
+ " Flatpak";
|
||||
} else if (Platform::InSnap()) {
|
||||
return QString::fromLatin1(AppVersionStr)
|
||||
+ " Snap";
|
||||
}
|
||||
return QString::fromLatin1(AppVersionStr);
|
||||
}();
|
||||
#else // OS_MAC_STORE || OS_WIN_STORE || (defined Q_OS_UNIX && !defined Q_OS_MAC)
|
||||
const auto appVersion = QString::fromLatin1(AppVersionStr);
|
||||
#endif // OS_MAC_STORE || OS_WIN_STORE
|
||||
#endif // OS_MAC_STORE || OS_WIN_STORE || (defined Q_OS_UNIX && !defined Q_OS_MAC)
|
||||
const auto proxyType = _options->proxy.type;
|
||||
const auto mtprotoProxy = (proxyType == ProxyData::Type::Mtproto);
|
||||
const auto clientProxyFields = mtprotoProxy
|
||||
|
||||
@@ -113,14 +113,15 @@ bool UseNative(Type type = Type::ReadFile) {
|
||||
// or if QT_QPA_PLATFORMTHEME=(gtk2|gtk3)
|
||||
// or if portals are used and operation is to open folder
|
||||
// and portal doesn't support folder choosing
|
||||
const auto neededForPortal = UseXDGDesktopPortal()
|
||||
&& type == Type::ReadFolder
|
||||
const auto neededForPortal = (type == Type::ReadFolder)
|
||||
&& !CanOpenDirectoryWithPortal();
|
||||
|
||||
const auto neededNonForced = DesktopEnvironment::IsGtkBased()
|
||||
|| neededForPortal;
|
||||
|| (UseXDGDesktopPortal() && neededForPortal);
|
||||
|
||||
const auto excludeNonForced = InFlatpak() || InSnap();
|
||||
const auto excludeNonForced = InFlatpak()
|
||||
|| InSnap()
|
||||
|| (UseXDGDesktopPortal() && !neededForPortal);
|
||||
|
||||
return IsGtkIntegrationForced()
|
||||
|| (neededNonForced && !excludeNonForced);
|
||||
|
||||
@@ -58,7 +58,6 @@ namespace Platform {
|
||||
namespace {
|
||||
|
||||
constexpr auto kDisableTrayCounter = "TDESKTOP_DISABLE_TRAY_COUNTER"_cs;
|
||||
constexpr auto kForcePanelIcon = "TDESKTOP_FORCE_PANEL_ICON"_cs;
|
||||
constexpr auto kPanelTrayIconName = "telegram-panel"_cs;
|
||||
constexpr auto kMutePanelTrayIconName = "telegram-mute-panel"_cs;
|
||||
constexpr auto kAttentionPanelTrayIconName = "telegram-attention-panel"_cs;
|
||||
@@ -149,8 +148,7 @@ QString GetTrayIconName(int counter, bool muted) {
|
||||
const auto iconName = GetIconName();
|
||||
const auto panelIconName = GetPanelIconName(counter, muted);
|
||||
|
||||
if (QIcon::hasThemeIcon(panelIconName)
|
||||
|| qEnvironmentVariableIsSet(kForcePanelIcon.utf8())) {
|
||||
if (QIcon::hasThemeIcon(panelIconName)) {
|
||||
return panelIconName;
|
||||
} else if (QIcon::hasThemeIcon(iconName)) {
|
||||
return iconName;
|
||||
@@ -593,9 +591,7 @@ void MainWindow::setSNITrayIcon(int counter, bool muted) {
|
||||
const auto iconName = GetTrayIconName(counter, muted);
|
||||
|
||||
if (qEnvironmentVariableIsSet(kDisableTrayCounter.utf8())
|
||||
&& !iconName.isEmpty()
|
||||
&& (!InSnap()
|
||||
|| qEnvironmentVariableIsSet(kForcePanelIcon.utf8()))) {
|
||||
&& !iconName.isEmpty()) {
|
||||
if (_sniTrayIcon->iconName() == iconName) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -132,6 +132,14 @@ bool IsXDGDesktopPortalKDEPresent() {
|
||||
return Result;
|
||||
}
|
||||
|
||||
bool IsIBusPortalPresent() {
|
||||
static const auto Result = QDBusInterface(
|
||||
qsl("org.freedesktop.portal.IBus"),
|
||||
qsl("/org/freedesktop/IBus")).isValid();
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
uint FileChooserPortalVersion() {
|
||||
static const auto Result = [&]() -> uint {
|
||||
auto message = QDBusMessage::createMethodCall(
|
||||
@@ -693,10 +701,6 @@ QString AppRuntimeDirectory() {
|
||||
runtimeDir = QDir::tempPath();
|
||||
}
|
||||
|
||||
if (runtimeDir.isEmpty()) {
|
||||
runtimeDir = qsl("/tmp/");
|
||||
}
|
||||
|
||||
if (!runtimeDir.endsWith('/')) {
|
||||
runtimeDir += '/';
|
||||
}
|
||||
@@ -1101,18 +1105,35 @@ void start() {
|
||||
LOG(("XDG Desktop Portal is not present :("));
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
|
||||
// IBus has changed its socket path several times
|
||||
// and each change should be synchronized with Qt.
|
||||
// Moreover, the last time Qt changed the path,
|
||||
// they didn't introduce a fallback to the old path
|
||||
// and made the new Qt incompatible with IBus from older distributions.
|
||||
// Since tdesktop is distributed in static binary form,
|
||||
// it makes sense to use ibus portal whenever it present
|
||||
// to ensure compatibility with the maximum range of distributions.
|
||||
if (IsIBusPortalPresent()) {
|
||||
LOG(("IBus portal is present! Using it."));
|
||||
qputenv("IBUS_USE_PORTAL", "1");
|
||||
}
|
||||
#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION
|
||||
}
|
||||
|
||||
void finish() {
|
||||
}
|
||||
|
||||
void InstallLauncher() {
|
||||
void InstallLauncher(bool force) {
|
||||
static const auto DisabledByEnv = qEnvironmentVariableIsSet(
|
||||
"TDESKTOP_DISABLE_DESKTOP_FILE_GENERATION");
|
||||
|
||||
// don't update desktop file for alpha version or if updater is disabled
|
||||
if (cAlphaVersion() || Core::UpdaterDisabled() || DisabledByEnv)
|
||||
if ((cAlphaVersion() || Core::UpdaterDisabled() || DisabledByEnv)
|
||||
&& !force) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto applicationsPath = QStandardPaths::writableLocation(
|
||||
QStandardPaths::ApplicationsLocation) + '/';
|
||||
|
||||
@@ -41,7 +41,7 @@ QString GetIconName();
|
||||
inline void IgnoreApplicationActivationRightNow() {
|
||||
}
|
||||
|
||||
void InstallLauncher();
|
||||
void InstallLauncher(bool force = false);
|
||||
|
||||
} // namespace Platform
|
||||
|
||||
|
||||
@@ -157,11 +157,13 @@ ApplicationDelegate *_sharedDelegate = nil;
|
||||
}
|
||||
|
||||
- (void) receiveWakeNote:(NSNotification*)aNotification {
|
||||
if (Core::IsAppLaunched()) {
|
||||
Core::App().checkLocalTime();
|
||||
if (!Core::IsAppLaunched()) {
|
||||
return;
|
||||
}
|
||||
Core::App().checkLocalTime();
|
||||
|
||||
LOG(("Audio Info: -receiveWakeNote: received, scheduling detach from audio device"));
|
||||
LOG(("Audio Info: "
|
||||
"-receiveWakeNote: received, scheduling detach from audio device"));
|
||||
Media::Audio::ScheduleDetachFromDeviceSafe();
|
||||
}
|
||||
|
||||
|
||||
@@ -136,14 +136,6 @@ auto GenerateCodes() {
|
||||
window->showSettings(Settings::Type::Folders);
|
||||
}
|
||||
});
|
||||
codes.emplace(qsl("autodark"), [](SessionController *window) {
|
||||
auto text = Core::App().settings().systemDarkModeEnabled() ? qsl("Disable system dark mode?") : qsl("Enable system dark mode?");
|
||||
Ui::show(Box<ConfirmBox>(text, [=] {
|
||||
Core::App().settings().setSystemDarkModeEnabled(!Core::App().settings().systemDarkModeEnabled());
|
||||
Core::App().saveSettingsDelayed();
|
||||
Ui::hideLayer();
|
||||
}));
|
||||
});
|
||||
codes.emplace(qsl("registertg"), [](SessionController *window) {
|
||||
Platform::RegisterCustomScheme(true);
|
||||
Ui::Toast::Show("Forced custom scheme register.");
|
||||
@@ -165,6 +157,13 @@ auto GenerateCodes() {
|
||||
});
|
||||
#endif // Q_OS_WIN || Q_OS_MAC
|
||||
|
||||
#if defined Q_OS_UNIX && !defined Q_OS_MAC
|
||||
codes.emplace(qsl("installauncher"), [](SessionController *window) {
|
||||
Platform::InstallLauncher(true);
|
||||
Ui::Toast::Show("Forced launcher installation.");
|
||||
});
|
||||
#endif // Q_OS_UNIX && !Q_OS_MAC
|
||||
|
||||
auto audioFilters = qsl("Audio files (*.wav *.mp3);;") + FileDialog::AllFilesFilter();
|
||||
auto audioKeys = {
|
||||
qsl("msg_incoming"),
|
||||
|
||||
@@ -154,11 +154,15 @@ bool PreparedList::canAddCaption(bool sendingAlbum) const {
|
||||
files,
|
||||
PreparedFile::Type::File,
|
||||
&PreparedFile::type);
|
||||
const auto hasMusic = ranges::contains(
|
||||
files,
|
||||
PreparedFile::Type::Music,
|
||||
&PreparedFile::type);
|
||||
const auto hasNotGrouped = ranges::contains(
|
||||
files,
|
||||
PreparedFile::Type::None,
|
||||
&PreparedFile::type);
|
||||
return !hasFiles && !hasNotGrouped;
|
||||
return !hasFiles && !hasMusic && !hasNotGrouped;
|
||||
}
|
||||
|
||||
bool PreparedList::hasGroupOption(bool slowmode) const {
|
||||
|
||||
@@ -126,11 +126,18 @@ void SingleMediaPreview::preparePreview(
|
||||
_previewWidth = qMax(preview.width(), kMinPreviewWidth);
|
||||
}
|
||||
auto maxthumbh = qMin(qRound(1.5 * _previewWidth), st::confirmMaxHeight);
|
||||
const auto minthumbh = st::sendBoxAlbumGroupHeight
|
||||
+ st::sendBoxAlbumGroupSkipTop * 2;
|
||||
_previewHeight = qRound(originalHeight * float64(_previewWidth) / originalWidth);
|
||||
if (_previewHeight > maxthumbh) {
|
||||
_previewWidth = qRound(_previewWidth * float64(maxthumbh) / _previewHeight);
|
||||
accumulate_max(_previewWidth, kMinPreviewWidth);
|
||||
_previewHeight = maxthumbh;
|
||||
} else if (_previewHeight < minthumbh) {
|
||||
_previewWidth = qRound(_previewWidth * float64(minthumbh)
|
||||
/ _previewHeight);
|
||||
accumulate_max(_previewWidth, kMinPreviewWidth);
|
||||
_previewHeight = minthumbh;
|
||||
}
|
||||
_previewLeft = (st::boxWideWidth - _previewWidth) / 2;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
AppVersion 2004005
|
||||
AppVersion 2004007
|
||||
AppVersionStrMajor 2.4
|
||||
AppVersionStrSmall 2.4.5
|
||||
AppVersionStr 2.4.5
|
||||
AppVersionStrSmall 2.4.7
|
||||
AppVersionStr 2.4.7
|
||||
BetaChannel 0
|
||||
AlphaVersion 0
|
||||
AppVersionOriginal 2.4.5
|
||||
AppVersionOriginal 2.4.7
|
||||
|
||||
Submodule Telegram/lib_base updated: df430bc4a2...ffe7a4681a
@@ -1,3 +1,15 @@
|
||||
2.4.7 (05.11.20)
|
||||
|
||||
- Fix playback display in albums of music files.
|
||||
- Several crash fixes.
|
||||
|
||||
2.4.6 (02.11.20)
|
||||
|
||||
- Fix image compression option when sending files with drag-n-drop.
|
||||
- Fix caption text selection in media albums.
|
||||
- Fix drafts display in personal chats in the chats list.
|
||||
- Bug fixes and other minor improvements.
|
||||
|
||||
2.4.5 (30.10.20)
|
||||
|
||||
- Pin several messages in any chat, including one-on-one chats.
|
||||
|
||||
@@ -105,7 +105,8 @@ Open **x86 Native Tools Command Prompt for VS 2019.bat**, go to ***BuildPath***
|
||||
-A Win32 ^
|
||||
-DWITH_JPEG8=ON ^
|
||||
-DPNG_SUPPORTED=OFF
|
||||
cmake --build .
|
||||
cmake --build . --config Debug
|
||||
cmake --build . --config Release
|
||||
cd ..
|
||||
|
||||
git clone https://github.com/telegramdesktop/openal-soft.git
|
||||
|
||||
@@ -18,8 +18,6 @@ apps:
|
||||
common-id: org.telegram.desktop
|
||||
desktop: usr/share/applications/telegram-desktop_telegram-desktop.desktop
|
||||
environment:
|
||||
# Use GTK3 cursor theme, icon theme and open/save file dialogs.
|
||||
QT_QPA_PLATFORMTHEME: gtk3
|
||||
GTK_USE_PORTAL: 1
|
||||
plugs:
|
||||
- alsa
|
||||
@@ -119,7 +117,10 @@ parts:
|
||||
|
||||
snapcraftctl set-version "$version"
|
||||
|
||||
sed -i 's|^Icon=.*|Icon=/usr/share/icons/hicolor/512x512/apps/telegram.png|g' lib/xdg/telegramdesktop.desktop
|
||||
sed -i 's|^Icon=.*|Icon=${SNAP}/meta/gui/icon.png|g' lib/xdg/telegramdesktop.desktop
|
||||
override-build: |
|
||||
snapcraftctl build
|
||||
rm -rf "$SNAPCRAFT_PART_INSTALL/usr/share/icons"
|
||||
stage:
|
||||
- -./usr/lib/$SNAPCRAFT_ARCH_TRIPLET/libjpeg.so.8.2.2
|
||||
after:
|
||||
@@ -258,6 +259,7 @@ parts:
|
||||
webrtc:
|
||||
source: https://github.com/desktop-app/tg_owt.git
|
||||
source-depth: 1
|
||||
source-commit: 12f4a27f2f02f9dd40f9891d8ec6e58bc1ff5263
|
||||
plugin: cmake
|
||||
build-packages:
|
||||
- yasm
|
||||
@@ -269,6 +271,7 @@ parts:
|
||||
cmake-parameters:
|
||||
- -DCMAKE_BUILD_TYPE=Release
|
||||
- -DCMAKE_INSTALL_PREFIX=/usr
|
||||
- -DBUILD_SHARED_LIBS=OFF
|
||||
- -DJPEG_LIBRARY_RELEASE=$SNAPCRAFT_STAGE/usr/lib/$SNAPCRAFT_ARCH_TRIPLET/libjpeg.so
|
||||
- -DJPEG_INCLUDE_DIR=$SNAPCRAFT_STAGE/usr/include
|
||||
prime: [-./*]
|
||||
|
||||
Reference in New Issue
Block a user