Compare commits

...

18 Commits

Author SHA1 Message Date
John Preston
26e023058c Version 1.2.6.
- Grouped Photos. Group media into an album when sharing multiple photos and videos.
Choose the exact order of media you send.
2017-12-30 13:13:48 +03:00
John Preston
6236590ca4 Fix SendFilesWay radiobuttons when adding media.
Also add /LTCG flag for static libraries Release builds on Windows.
2017-12-30 00:06:43 +03:00
John Preston
ea51f976f2 Alpha version 1.2.5: Workaround GCC 7.2 ICE. 2017-12-29 21:47:49 +03:00
John Preston
719f3428ec Alpha version 1.2.5:
- When viewing a photo from an album, you'll see other pictures
from the same group as thumbnails in the lower part of the screen.
- When composing an album paste additional media from the clipboard.
- Bug fixes and other minor improvements.
2017-12-29 21:21:57 +03:00
John Preston
2df4d19474 Move changelogs from ApiWrap to a separate module. 2017-12-29 21:17:07 +03:00
John Preston
2a409e3734 Add files from clipboard to composed album.
Fixes #4243.
2017-12-29 20:02:23 +03:00
John Preston
0171a4e874 Handle click on group thumb item in MediaView. 2017-12-29 17:58:53 +03:00
John Preston
59e5ffe743 Don't insert mime text in field for url list.
Fixes #4241.
2017-12-29 17:58:32 +03:00
John Preston
2bcbb5a5be Display group / userpic thumbnails in MediaView. 2017-12-29 16:44:36 +03:00
John Preston
5b4694a4eb Move text options constant to a separate module.
Also start MediaView group thumbs code.
2017-12-28 16:06:06 +03:00
John Preston
54d6673d0b Display photos and videos together in MediaView. 2017-12-28 13:12:07 +03:00
John Preston
e07a7a4b4c Improve phrases. No Restricted Users in channels info. 2017-12-27 22:44:04 +03:00
John Preston
f2d11e7432 Fix video grouped thumb on Retina displays. 2017-12-27 22:16:26 +03:00
John Preston
1a115cc7e5 Fix file upload progress display.
Regression was introduced in 5d18d7c813.
2017-12-27 21:57:37 +03:00
John Preston
a00941f7ce Update crl and libtgvoip submodules. 2017-12-27 21:49:08 +03:00
John Preston
634d21e486 Fix animation in album reordering. 2017-12-27 14:00:32 +03:00
John Preston
95d8742e3c Fix round corners on Retina displays. 2017-12-27 13:08:18 +03:00
John Preston
bd8dee0972 Fix crash in audio player hiding. 2017-12-27 10:18:09 +03:00
66 changed files with 2242 additions and 499 deletions

View File

@@ -137,8 +137,9 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_error_cant_add_admin_unban" = "Sorry, you can't add this user as an admin because they are in the blacklist and you can't unban them.";
"lng_error_cant_ban_admin" = "Sorry, you can't ban this user because they are an admin in this group and you are not allowed to demote them.";
"lng_sure_add_admin_invite" = "This user is not a member of this group. Add them to the group and promote them to admin?";
"lng_sure_add_admin_unban" = "This user is currently restricted or banned in this group. Are you sure you want to unban and promote them?";
"lng_sure_ban_admin" = "This user is an admin in this group. Are you sure you want to go ahead and restrict them?";
"lng_sure_add_admin_invite_channel" = "This user is not a subscriber of this channel. Add them to the channel and promote them to admin?";
"lng_sure_add_admin_unban" = "This user is currently restricted or banned. Are you sure you want to unban and promote them?";
"lng_sure_ban_admin" = "This user is an admin. Are you sure you want to go ahead and restrict them?";
"lng_sure_ban_user_group" = "Ban {user} in the group?";
"lng_sure_enable_socks" = "Are you sure you want to enable this proxy?\n\nServer: {server}\nPort: {port}\n\nYou can change your proxy server later in the Settings (Connection Type).";
"lng_sure_enable" = "Enable";
@@ -577,8 +578,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_profile_kick" = "Remove";
"lng_profile_sure_kick" = "Remove {user} from the group?";
"lng_profile_sure_kick_channel" = "Remove {user} from the channel?";
"lng_profile_sure_remove_admin" = "Remove {user} from group admins?";
"lng_profile_sure_remove_admin_channel" = "Remove {user} from channel admins?";
"lng_profile_sure_remove_admin" = "Remove {user} from admins?";
"lng_profile_loading" = "Loading...";
"lng_profile_photos#one" = "{count} photo";
"lng_profile_photos#other" = "{count} photos";

View File

@@ -9,7 +9,7 @@
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
ProcessorArchitecture="ARCHITECTURE"
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
Version="1.2.4.0" />
Version="1.2.6.0" />
<Properties>
<DisplayName>Telegram Desktop</DisplayName>
<PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName>

View File

@@ -34,8 +34,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,2,4,0
PRODUCTVERSION 1,2,4,0
FILEVERSION 1,2,6,0
PRODUCTVERSION 1,2,6,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -52,10 +52,10 @@ BEGIN
BEGIN
VALUE "CompanyName", "Telegram Messenger LLP"
VALUE "FileDescription", "Telegram Desktop"
VALUE "FileVersion", "1.2.4.0"
VALUE "FileVersion", "1.2.6.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2017"
VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "1.2.4.0"
VALUE "ProductVersion", "1.2.6.0"
END
END
BLOCK "VarFileInfo"

View File

@@ -25,8 +25,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,2,4,0
PRODUCTVERSION 1,2,4,0
FILEVERSION 1,2,6,0
PRODUCTVERSION 1,2,6,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -43,10 +43,10 @@ BEGIN
BEGIN
VALUE "CompanyName", "Telegram Messenger LLP"
VALUE "FileDescription", "Telegram Desktop Updater"
VALUE "FileVersion", "1.2.4.0"
VALUE "FileVersion", "1.2.6.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2017"
VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "1.2.4.0"
VALUE "ProductVersion", "1.2.6.0"
END
END
BLOCK "VarFileInfo"

View File

@@ -38,7 +38,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "storage/localstorage.h"
#include "auth_session.h"
#include "boxes/confirm_box.h"
#include "window/themes/window_theme.h"
#include "window/notifications_manager.h"
#include "chat_helpers/message_field.h"
#include "chat_helpers/stickers.h"
@@ -144,64 +143,14 @@ ApiWrap::ApiWrap(not_null<AuthSession*> session)
, _fileLoader(std::make_unique<TaskQueue>(kFileLoaderQueueStopTimeout)) {
}
void ApiWrap::start() {
Window::Theme::Background()->start();
requestAppChangelogs();
}
void ApiWrap::requestAppChangelogs() {
auto oldAppVersion = Local::oldMapVersion();
if (oldAppVersion > 0 && oldAppVersion < AppVersion) {
_changelogSubscription = subscribe(_session->data().moreChatsLoaded(), [this, oldAppVersion] {
auto oldVersionString = qsl("%1.%2.%3").arg(oldAppVersion / 1000000).arg((oldAppVersion % 1000000) / 1000).arg(oldAppVersion % 1000);
request(MTPhelp_GetAppChangelog(MTP_string(oldVersionString))).done([this, oldAppVersion](const MTPUpdates &result) {
applyUpdates(result);
auto resultEmpty = true;
switch (result.type()) {
case mtpc_updateShortMessage:
case mtpc_updateShortChatMessage:
case mtpc_updateShort: resultEmpty = false; break;
case mtpc_updatesCombined: resultEmpty = result.c_updatesCombined().vupdates.v.isEmpty(); break;
case mtpc_updates: resultEmpty = result.c_updates().vupdates.v.isEmpty(); break;
case mtpc_updatesTooLong:
case mtpc_updateShortSentMessage: LOG(("API Error: Bad updates type in app changelog.")); break;
}
if (resultEmpty) {
addLocalChangelogs(oldAppVersion);
}
}).send();
unsubscribe(base::take(_changelogSubscription));
});
}
}
void ApiWrap::addLocalChangelogs(int oldAppVersion) {
auto addedSome = false;
auto addLocalChangelog = [this, &addedSome](const QString &text) {
auto textWithEntities = TextWithEntities { text };
TextUtilities::ParseEntities(textWithEntities, TextParseLinks);
App::wnd()->serviceNotification(textWithEntities, MTP_messageMediaEmpty(), unixtime());
addedSome = true;
};
if (cAlphaVersion() || cBetaVersion()) {
auto addLocalAlphaChangelog = [this, oldAppVersion, addLocalChangelog](int changeVersion, const char *changes) {
if (oldAppVersion < changeVersion) {
auto changeVersionString = QString::number(changeVersion / 1000000) + '.' + QString::number((changeVersion % 1000000) / 1000) + ((changeVersion % 1000) ? ('.' + QString::number(changeVersion % 1000)) : QString());
auto text = qsl("New in version %1:\n\n").arg(changeVersionString) + QString::fromUtf8(changes).trimmed();
addLocalChangelog(text);
}
};
addLocalAlphaChangelog(1001024, "\xE2\x80\x94 Radically improved navigation. New side panel on the right with quick access to shared media and group members.\n\xE2\x80\x94 Pinned Messages. If you are a channel admin, pin messages to focus your subscribers\xE2\x80\x99 attention on important announcements.\n\xE2\x80\x94 Also supported clearing history in supergroups and added a host of minor improvements.");
addLocalAlphaChangelog(1001026, "\xE2\x80\x94 Admin badges in supergroup messages.\n\xE2\x80\x94 Fix crashing on launch in OS X 10.6.\n\xE2\x80\x94 Bug fixes and other minor improvements.");
addLocalAlphaChangelog(1001027, "\xE2\x80\x94 Saved Messages. Bookmark messages by forwarding them to \xE2\x80\x9C""Saved Messages\xE2\x80\x9D. Access them from the Chats list or from the side menu.");
addLocalAlphaChangelog(1002002, "\xE2\x80\x94 Grouped photos and videos are displayed as albums.");
addLocalAlphaChangelog(1002004, "\xE2\x80\x94 Group media into an album when sharing multiple photos and videos.\n\xE2\x80\x94 Bug fixes and other minor improvements.");
}
if (!addedSome) {
auto text = lng_new_version_wrap(lt_version, str_const_toString(AppVersionStr), lt_changes, lang(lng_new_version_minor), lt_link, qsl("https://desktop.telegram.org/changelog")).trimmed();
addLocalChangelog(text);
}
void ApiWrap::requestChangelog(
const QString &sinceVersion,
base::lambda<void(const MTPUpdates &result)> callback) {
request(MTPhelp_GetAppChangelog(
MTP_string(sinceVersion)
)).done(
callback
).send();
}
void ApiWrap::applyUpdates(

View File

@@ -56,7 +56,6 @@ class ApiWrap : private MTP::Sender, private base::Subscriber {
public:
ApiWrap(not_null<AuthSession*> session);
void start();
void applyUpdates(const MTPUpdates &updates, uint64 sentMessageRandomId = 0);
using RequestMessageDataCallback = base::lambda<void(ChannelData*, MsgId)>;
@@ -70,6 +69,10 @@ public:
void requestAdmins(not_null<ChannelData*> channel);
void requestParticipantsCountDelayed(not_null<ChannelData*> channel);
void requestChangelog(
const QString &sinceVersion,
base::lambda<void(const MTPUpdates &result)> callback);
void requestChannelMembersForAdd(
not_null<ChannelData*> channel,
base::lambda<void(const MTPchannels_ChannelParticipants&)> callback);
@@ -234,8 +237,6 @@ private:
using MessageDataRequests = QMap<MsgId, MessageDataRequest>;
using SharedMediaType = Storage::SharedMediaType;
void requestAppChangelogs();
void addLocalChangelogs(int oldAppVersion);
void updatesReceived(const MTPUpdates &updates);
void checkQuitPreventFinished();
@@ -344,7 +345,6 @@ private:
uint64 randomId);
not_null<AuthSession*> _session;
mtpRequestId _changelogSubscription = 0;
MessageDataRequests _messageDataRequests;
QMap<ChannelData*, MessageDataRequests> _channelMessageDataRequests;

View File

@@ -22,6 +22,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "apiwrap.h"
#include "messenger.h"
#include "core/changelogs.h"
#include "storage/file_download.h"
#include "storage/file_upload.h"
#include "storage/localstorage.h"
@@ -29,6 +30,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "storage/serialize_common.h"
#include "history/history_item_components.h"
#include "window/notifications_manager.h"
#include "window/themes/window_theme.h"
#include "platform/platform_specific.h"
#include "calls/calls_instance.h"
#include "window/section_widget.h"
@@ -408,8 +410,10 @@ AuthSession::AuthSession(UserId userId)
, _downloader(std::make_unique<Storage::Downloader>())
, _uploader(std::make_unique<Storage::Uploader>())
, _storage(std::make_unique<Storage::Facade>())
, _notifications(std::make_unique<Window::Notifications::System>(this)) {
, _notifications(std::make_unique<Window::Notifications::System>(this))
, _changelogs(Core::Changelogs::Create(this)) {
Expects(_userId != 0);
_saveDataTimer.setCallback([this] {
Local::writeUserSettings();
});
@@ -417,7 +421,7 @@ AuthSession::AuthSession(UserId userId)
_shouldLockAt = 0;
notifications().updateAll();
});
_api->start();
Window::Theme::Background()->start();
}
bool AuthSession::Exists() {

View File

@@ -50,6 +50,10 @@ namespace ChatHelpers {
enum class SelectorTab;
} // namespace ChatHelpers
namespace Core {
class Changelogs;
} // namespace Core
class AuthSessionData final {
public:
base::Variable<bool> &contactsLoaded() {
@@ -414,5 +418,6 @@ private:
const std::unique_ptr<Storage::Uploader> _uploader;
const std::unique_ptr<Storage::Facade> _storage;
const std::unique_ptr<Window::Notifications::System> _notifications;
const std::unique_ptr<Core::Changelogs> _changelogs;
};

View File

@@ -36,6 +36,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "ui/widgets/labels.h"
#include "ui/toast/toast.h"
#include "ui/special_buttons.h"
#include "ui/text_options.h"
#include "mainwidget.h"
#include "mainwindow.h"
#include "apiwrap.h"
@@ -1378,8 +1379,15 @@ RevokePublicLinkBox::Inner::Inner(QWidget *parent, base::lambda<void()> revokeCa
auto row = ChatRow(peer);
row.peer = peer;
row.name.setText(st::contactsNameStyle, peer->name, _textNameOptions);
row.status.setText(st::defaultTextStyle, Messenger::Instance().createInternalLink(textcmdLink(1, peer->userName())), _textDlgOptions);
row.name.setText(
st::contactsNameStyle,
peer->name,
Ui::NameTextOptions());
row.status.setText(
st::defaultTextStyle,
Messenger::Instance().createInternalLink(
textcmdLink(1, peer->userName())),
Ui::DialogTextOptions());
_rows.push_back(std::move(row));
}
}

View File

@@ -21,6 +21,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "boxes/edit_caption_box.h"
#include "ui/widgets/input_fields.h"
#include "ui/text_options.h"
#include "media/media_clip_reader.h"
#include "history/history_media_types.h"
#include "lang/lang_keys.h"
@@ -106,7 +107,7 @@ EditCaptionBox::EditCaptionBox(
_name.setText(
st::semiboldTextStyle,
nameString,
_textNameOptions);
Ui::NameTextOptions());
_status = formatSizeText(doc->size);
_statusw = qMax(
_name.maxWidth(),

View File

@@ -24,10 +24,11 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "ui/widgets/checkbox.h"
#include "ui/widgets/labels.h"
#include "ui/widgets/buttons.h"
#include "styles/style_boxes.h"
#include "ui/text_options.h"
#include "ui/special_buttons.h"
#include "boxes/calendar_box.h"
#include "data/data_peer_values.h"
#include "styles/style_boxes.h"
namespace {
@@ -128,7 +129,10 @@ EditParticipantBox::Inner::Inner(
st::rightsPhotoButton)
, _hasAdminRights(hasAdminRights) {
_userPhoto->setPointerCursor(false);
_userName.setText(st::rightsNameStyle, App::peerName(_user), _textNameOptions);
_userName.setText(
st::rightsNameStyle,
App::peerName(_user),
Ui::NameTextOptions());
}
void EditParticipantBox::Inner::removeControl(QPointer<TWidget> widget) {

View File

@@ -34,6 +34,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "ui/effects/ripple_animation.h"
#include "ui/empty_userpic.h"
#include "ui/wrap/slide_wrap.h"
#include "ui/text_options.h"
#include "lang/lang_keys.h"
#include "observer_peer.h"
#include "storage/file_download.h"
@@ -391,7 +392,7 @@ void PeerListRow::refreshName(const style::PeerListItem &st) {
const auto text = _isSavedMessagesChat
? lang(lng_saved_messages)
: peer()->name;
_name.setText(st.nameStyle, text, _textNameOptions);
_name.setText(st.nameStyle, text, Ui::NameTextOptions());
}
PeerListRow::~PeerListRow() = default;
@@ -516,7 +517,7 @@ void PeerListRow::paintDisabledCheckUserpic(
}
void PeerListRow::setStatusText(const QString &text) {
_status.setText(st::defaultTextStyle, text, _textNameOptions);
_status.setText(st::defaultTextStyle, text, Ui::NameTextOptions());
}
float64 PeerListRow::checkedRatio() {

View File

@@ -168,18 +168,20 @@ void FillManageBox(
st::infoIconAdministrators);
}
if (channel->canViewBanned()) {
AddButtonWithCount(
content,
Lang::Viewer(lng_manage_peer_restricted_users),
Info::Profile::RestrictedCountValue(channel)
| ToPositiveNumberString(),
[=] {
ParticipantsBoxController::Start(
controller,
channel,
ParticipantsBoxController::Role::Restricted);
},
st::infoIconRestrictedUsers);
if (channel->isMegagroup()) {
AddButtonWithCount(
content,
Lang::Viewer(lng_manage_peer_restricted_users),
Info::Profile::RestrictedCountValue(channel)
| ToPositiveNumberString(),
[=] {
ParticipantsBoxController::Start(
controller,
channel,
ParticipantsBoxController::Role::Restricted);
},
st::infoIconRestrictedUsers);
}
AddButtonWithCount(
content,
Lang::Viewer(lng_manage_peer_banned_users),

View File

@@ -32,6 +32,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "ui/widgets/scroll_area.h"
#include "ui/wrap/fade_wrap.h"
#include "ui/grouped_layout.h"
#include "ui/text_options.h"
#include "media/media_clip_reader.h"
#include "window/window_controller.h"
#include "styles/style_history.h"
@@ -115,6 +116,8 @@ public:
const Ui::GroupMediaLayout &layout);
void moveToLayout(const Ui::GroupMediaLayout &layout);
void animateLayoutToInitial();
void resetLayoutAnimation();
int photoHeight() const;
@@ -143,7 +146,7 @@ private:
void drawSimpleFrame(Painter &p, QRect to, QSize size) const;
Ui::GroupMediaLayout _layout;
QRect _fromGeometry;
base::optional<QRect> _animateFromGeometry;
const QImage _fullPreview;
const int _shrinkSize = 0;
QPixmap _albumImage;
@@ -234,11 +237,19 @@ AlbumThumb::AlbumThumb(
_statusWidth = st::normalFont->width(_status);
}
void AlbumThumb::moveToLayout(const Ui::GroupMediaLayout &layout) {
_fromGeometry = countRealGeometry();
_layout = layout;
void AlbumThumb::resetLayoutAnimation() {
_animateFromGeometry = base::none;
}
void AlbumThumb::animateLayoutToInitial() {
_animateFromGeometry = countRealGeometry();
_suggestedMove = 0.;
_albumPosition = QPoint(0, 0);
}
void AlbumThumb::moveToLayout(const Ui::GroupMediaLayout &layout) {
animateLayoutToInitial();
_layout = layout;
const auto width = _layout.geometry.width();
const auto height = _layout.geometry.height();
@@ -323,12 +334,11 @@ void AlbumThumb::paintInAlbum(
void AlbumThumb::prepareCache(QSize size, int shrink) {
const auto width = std::max(
_layout.geometry.width(),
_fromGeometry.width());
_animateFromGeometry ? _animateFromGeometry->width() : 0);
const auto height = std::max(
_layout.geometry.height(),
_fromGeometry.height());
_animateFromGeometry ? _animateFromGeometry->height() : 0);
const auto cacheSize = QSize(width, height) * cIntRetinaFactor();
const auto initial = QRect(QPoint(), size);
if (_albumCache.width() < cacheSize.width()
|| _albumCache.height() < cacheSize.height()) {
@@ -337,7 +347,7 @@ void AlbumThumb::prepareCache(QSize size, int shrink) {
_albumCache.fill(Qt::transparent);
{
Painter p(&_albumCache);
const auto to = initial.marginsRemoved(
const auto to = QRect(QPoint(), size).marginsRemoved(
{ shrink, shrink, shrink, shrink }
);
drawSimpleFrame(p, to, size);
@@ -346,7 +356,7 @@ void AlbumThumb::prepareCache(QSize size, int shrink) {
_albumCache,
ImageRoundRadius::Large,
_albumCorners,
initial);
QRect(QPoint(), size * cIntRetinaFactor()));
}
void AlbumThumb::drawSimpleFrame(Painter &p, QRect to, QSize size) const {
@@ -543,12 +553,12 @@ QRect AlbumThumb::countRealGeometry() const {
QRect AlbumThumb::countCurrentGeometry(float64 progress) const {
const auto now = countRealGeometry();
if (progress < 1.) {
if (_animateFromGeometry && progress < 1.) {
return {
anim::interpolate(_fromGeometry.x(), now.x(), progress),
anim::interpolate(_fromGeometry.y(), now.y(), progress),
anim::interpolate(_fromGeometry.width(), now.width(), progress),
anim::interpolate(_fromGeometry.height(), now.height(), progress)
anim::interpolate(_animateFromGeometry->x(), now.x(), progress),
anim::interpolate(_animateFromGeometry->y(), now.y(), progress),
anim::interpolate(_animateFromGeometry->width(), now.width(), progress),
anim::interpolate(_animateFromGeometry->height(), now.height(), progress)
};
}
return now;
@@ -787,7 +797,10 @@ void SingleFilePreview::preparePreview(const Storage::PreparedFile &file) {
const auto filepath = file.path;
if (filepath.isEmpty()) {
auto filename = filedialogDefaultName(qsl("image"), qsl(".png"), QString(), true);
_nameText.setText(st::semiboldTextStyle, filename, _textNameOptions);
_nameText.setText(
st::semiboldTextStyle,
filename,
Ui::NameTextOptions());
_statusText = qsl("%1x%2").arg(preview.width()).arg(preview.height());
_statusWidth = qMax(_nameText.maxWidth(), st::normalFont->width(_statusText));
_fileIsImage = true;
@@ -814,7 +827,7 @@ void SingleFilePreview::preparePreview(const Storage::PreparedFile &file) {
_nameText.setText(
st::semiboldTextStyle,
nameString,
_textNameOptions);
Ui::NameTextOptions());
_statusText = formatSizeText(fileinfo.size());
_statusWidth = qMax(
_nameText.maxWidth(),
@@ -1113,7 +1126,11 @@ void SendFilesBox::AlbumPreview::finishDrag() {
updateSizeAnimated(layout);
} else {
_draggedThumb->moveInAlbum(QPoint());
for (const auto &thumb : _thumbs) {
thumb->resetLayoutAnimation();
}
_draggedThumb->animateLayoutToInitial();
_finishDragAnimation.start([=] { update(); }, 0., 1., kDragDuration);
}
}
@@ -1312,8 +1329,8 @@ SendFilesBox::SendFilesBox(
Storage::PreparedList &&list,
CompressConfirm compressed)
: _list(std::move(list))
, _compressConfirm(compressed)
, _caption(this, st::confirmCaptionArea, FieldPlaceholder(_list)) {
, _compressConfirmInitial(compressed)
, _compressConfirm(compressed) {
}
void SendFilesBox::initPreview(rpl::producer<int> desiredPreviewHeight) {
@@ -1381,7 +1398,7 @@ void SendFilesBox::setupShadows(
const auto topShadow = Ui::CreateChild<Ui::FadeShadow>(this);
const auto bottomShadow = Ui::CreateChild<Ui::FadeShadow>(this);
wrap->geometryValue(
) | rpl::start_with_next([=](const QRect &geometry) {
) | rpl::start_with_next_done([=](const QRect &geometry) {
topShadow->resizeToWidth(geometry.width());
topShadow->move(
geometry.x(),
@@ -1390,7 +1407,11 @@ void SendFilesBox::setupShadows(
bottomShadow->move(
geometry.x(),
geometry.y() + geometry.height() - st::lineWidth);
}, [t = make_weak(topShadow), b = make_weak(bottomShadow)] {
Ui::DestroyChild(t.data());
Ui::DestroyChild(b.data());
}, topShadow->lifetime());
topShadow->toggleOn(wrap->scrollTopValue() | rpl::map(_1 > 0));
bottomShadow->toggleOn(rpl::combine(
wrap->scrollTopValue(),
@@ -1405,17 +1426,7 @@ void SendFilesBox::prepare() {
_send = addButton(langFactory(lng_send_button), [this] { send(); });
addButton(langFactory(lng_cancel), [this] { closeBox(); });
initSendWay();
if (_list.files.size() == 1) {
prepareSingleFilePreview();
} else {
if (_list.albumIsPossible) {
prepareAlbumPreview();
} else {
auto desiredPreviewHeight = rpl::single(0);
initPreview(std::move(desiredPreviewHeight));
}
}
preparePreview();
subscribe(boxClosing, [this] {
if (!_confirmed && _cancelledCallback) {
_cancelledCallback();
@@ -1424,15 +1435,7 @@ void SendFilesBox::prepare() {
}
void SendFilesBox::initSendWay() {
_albumVideosCount = _list.albumIsPossible
? ranges::count(
_list.files,
Storage::PreparedFile::AlbumType::Video,
[](const Storage::PreparedFile &file) { return file.type; })
: 0;
_albumPhotosCount = _list.albumIsPossible
? (_list.files.size() - _albumVideosCount)
: 0;
refreshAlbumMediaCount();
const auto value = [&] {
if (_compressConfirm == CompressConfirm::None) {
return SendFilesWay::Files;
@@ -1456,6 +1459,38 @@ void SendFilesBox::initSendWay() {
: SendFilesWay::Photos;
}();
_sendWay = std::make_shared<Ui::RadioenumGroup<SendFilesWay>>(value);
_sendWay->setChangedCallback([this](SendFilesWay value) {
applyAlbumOrder();
if (_albumPreview) {
_albumPreview->setSendWay(value);
}
setInnerFocus();
});
}
void SendFilesBox::refreshAlbumMediaCount() {
_albumVideosCount = _list.albumIsPossible
? ranges::count(
_list.files,
Storage::PreparedFile::AlbumType::Video,
[](const Storage::PreparedFile &file) { return file.type; })
: 0;
_albumPhotosCount = _list.albumIsPossible
? (_list.files.size() - _albumVideosCount)
: 0;
}
void SendFilesBox::preparePreview() {
if (_list.files.size() == 1) {
prepareSingleFilePreview();
} else {
if (_list.albumIsPossible) {
prepareAlbumPreview();
} else {
auto desiredPreviewHeight = rpl::single(0);
initPreview(std::move(desiredPreviewHeight));
}
}
}
void SendFilesBox::setupControls() {
@@ -1465,15 +1500,19 @@ void SendFilesBox::setupControls() {
}
void SendFilesBox::setupSendWayControls() {
_sendAlbum.destroy();
_sendPhotos.destroy();
_sendFiles.destroy();
if (_compressConfirm == CompressConfirm::None) {
return;
}
const auto addRadio = [&](
object_ptr<Ui::Radioenum<SendFilesWay>> &button,
SendFilesWay value,
const QString &text) {
object_ptr<Ui::Radioenum<SendFilesWay>> &button,
SendFilesWay value,
const QString &text) {
const auto &style = st::defaultBoxCheckbox;
button.create(this, _sendWay, value, text, style);
button->show();
};
if (_list.albumIsPossible) {
addRadio(_sendAlbum, SendFilesWay::Album, lang(lng_send_album));
@@ -1490,17 +1529,12 @@ void SendFilesBox::setupSendWayControls() {
addRadio(_sendFiles, SendFilesWay::Files, (_list.files.size() == 1)
? lang(lng_send_file)
: lng_send_files(lt_count, _list.files.size()));
_sendWay->setChangedCallback([this](SendFilesWay value) {
if (_albumPreview) {
applyAlbumOrder();
_albumPreview->setSendWay(value);
}
setInnerFocus();
});
}
void SendFilesBox::applyAlbumOrder() {
Expects(_albumPreview != nullptr);
if (!_albumPreview) {
return;
}
const auto order = _albumPreview->takeOrder();
const auto isDefault = [&] {
@@ -1519,10 +1553,12 @@ void SendFilesBox::applyAlbumOrder() {
}
void SendFilesBox::setupCaption() {
if (!_caption) {
if (_caption) {
_caption->setPlaceholder(FieldPlaceholder(_list));
return;
}
_caption.create(this, st::confirmCaptionArea, FieldPlaceholder(_list));
_caption->setMaxLength(MaxPhotoCaption);
_caption->setCtrlEnterSubmit(Ui::CtrlEnterSubmit::Both);
connect(_caption, &Ui::InputArea::resized, this, [this] {
@@ -1535,6 +1571,16 @@ void SendFilesBox::setupCaption() {
connect(_caption, &Ui::InputArea::cancelled, this, [this] {
closeBox();
});
_caption->setMimeDataHook([this](
not_null<const QMimeData*> data,
Ui::InputArea::MimeAction action) {
if (action == Ui::InputArea::MimeAction::Check) {
return canAddFiles(data);
} else if (action == Ui::InputArea::MimeAction::Insert) {
return addFiles(data);
}
Unexpected("action in MimeData hook.");
});
}
void SendFilesBox::captionResized() {
@@ -1543,6 +1589,79 @@ void SendFilesBox::captionResized() {
update();
}
bool SendFilesBox::canAddFiles(not_null<const QMimeData*> data) const {
auto files = 0;
if (data->hasUrls()) {
for (const auto &url : data->urls()) {
if (url.isLocalFile()) {
++files;
}
}
} else if (data->hasImage()) {
++files;
}
if (_list.files.size() + files > Storage::MaxAlbumItems()) {
return false;
} else if (_list.files.size() > 1 && !_albumPreview) {
return false;
} else if (_list.files.front().type
== Storage::PreparedFile::AlbumType::None) {
return false;
}
return true;
}
bool SendFilesBox::addFiles(not_null<const QMimeData*> data) {
auto list = [&] {
if (data->hasUrls()) {
return Storage::PrepareMediaList(
data->urls(),
st::sendMediaPreviewSize);
} else if (data->hasImage()) {
auto image = qvariant_cast<QImage>(data->imageData());
if (!image.isNull()) {
return Storage::PrepareMediaFromImage(
std::move(image),
QByteArray(),
st::sendMediaPreviewSize);
}
}
return Storage::PreparedList(
Storage::PreparedList::Error::EmptyFile,
QString());
}();
if (_list.files.size() + list.files.size() > Storage::MaxAlbumItems()) {
return false;
} else if (list.error != Storage::PreparedList::Error::None) {
return false;
} else if (list.files.size() != 1 && !list.albumIsPossible) {
return false;
} else if (list.files.front().type
== Storage::PreparedFile::AlbumType::None) {
return false;
} else if (_list.files.size() > 1 && !_albumPreview) {
return false;
} else if (_list.files.front().type
== Storage::PreparedFile::AlbumType::None) {
return false;
}
applyAlbumOrder();
delete base::take(_preview);
_albumPreview = nullptr;
if (_list.files.size() == 1
&& _sendWay->value() == SendFilesWay::Photos) {
_sendWay->setValue(SendFilesWay::Album);
}
_list.mergeToEnd(std::move(list));
_compressConfirm = _compressConfirmInitial;
refreshAlbumMediaCount();
preparePreview();
updateControlsGeometry();
return true;
}
void SendFilesBox::setupTitleText() {
if (_list.files.size() > 1) {
const auto onlyImages = (_compressConfirm != CompressConfirm::None)
@@ -1664,9 +1783,7 @@ void SendFilesBox::send(bool ctrlShiftEnter) {
}
}
if (_albumPreview) {
applyAlbumOrder();
}
applyAlbumOrder();
_confirmed = true;
if (_confirmedCallback) {
auto caption = _caption

View File

@@ -83,6 +83,8 @@ private:
not_null<Ui::ScrollArea*> wrap,
not_null<AlbumPreview*> content);
void refreshAlbumMediaCount();
void preparePreview();
void prepareSingleFilePreview();
void prepareAlbumPreview();
void applyAlbumOrder();
@@ -94,11 +96,15 @@ private:
void updateBoxSize();
void updateControlsGeometry();
bool canAddFiles(not_null<const QMimeData*> data) const;
bool addFiles(not_null<const QMimeData*> data);
QString _titleText;
int _titleHeight = 0;
Storage::PreparedList _list;
CompressConfirm _compressConfirmInitial = CompressConfirm::None;
CompressConfirm _compressConfirm = CompressConfirm::None;
base::lambda<void(

View File

@@ -33,10 +33,11 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "apiwrap.h"
#include "ui/toast/toast.h"
#include "ui/widgets/multi_select.h"
#include "history/history_media_types.h"
#include "history/history_message.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/scroll_area.h"
#include "ui/text_options.h"
#include "history/history_media_types.h"
#include "history/history_message.h"
#include "window/themes/window_theme.h"
#include "boxes/peer_list_box.h"
#include "auth_session.h"
@@ -377,7 +378,7 @@ void ShareBox::Inner::updateChatName(
not_null<Chat*> chat,
not_null<PeerData*> peer) {
const auto text = peer->isSelf() ? lang(lng_saved_messages) : peer->name;
chat->name.setText(st::shareNameStyle, text, _textNameOptions);
chat->name.setText(st::shareNameStyle, text, Ui::NameTextOptions());
}
void ShareBox::Inner::repaintChatAtIndex(int index) {

View File

@@ -0,0 +1,190 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#include "core/changelogs.h"
#include "storage/localstorage.h"
#include "lang/lang_keys.h"
#include "mainwindow.h"
#include "apiwrap.h"
namespace Core {
namespace {
std::map<int, const char*> AlphaLogs() {
return {
{
1001024,
"\xE2\x80\x94 Radically improved navigation. "
"New side panel on the right with quick access to "
"shared media and group members.\n"
"\xE2\x80\x94 Pinned Messages. If you are a channel admin, "
"pin messages to focus your subscribers\xE2\x80\x99 attention "
"on important announcements.\n"
"\xE2\x80\x94 Also supported clearing history in supergroups "
"and added a host of minor improvements."
},
{
1001026,
"\xE2\x80\x94 Admin badges in supergroup messages.\n"
"\xE2\x80\x94 Fix crashing on launch in OS X 10.6.\n"
"\xE2\x80\x94 Bug fixes and other minor improvements."
},
{
1001027,
"\xE2\x80\x94 Saved Messages. Bookmark messages by forwarding them "
"to \xE2\x80\x9C""Saved Messages\xE2\x80\x9D. "
"Access them from the Chats list or from the side menu."
},
{
1002002,
"\xE2\x80\x94 Grouped photos and videos are displayed as albums."
},
{
1002004,
"\xE2\x80\x94 Group media into an album "
"when sharing multiple photos and videos.\n"
"\xE2\x80\x94 Bug fixes and other minor improvements."
},
{
1002005,
"\xE2\x80\x94 When viewing a photo from an album, "
"you'll see other pictures from the same group "
"as thumbnails in the lower part of the screen.\n"
"\xE2\x80\x94 When composing an album paste "
"additional media from the clipboard.\n"
"\xE2\x80\x94 Bug fixes and other minor improvements."
},
};
}
QString FormatVersionDisplay(int version) {
return QString::number(version / 1000000)
+ '.' + QString::number((version % 1000000) / 1000)
+ ((version % 1000)
? ('.' + QString::number(version % 1000))
: QString());
}
QString FormatVersionPrecise(int version) {
return QString::number(version / 1000000)
+ '.' + QString::number((version % 1000000) / 1000)
+ '.' + QString::number(version % 1000);
}
} // namespace
Changelogs::Changelogs(not_null<AuthSession*> session, int oldVersion)
: _session(session)
, _oldVersion(oldVersion) {
_chatsSubscription = subscribe(
_session->data().moreChatsLoaded(),
[this] { requestCloudLogs(); });
}
std::unique_ptr<Changelogs> Changelogs::Create(
not_null<AuthSession*> session) {
const auto oldVersion = Local::oldMapVersion();
return (oldVersion > 0 && oldVersion < AppVersion)
? std::make_unique<Changelogs>(session, oldVersion)
: nullptr;
}
void Changelogs::requestCloudLogs() {
unsubscribe(base::take(_chatsSubscription));
const auto callback = [this](const MTPUpdates &result) {
_session->api().applyUpdates(result);
auto resultEmpty = true;
switch (result.type()) {
case mtpc_updateShortMessage:
case mtpc_updateShortChatMessage:
case mtpc_updateShort:
resultEmpty = false;
break;
case mtpc_updatesCombined:
resultEmpty = result.c_updatesCombined().vupdates.v.isEmpty();
break;
case mtpc_updates:
resultEmpty = result.c_updates().vupdates.v.isEmpty();
break;
case mtpc_updatesTooLong:
case mtpc_updateShortSentMessage:
LOG(("API Error: Bad updates type in app changelog."));
break;
}
if (resultEmpty) {
addLocalLogs();
}
};
_session->api().requestChangelog(
FormatVersionPrecise(_oldVersion),
base::lambda_guarded(this, callback));
}
void Changelogs::addLocalLogs() {
if (cAlphaVersion() || cBetaVersion()) {
addAlphaLogs();
}
if (!_addedSomeLocal) {
const auto text = lng_new_version_wrap(
lt_version,
str_const_toString(AppVersionStr),
lt_changes,
lang(lng_new_version_minor),
lt_link,
qsl("https://desktop.telegram.org/changelog"));
addLocalLog(text.trimmed());
}
}
void Changelogs::addLocalLog(const QString &text) {
auto textWithEntities = TextWithEntities{ text };
TextUtilities::ParseEntities(textWithEntities, TextParseLinks);
App::wnd()->serviceNotification(
textWithEntities,
MTP_messageMediaEmpty(),
unixtime());
_addedSomeLocal = true;
};
void Changelogs::addAlphaLogs() {
for (const auto[version, changes] : AlphaLogs()) {
addAlphaLog(version, changes);
}
}
void Changelogs::addAlphaLog(int changeVersion, const char *changes) {
if (_oldVersion >= changeVersion) {
return;
}
const auto version = FormatVersionDisplay(changeVersion);
const auto text = qsl("New in version %1:\n\n").arg(version)
+ QString::fromUtf8(changes).trimmed();
addLocalLog(text);
}
} // namespace Core

View File

@@ -0,0 +1,50 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "base/weak_ptr.h"
class AuthSession;
namespace Core {
class Changelogs : public base::has_weak_ptr, private base::Subscriber {
public:
Changelogs(not_null<AuthSession*> session, int oldVersion);
static std::unique_ptr<Changelogs> Create(
not_null<AuthSession*> session);
private:
void requestCloudLogs();
void addLocalLogs();
void addLocalLog(const QString &text);
void addAlphaLogs();
void addAlphaLog(int changeVersion, const char *changes);
const not_null<AuthSession*> _session;
const int _oldVersion = 0;
int _chatsSubscription = 0;
bool _addedSomeLocal = false;
};
} // namespace Core

View File

@@ -24,7 +24,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#define BETA_VERSION_MACRO (0ULL)
constexpr int AppVersion = 1002004;
constexpr str_const AppVersionStr = "1.2.4";
constexpr bool AppAlphaVersion = true;
constexpr int AppVersion = 1002006;
constexpr str_const AppVersionStr = "1.2.6";
constexpr bool AppAlphaVersion = false;
constexpr uint64 AppBetaVersion = BETA_VERSION_MACRO;

View File

@@ -36,6 +36,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "mainwindow.h"
#include "window/window_controller.h"
#include "ui/empty_userpic.h"
#include "ui/text_options.h"
namespace {
@@ -100,7 +101,7 @@ void PeerClickHandler::onClick(Qt::MouseButton button) const {
PeerData::PeerData(const PeerId &id)
: id(id)
, _userpicEmpty(createEmptyUserpic()) {
nameText.setText(st::msgNameStyle, QString(), _textNameOptions);
nameText.setText(st::msgNameStyle, QString(), Ui::NameTextOptions());
}
void PeerData::updateNameDelayed(
@@ -124,7 +125,7 @@ void PeerData::updateNameDelayed(
++nameVersion;
name = newName;
nameText.setText(st::msgNameStyle, name, _textNameOptions);
nameText.setText(st::msgNameStyle, name, Ui::NameTextOptions());
refreshEmptyUserpic();
Notify::PeerUpdate update(this);
@@ -351,7 +352,10 @@ PeerData::~PeerData() = default;
const Text &BotCommand::descriptionText() const {
if (_descriptionText.isEmpty() && !_description.isEmpty()) {
_descriptionText.setText(st::defaultTextStyle, _description, _textNameOptions);
_descriptionText.setText(
st::defaultTextStyle,
_description,
Ui::NameTextOptions());
}
return _descriptionText;
}
@@ -491,7 +495,10 @@ void UserData::setBotInfo(const MTPBotInfo &info) {
void UserData::setNameOrPhone(const QString &newNameOrPhone) {
if (nameOrPhone != newNameOrPhone) {
nameOrPhone = newNameOrPhone;
phoneText.setText(st::msgNameStyle, nameOrPhone, _textNameOptions);
phoneText.setText(
st::msgNameStyle,
nameOrPhone,
Ui::NameTextOptions());
}
}

View File

@@ -36,13 +36,15 @@ MTPmessages_Search PrepareSearchRequest(
const QString &query,
MsgId messageId,
SparseIdsLoadDirection direction) {
auto filter = [&] {
const auto filter = [&] {
using Type = Storage::SharedMediaType;
switch (type) {
case Type::Photo:
return MTP_inputMessagesFilterPhotos();
case Type::Video:
return MTP_inputMessagesFilterVideo();
case Type::PhotoVideo:
return MTP_inputMessagesFilterPhotoVideo();
case Type::MusicFile:
return MTP_inputMessagesFilterMusic();
case Type::File:
@@ -63,10 +65,10 @@ MTPmessages_Search PrepareSearchRequest(
return MTP_inputMessagesFilterEmpty();
}();
auto minId = 0;
auto maxId = 0;
auto limit = messageId ? kSharedMediaLimit : 0;
auto offsetId = [&] {
const auto minId = 0;
const auto maxId = 0;
const auto limit = messageId ? kSharedMediaLimit : 0;
const auto offsetId = [&] {
switch (direction) {
case SparseIdsLoadDirection::Before:
case SparseIdsLoadDirection::Around: return messageId;
@@ -74,7 +76,7 @@ MTPmessages_Search PrepareSearchRequest(
}
Unexpected("Direction in PrepareSearchRequest");
}();
auto addOffset = [&] {
const auto addOffset = [&] {
switch (direction) {
case SparseIdsLoadDirection::Before: return 0;
case SparseIdsLoadDirection::Around: return -limit / 2;
@@ -130,7 +132,8 @@ SearchResult ParseSearchResult(
if (auto channel = peer->asChannel()) {
channel->ptsReceived(d.vpts.v);
} else {
LOG(("API Error: received messages.channelMessages when no channel was passed! (ParseSearchResult)"));
LOG(("API Error: received messages.channelMessages when "
"no channel was passed! (ParseSearchResult)"));
}
App::feedUsers(d.vusers);
App::feedChats(d.vchats);
@@ -139,7 +142,8 @@ SearchResult ParseSearchResult(
} break;
case mtpc_messages_messagesNotModified: {
LOG(("API Error: received messages.messagesNotModified! (ParseSearchResult)"));
LOG(("API Error: received messages.messagesNotModified! "
"(ParseSearchResult)"));
return (const QVector<MTPMessage>*)nullptr;
} break;
}

View File

@@ -28,6 +28,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "styles/style_window.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/popup_menu.h"
#include "ui/text_options.h"
#include "data/data_drafts.h"
#include "lang/lang_keys.h"
#include "mainwindow.h"
@@ -1799,7 +1800,7 @@ void DialogsInner::searchInPeer(PeerData *peer, UserData *from) {
_searchInSavedText.setText(
st::msgNameStyle,
lang(lng_saved_messages),
_textDlgOptions);
Ui::DialogTextOptions());
}
} else {
_cancelSearchInPeer->hide();
@@ -1811,7 +1812,7 @@ void DialogsInner::searchInPeer(PeerData *peer, UserData *from) {
_searchFromUserText.setText(
st::dialogsSearchFromStyle,
fromUserText,
_textDlgOptions);
Ui::DialogTextOptions());
_cancelSearchFromUser->show();
} else {
_cancelSearchFromUser->hide();

View File

@@ -26,6 +26,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "styles/style_dialogs.h"
#include "storage/localstorage.h"
#include "ui/empty_userpic.h"
#include "ui/text_options.h"
#include "lang/lang_keys.h"
namespace Dialogs {
@@ -156,7 +157,7 @@ void paintRow(
if (history->cloudDraftTextCache.isEmpty()) {
auto draftWrapped = textcmdLink(1, lng_dialogs_text_from_wrapped(lt_from, lang(lng_from_draft)));
auto draftText = lng_dialogs_text_with_from(lt_from_part, draftWrapped, lt_message, TextUtilities::Clean(draft->textWithTags.text));
history->cloudDraftTextCache.setText(st::dialogsTextStyle, draftText, _textDlgOptions);
history->cloudDraftTextCache.setText(st::dialogsTextStyle, draftText, Ui::DialogTextOptions());
}
p.setPen(active ? st::dialogsTextFgActive : (selected ? st::dialogsTextFgOver : st::dialogsTextFg));
p.setTextPalette(active ? st::dialogsTextPaletteDraftActive : (selected ? st::dialogsTextPaletteDraftOver : st::dialogsTextPaletteDraft));

View File

@@ -39,6 +39,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "storage/storage_facade.h"
#include "storage/storage_shared_media.h"
#include "data/data_channel_admins.h"
#include "ui/text_options.h"
#include "core/crash_reports.h"
namespace {
@@ -62,7 +63,7 @@ auto GlobalPinnedIndex = 0;
HistoryItem *createUnsupportedMessage(History *history, MsgId msgId, MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, QDateTime date, int32 from) {
auto text = TextWithEntities { lng_message_unsupported(lt_link, qsl("https://desktop.telegram.org")) };
TextUtilities::ParseEntities(text, _historyTextNoMonoOptions.flags);
TextUtilities::ParseEntities(text, Ui::ItemTextNoMonoOptions().flags);
text.entities.push_front(EntityInText(EntityInTextItalic, 0, text.text.size()));
flags &= ~MTPDmessage::Flag::f_post_author;
return HistoryMessage::create(history, msgId, flags, replyTo, viaBotId, date, from, QString(), text);
@@ -70,11 +71,6 @@ HistoryItem *createUnsupportedMessage(History *history, MsgId msgId, MTPDmessage
} // namespace
void HistoryInit() {
HistoryInitMessages();
HistoryInitMedia();
}
History::History(const PeerId &peerId)
: peer(App::peer(peerId))
, lastItemTextCache(st::dialogsTextWidthMin)
@@ -364,7 +360,10 @@ bool History::updateSendActionNeedsAnimating(TimeMs ms, bool force) {
}
if (_sendActionString != newTypingString) {
_sendActionString = newTypingString;
_sendActionText.setText(st::dialogsTextStyle, _sendActionString, _textNameOptions);
_sendActionText.setText(
st::dialogsTextStyle,
_sendActionString,
Ui::NameTextOptions());
}
}
auto result = (!_typing.isEmpty() || !_sendActions.isEmpty());

View File

@@ -30,8 +30,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "base/flat_set.h"
#include "base/flags.h"
void HistoryInit();
class HistoryItem;
using HistoryItemsList = std::vector<not_null<HistoryItem*>>;

View File

@@ -27,6 +27,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "history/history_service_layout.h"
#include "history/history_media_types.h"
#include "history/history_item_components.h"
#include "ui/text_options.h"
#include "ui/widgets/popup_menu.h"
#include "window/window_controller.h"
#include "window/window_peer_menu.h"
@@ -1836,7 +1837,10 @@ void HistoryInner::updateBotInfo(bool recount) {
int newh = 0;
if (_botAbout && !_botAbout->info->description.isEmpty()) {
if (_botAbout->info->text.isEmpty()) {
_botAbout->info->text.setText(st::messageTextStyle, _botAbout->info->description, _historyBotNoMonoOptions);
_botAbout->info->text.setText(
st::messageTextStyle,
_botAbout->info->description,
Ui::ItemTextBotNoMonoOptions());
if (recount) {
int32 tw = _scroll->width() - st::msgMargin.left() - st::msgMargin.right();
if (tw > st::msgMaxWidth) tw = st::msgMaxWidth;

View File

@@ -31,6 +31,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "styles/style_dialogs.h"
#include "styles/style_history.h"
#include "ui/effects/ripple_animation.h"
#include "ui/text_options.h"
#include "storage/file_upload.h"
#include "storage/storage_facade.h"
#include "storage/storage_shared_media.h"
@@ -1032,7 +1033,7 @@ void HistoryItem::drawInDialog(
Text &cache) const {
if (cacheFor != this) {
cacheFor = this;
cache.setText(st::dialogsTextStyle, inDialogsText(way), _textDlgOptions);
cache.setText(st::dialogsTextStyle, inDialogsText(way), Ui::DialogTextOptions());
}
if (r.width()) {
p.setTextPalette(active ? st::dialogsTextPaletteActive : (selected ? st::dialogsTextPaletteOver : st::dialogsTextPalette));

View File

@@ -22,6 +22,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "lang/lang_keys.h"
#include "ui/effects/ripple_animation.h"
#include "ui/text_options.h"
#include "history/history_service_layout.h"
#include "history/history_message.h"
#include "history/history_media.h"
@@ -63,7 +64,10 @@ void HistoryMessageSigned::refresh(const QString &date) {
if (timew + namew > st::maxSignatureSize) {
name = st::msgDateFont->elided(author, st::maxSignatureSize - timew);
}
signature.setText(st::msgDateTextStyle, name + time, _textNameOptions);
signature.setText(
st::msgDateTextStyle,
name + time,
Ui::NameTextOptions());
}
int HistoryMessageSigned::maxWidth() const {
@@ -72,7 +76,7 @@ int HistoryMessageSigned::maxWidth() const {
void HistoryMessageEdited::refresh(const QString &date, bool displayed) {
const auto prefix = displayed ? (lang(lng_edited) + ' ') : QString();
text.setText(st::msgDateTextStyle, prefix + date, _textNameOptions);
text.setText(st::msgDateTextStyle, prefix + date, Ui::NameTextOptions());
}
int HistoryMessageEdited::maxWidth() const {
@@ -151,7 +155,10 @@ bool HistoryMessageReply::updateData(HistoryMessage *holder, bool force) {
}
if (replyToMsg) {
replyToText.setText(st::messageTextStyle, TextUtilities::Clean(replyToMsg->inReplyText()), _textDlgOptions);
replyToText.setText(
st::messageTextStyle,
TextUtilities::Clean(replyToMsg->inReplyText()),
Ui::DialogTextOptions());
updateName();
@@ -193,7 +200,7 @@ void HistoryMessageReply::updateName() const {
QString name = (replyToVia && replyToMsg->author()->isUser())
? replyToMsg->author()->asUser()->firstName
: App::peerName(replyToMsg->author());
replyToName.setText(st::fwdTextStyle, name, _textNameOptions);
replyToName.setText(st::fwdTextStyle, name, Ui::NameTextOptions());
replyToVersion = replyToMsg->author()->nameVersion;
bool hasPreview = replyToMsg->getMedia() ? replyToMsg->getMedia()->hasReplyPreview() : false;
int32 previewSkip = hasPreview ? (st::msgReplyBarSize.height() + st::msgReplyBarSkip - st::msgReplyBarSize.width() - st::msgReplyBarPos.x()) : 0;

View File

@@ -26,6 +26,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "storage/storage_shared_media.h"
#include "lang/lang_keys.h"
#include "ui/grouped_layout.h"
#include "ui/text_options.h"
#include "styles/style_history.h"
HistoryGroupedMedia::Element::Element(not_null<HistoryItem*> item)
@@ -441,7 +442,7 @@ void HistoryGroupedMedia::updateNeedBubbleState() {
_caption.setText(
st::messageTextStyle,
captionText.text + _parent->skipBlock(),
itemTextNoMonoOptions(_parent));
Ui::ItemTextNoMonoOptions(_parent));
_needBubble = computeNeedBubble();
}

View File

@@ -41,44 +41,13 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "calls/calls_instance.h"
#include "ui/empty_userpic.h"
#include "ui/grouped_layout.h"
#include "ui/text_options.h"
namespace {
constexpr auto kMaxGifForwardedBarLines = 4;
constexpr auto kMaxOriginalEntryLines = 8192;
TextParseOptions _webpageTitleOptions = {
TextParseMultiline | TextParseRichText, // flags
0, // maxw
0, // maxh
Qt::LayoutDirectionAuto, // dir
};
TextParseOptions _webpageDescriptionOptions = {
TextParseLinks | TextParseMentions | TextParseHashtags | TextParseMultiline | TextParseRichText | TextParseMarkdown, // flags
0, // maxw
0, // maxh
Qt::LayoutDirectionAuto, // dir
};
TextParseOptions _twitterDescriptionOptions = {
TextParseLinks | TextParseMentions | TextTwitterMentions | TextParseHashtags | TextTwitterHashtags | TextParseMultiline | TextParseRichText, // flags
0, // maxw
0, // maxh
Qt::LayoutDirectionAuto, // dir
};
TextParseOptions _instagramDescriptionOptions = {
TextParseLinks | TextParseMentions | TextInstagramMentions | TextParseHashtags | TextInstagramHashtags | TextParseMultiline | TextParseRichText, // flags
0, // maxw
0, // maxh
Qt::LayoutDirectionAuto, // dir
};
inline void initTextOptions() {
_webpageTitleOptions.maxw = st::msgMaxWidth - st::msgPadding.left() - st::msgPadding.right() - st::webPageLeft;
_webpageTitleOptions.maxh = st::webPageTitleFont->height * 2;
_webpageDescriptionOptions.maxw = st::msgMaxWidth - st::msgPadding.left() - st::msgPadding.right() - st::webPageLeft;
_webpageDescriptionOptions.maxh = st::webPageDescriptionFont->height * 3;
}
bool needReSetInlineResultDocument(const MTPMessageMedia &media, DocumentData *existing) {
if (media.type() == mtpc_messageMediaDocument) {
auto &mediaDocument = media.c_messageMediaDocument();
@@ -121,10 +90,6 @@ int32 gifMaxStatusWidth(DocumentData *document) {
} // namespace
void HistoryInitMedia() {
initTextOptions();
}
TextWithEntities WithCaptionSelectedText(
const QString &attachType,
const Text &caption,
@@ -263,7 +228,7 @@ HistoryPhoto::HistoryPhoto(
_caption.setText(
st::messageTextStyle,
caption + _parent->skipBlock(),
itemTextNoMonoOptions(_parent));
Ui::ItemTextNoMonoOptions(_parent));
}
init();
}
@@ -839,10 +804,13 @@ bool HistoryPhoto::needsBubble() const {
}
Storage::SharedMediaTypesMask HistoryPhoto::sharedMediaTypes() const {
using Type = Storage::SharedMediaType;
if (_parent->toHistoryMessage()) {
return Storage::SharedMediaType::Photo;
return Storage::SharedMediaTypesMask{}
.added(Type::Photo)
.added(Type::PhotoVideo);
}
return Storage::SharedMediaType::ChatPhoto;
return Type::ChatPhoto;
}
ImagePtr HistoryPhoto::replyPreview() {
@@ -861,7 +829,7 @@ HistoryVideo::HistoryVideo(
_caption.setText(
st::messageTextStyle,
caption + _parent->skipBlock(),
itemTextNoMonoOptions(_parent));
Ui::ItemTextNoMonoOptions(_parent));
}
setDocumentLinks(_data, parent);
@@ -1264,8 +1232,8 @@ void HistoryVideo::validateGroupedCache(
const auto pixSize = Ui::GetImageScaleSizeForGeometry(
{ originalWidth, originalHeight },
{ width, height });
const auto pixWidth = pixSize.width();
const auto pixHeight = pixSize.height();
const auto pixWidth = pixSize.width() * cIntRetinaFactor();
const auto pixHeight = pixSize.height() * cIntRetinaFactor();
const auto &image = _data->thumb;
*cacheKey = key;
@@ -1305,7 +1273,10 @@ bool HistoryVideo::needsBubble() const {
}
Storage::SharedMediaTypesMask HistoryVideo::sharedMediaTypes() const {
return Storage::SharedMediaType::Video;
using Type = Storage::SharedMediaType;
return Storage::SharedMediaTypesMask{}
.added(Type::Video)
.added(Type::PhotoVideo);
}
void HistoryVideo::updateStatusText() const {
@@ -1380,7 +1351,10 @@ HistoryDocument::HistoryDocument(
setStatusSize(FileStatusSizeReady);
if (auto captioned = Get<HistoryDocumentCaptioned>()) {
captioned->_caption.setText(st::messageTextStyle, caption + _parent->skipBlock(), itemTextNoMonoOptions(_parent));
captioned->_caption.setText(
st::messageTextStyle,
caption + _parent->skipBlock(),
Ui::ItemTextNoMonoOptions(_parent));
}
}
@@ -2168,7 +2142,10 @@ HistoryGif::HistoryGif(
setStatusSize(FileStatusSizeReady);
if (!caption.isEmpty() && !_data->isVideoMessage()) {
_caption.setText(st::messageTextStyle, caption + _parent->skipBlock(), itemTextNoMonoOptions(_parent));
_caption.setText(
st::messageTextStyle,
caption + _parent->skipBlock(),
Ui::ItemTextNoMonoOptions(_parent));
}
_data->thumb->load();
@@ -3304,7 +3281,10 @@ HistoryContact::HistoryContact(not_null<HistoryItem*> parent, int32 userId, cons
, _fname(first)
, _lname(last)
, _phone(App::formatPhone(phone)) {
_name.setText(st::semiboldTextStyle, lng_full_name(lt_first_name, first, lt_last_name, last).trimmed(), _textNameOptions);
_name.setText(
st::semiboldTextStyle,
lng_full_name(lt_first_name, first, lt_last_name, last).trimmed(),
Ui::NameTextOptions());
_phonew = st::normalFont->width(_phone);
}
@@ -3695,12 +3675,6 @@ void HistoryWebPage::initDimensions() {
if (textFloatsAroundInfo) {
text.text += _parent->skipBlock();
}
auto opts = &_webpageDescriptionOptions;
if (_data->siteName == qstr("Twitter")) {
opts = &_twitterDescriptionOptions;
} else if (_data->siteName == qstr("Instagram")) {
opts = &_instagramDescriptionOptions;
}
if (isLogEntryOriginal()) {
// Fix layout for small bubbles (narrow media caption edit log entries).
_description = Text(st::minPhotoSize
@@ -3708,13 +3682,19 @@ void HistoryWebPage::initDimensions() {
- st::msgPadding.right()
- st::webPageLeft);
}
_description.setMarkedText(st::webPageDescriptionStyle, text, *opts);
_description.setMarkedText(
st::webPageDescriptionStyle,
text,
Ui::WebpageTextDescriptionOptions(_data->siteName));
}
if (_title.isEmpty() && !title.isEmpty()) {
if (textFloatsAroundInfo && _description.isEmpty()) {
title += _parent->skipBlock();
}
_title.setText(st::webPageTitleStyle, title, _webpageTitleOptions);
_title.setText(
st::webPageTitleStyle,
title,
Ui::WebpageTextTitleOptions());
}
if (!_siteNameWidth && !_data->siteName.isEmpty()) {
_siteNameWidth = st::webPageTitleFont->width(_data->siteName);
@@ -4257,11 +4237,17 @@ void HistoryGame::initDimensions() {
auto marked = TextWithEntities { text };
auto parseFlags = TextParseLinks | TextParseMultiline | TextParseRichText;
TextUtilities::ParseEntities(marked, parseFlags);
_description.setMarkedText(st::webPageDescriptionStyle, marked, _webpageDescriptionOptions);
_description.setMarkedText(
st::webPageDescriptionStyle,
marked,
Ui::WebpageTextDescriptionOptions());
}
}
if (_title.isEmpty() && !title.isEmpty()) {
_title.setText(st::webPageTitleStyle, title, _webpageTitleOptions);
_title.setText(
st::webPageTitleStyle,
title,
Ui::WebpageTextTitleOptions());
}
// init dimensions
@@ -4523,7 +4509,10 @@ TextSelection HistoryGame::adjustSelection(TextSelection selection, TextSelectTy
}
bool HistoryGame::consumeMessageText(const TextWithEntities &textWithEntities) {
_description.setMarkedText(st::webPageDescriptionStyle, textWithEntities, itemTextOptions(_parent));
_description.setMarkedText(
st::webPageDescriptionStyle,
textWithEntities,
Ui::ItemTextOptions(_parent));
return true;
}
@@ -4696,7 +4685,10 @@ void HistoryInvoice::fillFromData(const MTPDmessageMediaInvoice &data) {
};
statusText.entities.push_back(EntityInText(EntityInTextBold, 0, statusText.text.size()));
statusText.text += ' ' + labelText().toUpper();
_status.setMarkedText(st::defaultTextStyle, statusText, itemTextOptions(_parent));
_status.setMarkedText(
st::defaultTextStyle,
statusText,
Ui::ItemTextOptions(_parent));
_receiptMsgId = data.has_receipt_msg_id() ? data.vreceipt_msg_id.v : 0;
@@ -4706,11 +4698,17 @@ void HistoryInvoice::fillFromData(const MTPDmessageMediaInvoice &data) {
auto marked = TextWithEntities { description };
auto parseFlags = TextParseLinks | TextParseMultiline | TextParseRichText;
TextUtilities::ParseEntities(marked, parseFlags);
_description.setMarkedText(st::webPageDescriptionStyle, marked, _webpageDescriptionOptions);
_description.setMarkedText(
st::webPageDescriptionStyle,
marked,
Ui::WebpageTextDescriptionOptions());
}
auto title = TextUtilities::SingleLine(qs(data.vtitle));
if (!title.isEmpty()) {
_title.setText(st::webPageTitleStyle, title, _webpageTitleOptions);
_title.setText(
st::webPageTitleStyle,
title,
Ui::WebpageTextTitleOptions());
}
}
@@ -5025,13 +5023,19 @@ HistoryLocation::HistoryLocation(not_null<HistoryItem*> parent, const LocationCo
, _description(st::msgMinWidth)
, _link(std::make_shared<LocationClickHandler>(coords)) {
if (!title.isEmpty()) {
_title.setText(st::webPageTitleStyle, TextUtilities::Clean(title), _webpageTitleOptions);
_title.setText(
st::webPageTitleStyle,
TextUtilities::Clean(title),
Ui::WebpageTextTitleOptions());
}
if (!description.isEmpty()) {
auto marked = TextWithEntities { TextUtilities::Clean(description) };
auto parseFlags = TextParseLinks | TextParseMultiline | TextParseRichText;
TextUtilities::ParseEntities(marked, parseFlags);
_description.setMarkedText(st::webPageDescriptionStyle, marked, _webpageDescriptionOptions);
_description.setMarkedText(
st::webPageDescriptionStyle,
marked,
Ui::WebpageTextDescriptionOptions());
}
}

View File

@@ -43,7 +43,6 @@ namespace Ui {
class EmptyUserpic;
} // namespace Ui
void HistoryInitMedia();
TextWithEntities WithCaptionSelectedText(
const QString &attachType,
const Text &caption,

View File

@@ -33,6 +33,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "boxes/share_box.h"
#include "boxes/confirm_box.h"
#include "ui/toast/toast.h"
#include "ui/text_options.h"
#include "messenger.h"
#include "styles/style_dialogs.h"
#include "styles/style_widgets.h"
@@ -138,11 +139,6 @@ int KeyboardStyle::minButtonWidth(
return result;
}
inline void initTextOptions() {
_historySrvOptions.dir = _textNameOptions.dir = _textDlgOptions.dir = cLangDir();
_textDlgOptions.maxw = st::columnMaximalWidthLeft * 2;
}
QString AdminBadgeText() {
return lang(lng_admin_badge);
}
@@ -409,10 +405,6 @@ void FastShareMessage(not_null<HistoryItem*> item) {
std::move(filterCallback)));
}
void HistoryInitMessages() {
initTextOptions();
}
base::lambda<void(ChannelData*, MsgId)> HistoryDependentItemCallback(
const FullMsgId &msgId) {
return [dependent = msgId](ChannelData *channel, MsgId msgId) {
@@ -1501,9 +1493,18 @@ void HistoryMessage::setText(const TextWithEntities &textWithEntities) {
} else {
auto mediaOnBottom = (_media && _media->isDisplayed() && _media->isBubbleBottom()) || Has<HistoryMessageLogEntryOriginal>();
if (mediaOnBottom) {
_text.setMarkedText(st::messageTextStyle, textWithEntities, itemTextOptions(this));
_text.setMarkedText(
st::messageTextStyle,
textWithEntities,
Ui::ItemTextOptions(this));
} else {
_text.setMarkedText(st::messageTextStyle, { textWithEntities.text + skipBlock(), textWithEntities.entities }, itemTextOptions(this));
_text.setMarkedText(
st::messageTextStyle,
{
textWithEntities.text + skipBlock(),
textWithEntities.entities
},
Ui::ItemTextOptions(this));
}
_textWidth = -1;
_textHeight = 0;
@@ -1511,7 +1512,10 @@ void HistoryMessage::setText(const TextWithEntities &textWithEntities) {
}
void HistoryMessage::setEmptyText() {
_text.setMarkedText(st::messageTextStyle, { QString(), EntitiesInText() }, itemTextOptions(this));
_text.setMarkedText(
st::messageTextStyle,
{ QString(), EntitiesInText() },
Ui::ItemTextOptions(this));
_textWidth = -1;
_textHeight = 0;

View File

@@ -24,7 +24,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
struct HistoryMessageEdited;
void HistoryInitMessages();
base::lambda<void(ChannelData*, MsgId)> HistoryDependentItemCallback(
const FullMsgId &msgId);
MTPDmessage::Flags NewMessageFlags(not_null<PeerData*> peer);

View File

@@ -30,6 +30,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "auth_session.h"
#include "window/notifications_manager.h"
#include "storage/storage_shared_media.h"
#include "ui/text_options.h"
namespace {
@@ -37,13 +38,6 @@ constexpr auto kPinnedMessageTextLimit = 16;
} // namespace
TextParseOptions _historySrvOptions = {
TextParseLinks | TextParseMentions | TextParseHashtags/* | TextParseMultiline*/ | TextParseRichText, // flags
0, // maxw
0, // maxh
Qt::LayoutDirectionAuto, // lang-dependent
};
void HistoryService::setMessageByAction(const MTPmessageAction &action) {
auto prepareChatAddUserText = [this](const MTPDmessageActionChatAddUser &action) {
auto result = PreparedText {};
@@ -495,7 +489,10 @@ QString HistoryService::inReplyText() const {
}
void HistoryService::setServiceText(const PreparedText &prepared) {
_text.setText(st::serviceTextStyle, prepared.text, _historySrvOptions);
_text.setText(
st::serviceTextStyle,
prepared.text,
Ui::ItemTextServiceOptions());
auto linkIndex = 0;
for_const (auto &link, prepared.links) {
// Link indices start with 1.

View File

@@ -182,5 +182,3 @@ private:
static PreparedText GenerateText(not_null<History*> history, not_null<UserData*> inviter);
};
extern TextParseOptions _historySrvOptions;

View File

@@ -74,6 +74,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "observer_peer.h"
#include "base/qthelp_regex.h"
#include "ui/widgets/popup_menu.h"
#include "ui/text_options.h"
#include "auth_session.h"
#include "window/themes/window_theme.h"
#include "window/notifications_manager.h"
@@ -382,7 +383,7 @@ bool HistoryHider::offerPeer(PeerId peer) {
return false;
}
_toText.setText(st::boxLabelStyle, phrase, _textNameOptions);
_toText.setText(st::boxLabelStyle, phrase, Ui::NameTextOptions());
_toTextWidth = _toText.maxWidth();
if (_toTextWidth > _box.width() - st::boxPadding.left() - st::boxLayerButtonPadding.right()) {
_toTextWidth = _box.width() - st::boxPadding.left() - st::boxLayerButtonPadding.right();
@@ -2776,7 +2777,7 @@ void HistoryWidget::saveEditMsg() {
WebPageId webPageId = _previewCancelled ? CancelledWebPageId : ((_previewData && _previewData->pendingTill >= 0) ? _previewData->id : 0);
auto &textWithTags = _field->getTextWithTags();
auto prepareFlags = itemTextOptions(_history, App::self()).flags;
auto prepareFlags = Ui::ItemTextOptions(_history, App::self()).flags;
auto sending = TextWithEntities();
auto left = TextWithEntities { textWithTags.text, ConvertTextTagsToEntities(textWithTags.tags) };
TextUtilities::PrepareForSending(left, prepareFlags);
@@ -3664,7 +3665,10 @@ void HistoryWidget::onKbToggle(bool manual) {
_kbReplyTo = (_peer->isChat() || _peer->isChannel() || _keyboard->forceReply()) ? App::histItemById(_keyboard->forMsgId()) : 0;
if (_kbReplyTo && !_editMsgId && !_replyToId && fieldEnabled) {
updateReplyToName();
_replyEditMsgText.setText(st::messageTextStyle, TextUtilities::Clean(_kbReplyTo->inReplyText()), _textDlgOptions);
_replyEditMsgText.setText(
st::messageTextStyle,
TextUtilities::Clean(_kbReplyTo->inReplyText()),
Ui::DialogTextOptions());
_fieldBarCancel->show();
updateMouseTracking();
}
@@ -3683,7 +3687,10 @@ void HistoryWidget::onKbToggle(bool manual) {
_kbReplyTo = (_peer->isChat() || _peer->isChannel() || _keyboard->forceReply()) ? App::histItemById(_keyboard->forMsgId()) : 0;
if (_kbReplyTo && !_editMsgId && !_replyToId) {
updateReplyToName();
_replyEditMsgText.setText(st::messageTextStyle, TextUtilities::Clean(_kbReplyTo->inReplyText()), _textDlgOptions);
_replyEditMsgText.setText(
st::messageTextStyle,
TextUtilities::Clean(_kbReplyTo->inReplyText()),
Ui::DialogTextOptions());
_fieldBarCancel->show();
updateMouseTracking();
}
@@ -4090,7 +4097,9 @@ bool HistoryWidget::confirmSendingFiles(
const auto urls = data->urls();
for (const auto &url : urls) {
if (url.isLocalFile()) {
confirmSendingFiles(urls, compressed, insertTextOnCancel);
// Don't insert list of filenames on cancel.
const auto emptyTextOnCancel = QString();
confirmSendingFiles(urls, compressed, emptyTextOnCancel);
return true;
}
}
@@ -4877,7 +4886,10 @@ void HistoryWidget::updateBotKeyboard(History *h, bool force) {
_kbReplyTo = (_peer->isChat() || _peer->isChannel() || _keyboard->forceReply()) ? App::histItemById(_keyboard->forMsgId()) : 0;
if (_kbReplyTo && !_replyToId) {
updateReplyToName();
_replyEditMsgText.setText(st::messageTextStyle, TextUtilities::Clean(_kbReplyTo->inReplyText()), _textDlgOptions);
_replyEditMsgText.setText(
st::messageTextStyle,
TextUtilities::Clean(_kbReplyTo->inReplyText()),
Ui::DialogTextOptions());
_fieldBarCancel->show();
updateMouseTracking();
}
@@ -5211,7 +5223,10 @@ void HistoryWidget::updatePinnedBar(bool force) {
_pinnedBar->msg = App::histItemById(_history->channelId(), _pinnedBar->msgId);
}
if (_pinnedBar->msg) {
_pinnedBar->text.setText(st::messageTextStyle, TextUtilities::Clean(_pinnedBar->msg->notificationText()), _textDlgOptions);
_pinnedBar->text.setText(
st::messageTextStyle,
TextUtilities::Clean(_pinnedBar->msg->notificationText()),
Ui::DialogTextOptions());
update();
} else if (force) {
if (auto channel = _peer ? _peer->asChannel() : nullptr) {
@@ -5508,7 +5523,10 @@ void HistoryWidget::onReplyToMessage() {
} else {
_replyEditMsg = to;
_replyToId = to->id;
_replyEditMsgText.setText(st::messageTextStyle, TextUtilities::Clean(_replyEditMsg->inReplyText()), _textDlgOptions);
_replyEditMsgText.setText(
st::messageTextStyle,
TextUtilities::Clean(_replyEditMsg->inReplyText()),
Ui::DialogTextOptions());
updateBotKeyboard();
@@ -5864,13 +5882,19 @@ void HistoryWidget::updatePreview() {
_fieldBarCancel->show();
updateMouseTracking();
if (_previewData->pendingTill) {
_previewTitle.setText(st::msgNameStyle, lang(lng_preview_loading), _textNameOptions);
_previewTitle.setText(
st::msgNameStyle,
lang(lng_preview_loading),
Ui::NameTextOptions());
#ifndef OS_MAC_OLD
auto linkText = _previewLinks.splitRef(' ').at(0).toString();
#else // OS_MAC_OLD
auto linkText = _previewLinks.split(' ').at(0);
#endif // OS_MAC_OLD
_previewDescription.setText(st::messageTextStyle, TextUtilities::Clean(linkText), _textDlgOptions);
_previewDescription.setText(
st::messageTextStyle,
TextUtilities::Clean(linkText),
Ui::DialogTextOptions());
int32 t = (_previewData->pendingTill - unixtime()) * 1000;
if (t <= 0) t = 1;
@@ -5901,8 +5925,14 @@ void HistoryWidget::updatePreview() {
title = lang(lng_attach_photo);
}
}
_previewTitle.setText(st::msgNameStyle, title, _textNameOptions);
_previewDescription.setText(st::messageTextStyle, TextUtilities::Clean(desc), _textDlgOptions);
_previewTitle.setText(
st::msgNameStyle,
title,
Ui::NameTextOptions());
_previewDescription.setText(
st::messageTextStyle,
TextUtilities::Clean(desc),
Ui::DialogTextOptions());
}
} else if (!readyToForward() && !replyToId() && !_editMsgId) {
_fieldBarCancel->hide();
@@ -6122,7 +6152,10 @@ void HistoryWidget::updateReplyEditTexts(bool force) {
_replyEditMsg = App::histItemById(_channel, _editMsgId ? _editMsgId : _replyToId);
}
if (_replyEditMsg) {
_replyEditMsgText.setText(st::messageTextStyle, TextUtilities::Clean(_replyEditMsg->inReplyText()), _textDlgOptions);
_replyEditMsgText.setText(
st::messageTextStyle,
TextUtilities::Clean(_replyEditMsg->inReplyText()),
Ui::DialogTextOptions());
updateBotKeyboard();
@@ -6181,8 +6214,11 @@ void HistoryWidget::updateForwardingTexts() {
text = lng_forward_messages(lt_count, count);
}
}
_toForwardFrom.setText(st::msgNameStyle, from, _textNameOptions);
_toForwardText.setText(st::messageTextStyle, TextUtilities::Clean(text), _textDlgOptions);
_toForwardFrom.setText(st::msgNameStyle, from, Ui::NameTextOptions());
_toForwardText.setText(
st::messageTextStyle,
TextUtilities::Clean(text),
Ui::DialogTextOptions());
_toForwardNameVersion = version;
}
@@ -6201,7 +6237,10 @@ void HistoryWidget::checkForwardingInfo() {
void HistoryWidget::updateReplyToName() {
if (_editMsgId) return;
if (!_replyEditMsg && (_replyToId || !_kbReplyTo)) return;
_replyToName.setText(st::msgNameStyle, App::peerName((_replyEditMsg ? _replyEditMsg : _kbReplyTo)->author()), _textNameOptions);
_replyToName.setText(
st::msgNameStyle,
App::peerName((_replyEditMsg ? _replyEditMsg : _kbReplyTo)->author()),
Ui::NameTextOptions());
_replyToNameVersion = (_replyEditMsg ? _replyEditMsg : _kbReplyTo)->author()->nameVersion;
}

View File

@@ -32,65 +32,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "media/media_audio.h"
#include "storage/localstorage.h"
TextParseOptions _textNameOptions = {
0, // flags
4096, // maxw
1, // maxh
Qt::LayoutDirectionAuto, // lang-dependent
};
TextParseOptions _textDlgOptions = {
TextParseRichText, // flags
0, // maxw is style-dependent
1, // maxh
Qt::LayoutDirectionAuto, // lang-dependent
};
TextParseOptions _historyTextOptions = {
TextParseLinks | TextParseMentions | TextParseHashtags | TextParseMultiline | TextParseRichText | TextParseMarkdown, // flags
0, // maxw
0, // maxh
Qt::LayoutDirectionAuto, // dir
};
TextParseOptions _historyBotOptions = {
TextParseLinks | TextParseMentions | TextParseHashtags | TextParseBotCommands | TextParseMultiline | TextParseRichText | TextParseMarkdown, // flags
0, // maxw
0, // maxh
Qt::LayoutDirectionAuto, // dir
};
TextParseOptions _historyTextNoMonoOptions = {
TextParseLinks | TextParseMentions | TextParseHashtags | TextParseMultiline | TextParseRichText, // flags
0, // maxw
0, // maxh
Qt::LayoutDirectionAuto, // dir
};
TextParseOptions _historyBotNoMonoOptions = {
TextParseLinks | TextParseMentions | TextParseHashtags | TextParseBotCommands | TextParseMultiline | TextParseRichText, // flags
0, // maxw
0, // maxh
Qt::LayoutDirectionAuto, // dir
};
const TextParseOptions &itemTextOptions(History *h, PeerData *f) {
if ((h->peer->isUser() && h->peer->asUser()->botInfo) || (f->isUser() && f->asUser()->botInfo) || (h->peer->isChat() && h->peer->asChat()->botStatus >= 0) || (h->peer->isMegagroup() && h->peer->asChannel()->mgInfo->botStatus >= 0)) {
return _historyBotOptions;
}
return _historyTextOptions;
}
const TextParseOptions &itemTextOptions(const HistoryItem *item) {
return itemTextOptions(item->history(), item->author());
}
const TextParseOptions &itemTextNoMonoOptions(History *h, PeerData *f) {
if ((h->peer->isUser() && h->peer->asUser()->botInfo) || (f->isUser() && f->asUser()->botInfo) || (h->peer->isChat() && h->peer->asChat()->botStatus >= 0) || (h->peer->isMegagroup() && h->peer->asChannel()->mgInfo->botStatus >= 0)) {
return _historyBotNoMonoOptions;
}
return _historyTextNoMonoOptions;
}
const TextParseOptions &itemTextNoMonoOptions(const HistoryItem *item) {
return itemTextNoMonoOptions(item->history(), item->author());
}
QString formatSizeText(qint64 size) {
if (size >= 1024 * 1024) { // more than 1 mb
qint64 sizeTenthMb = (size * 10 / (1024 * 1024));

View File

@@ -58,14 +58,6 @@ inline bool IsGroupItemSelection(
: selection;
}
extern TextParseOptions _textNameOptions, _textDlgOptions;
extern TextParseOptions _historyTextOptions, _historyBotOptions, _historyTextNoMonoOptions, _historyBotNoMonoOptions;
const TextParseOptions &itemTextOptions(History *h, PeerData *f);
const TextParseOptions &itemTextOptions(const HistoryItem *item);
const TextParseOptions &itemTextNoMonoOptions(History *h, PeerData *f);
const TextParseOptions &itemTextNoMonoOptions(const HistoryItem *item);
enum RoundCorners {
SmallMaskCorners = 0x00, // for images
LargeMaskCorners,

View File

@@ -39,6 +39,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "ui/widgets/dropdown_menu.h"
#include "ui/focus_persister.h"
#include "ui/resize_area.h"
#include "ui/text_options.h"
#include "ui/toast/toast.h"
#include "chat_helpers/message_field.h"
#include "chat_helpers/stickers.h"
@@ -1456,7 +1457,7 @@ void MainWidget::sendMessage(const MessageToSend &message) {
auto sending = TextWithEntities();
auto left = TextWithEntities { textWithTags.text, ConvertTextTagsToEntities(textWithTags.tags) };
auto prepareFlags = itemTextOptions(history, App::self()).flags;
auto prepareFlags = Ui::ItemTextOptions(history, App::self()).flags;
TextUtilities::PrepareForSending(left, prepareFlags);
HistoryItem *lastMessage = nullptr;
@@ -1744,7 +1745,7 @@ void MainWidget::createPlayer() {
_player->shownValue()
) | rpl::start_with_next(
[this] { playerHeightUpdated(); },
lifetime());
_player->lifetime());
_player->entity()->setCloseCallback([this] { closeBothPlayers(); });
_playerVolume.create(this);
_player->entity()->volumeWidgetCreated(_playerVolume);
@@ -1768,6 +1769,10 @@ void MainWidget::createPlayer() {
}
void MainWidget::playerHeightUpdated() {
if (!_player) {
// Player could be already "destroyDelayed", but still handle events.
return;
}
auto playerHeight = _player->contentHeight();
if (playerHeight != _playerHeight) {
_contentScrollAddToY += playerHeight - _playerHeight;

View File

@@ -0,0 +1,663 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#include "media/view/media_view_group_thumbs.h"
#include "data/data_shared_media.h"
#include "data/data_user_photos.h"
#include "data/data_photo.h"
#include "data/data_document.h"
#include "history/history_media.h"
#include "styles/style_mediaview.h"
namespace Media {
namespace View {
namespace {
constexpr auto kThumbDuration = TimeMs(150);
int Round(float64 value) {
return int(std::round(value));
}
using Context = GroupThumbs::Context;
using Key = GroupThumbs::Key;
Context ComputeContext(const SharedMediaWithLastSlice &slice, int index) {
Expects(index >= 0 && index < slice.size());
const auto value = slice[index];
if (const auto photo = base::get_if<not_null<PhotoData*>>(&value)) {
if (const auto peer = (*photo)->peer) {
return peer->id;
}
return base::none;
} else if (const auto msgId = base::get_if<FullMsgId>(&value)) {
if (const auto item = App::histItemById(*msgId)) {
if (!item->toHistoryMessage()) {
return item->history()->peer->id;
} else if (const auto groupId = item->groupId()) {
return groupId;
}
}
return base::none;
}
Unexpected("Variant in ComputeContext(SharedMediaWithLastSlice::Value)");
}
Context ComputeContext(const UserPhotosSlice &slice, int index) {
return peerFromUser(slice.key().userId);
}
Key ComputeKey(const SharedMediaWithLastSlice &slice, int index) {
Expects(index >= 0 && index < slice.size());
const auto value = slice[index];
if (const auto photo = base::get_if<not_null<PhotoData*>>(&value)) {
return (*photo)->id;
} else if (const auto msgId = base::get_if<FullMsgId>(&value)) {
return *msgId;
}
Unexpected("Variant in ComputeContext(SharedMediaWithLastSlice::Value)");
}
Key ComputeKey(const UserPhotosSlice &slice, int index) {
return slice[index];
}
int ComputeThumbsLimit(int availableWidth) {
const auto singleWidth = st::mediaviewGroupWidth
+ 2 * st::mediaviewGroupSkip;
const auto currentWidth = st::mediaviewGroupWidthMax
+ 2 * st::mediaviewGroupSkipCurrent;
const auto skipForAnimation = 2 * singleWidth;
const auto leftWidth = availableWidth
- currentWidth
- skipForAnimation;
return std::max(leftWidth / (2 * singleWidth), 1);
}
} // namespace
class GroupThumbs::Thumb {
public:
enum class State {
Unknown,
Current,
Alive,
Dying,
};
Thumb(Key key, ImagePtr image, base::lambda<void()> handler);
int leftToUpdate() const;
int rightToUpdate() const;
void animateToLeft(not_null<Thumb*> next);
void animateToRight(not_null<Thumb*> prev);
void setState(State state);
State state() const;
bool removed() const;
void paint(Painter &p, int x, int y, int outerWidth, float64 progress);
ClickHandlerPtr getState(QPoint point) const;
private:
QSize wantedPixSize() const;
void validateImage();
int currentLeft() const;
int currentWidth() const;
int finalLeft() const;
int finalWidth() const;
void animateTo(int left, int width);
ClickHandlerPtr _link;
const Key _key;
ImagePtr _image;
State _state = State::Alive;
QPixmap _full;
int _fullWidth = 0;
bool _hiding = false;
anim::value _left = { 0. };
anim::value _width = { 0. };
anim::value _opacity = { 0., 1. };
};
GroupThumbs::Thumb::Thumb(
Key key,
ImagePtr image,
base::lambda<void()> handler)
: _key(key)
, _image(image) {
_link = std::make_shared<LambdaClickHandler>(std::move(handler));
_fullWidth = std::min(
wantedPixSize().width(),
st::mediaviewGroupWidthMax);
validateImage();
}
QSize GroupThumbs::Thumb::wantedPixSize() const {
const auto originalWidth = std::max(_image->width(), 1);
const auto originalHeight = std::max(_image->height(), 1);
const auto pixHeight = st::mediaviewGroupHeight;
const auto pixWidth = originalWidth * pixHeight / originalHeight;
return { pixWidth, pixHeight };
}
void GroupThumbs::Thumb::validateImage() {
if (!_full.isNull()) {
return;
}
_image->load();
if (!_image->loaded()) {
return;
}
const auto pixSize = wantedPixSize();
if (pixSize.width() > st::mediaviewGroupWidthMax) {
const auto originalWidth = _image->width();
const auto originalHeight = _image->height();
const auto takeWidth = originalWidth * st::mediaviewGroupWidthMax
/ pixSize.width();
const auto original = _image->pixNoCache().toImage();
_full = App::pixmapFromImageInPlace(original.copy(
(originalWidth - takeWidth) / 2,
0,
takeWidth,
originalHeight
).scaled(
st::mediaviewGroupWidthMax * cIntRetinaFactor(),
pixSize.height() * cIntRetinaFactor(),
Qt::IgnoreAspectRatio,
Qt::SmoothTransformation));
} else {
_full = _image->pixNoCache(
pixSize.width() * cIntRetinaFactor(),
pixSize.height() * cIntRetinaFactor(),
Images::Option::Smooth);
}
}
int GroupThumbs::Thumb::leftToUpdate() const {
return Round(std::min(_left.from(), _left.to()));
}
int GroupThumbs::Thumb::rightToUpdate() const {
return Round(std::max(
_left.from() + _width.from(),
_left.to() + _width.to()));
}
int GroupThumbs::Thumb::currentLeft() const {
return Round(_left.current());
}
int GroupThumbs::Thumb::currentWidth() const {
return Round(_width.current());
}
int GroupThumbs::Thumb::finalLeft() const {
return Round(_left.to());
}
int GroupThumbs::Thumb::finalWidth() const {
return Round(_width.to());
}
void GroupThumbs::Thumb::setState(State state) {
const auto isNewThumb = (_state == State::Alive);
_state = state;
if (_state == State::Current) {
if (isNewThumb) {
_opacity = anim::value(1.);
_left = anim::value(-_fullWidth / 2);
_width = anim::value(_fullWidth);
} else {
_opacity.start(1.);
}
_hiding = false;
animateTo(-_fullWidth / 2, _fullWidth);
} else if (_state == State::Alive) {
_opacity.start(0.7);
_hiding = false;
} else if (_state == State::Dying) {
_opacity.start(0.);
_hiding = true;
_left.restart();
_width.restart();
}
}
void GroupThumbs::Thumb::animateTo(int left, int width) {
_left.start(left);
_width.start(width);
}
void GroupThumbs::Thumb::animateToLeft(not_null<Thumb*> next) {
const auto width = st::mediaviewGroupWidth;
if (_state == State::Alive) {
// New item animation, start exactly from the next, move only.
_left = anim::value(next->currentLeft() - width);
_width = anim::value(width);
} else if (_state == State::Unknown) {
// Existing item animation.
setState(State::Alive);
}
const auto skip1 = st::mediaviewGroupSkip;
const auto skip2 = (next->state() == State::Current)
? st::mediaviewGroupSkipCurrent
: st::mediaviewGroupSkip;
animateTo(next->finalLeft() - width - skip1 - skip2, width);
}
void GroupThumbs::Thumb::animateToRight(not_null<Thumb*> prev) {
const auto width = st::mediaviewGroupWidth;
if (_state == State::Alive) {
// New item animation, start exactly from the next, move only.
_left = anim::value(prev->currentLeft() + prev->currentWidth());
_width = anim::value(width);
} else if (_state == State::Unknown) {
// Existing item animation.
setState(State::Alive);
}
const auto skip1 = st::mediaviewGroupSkip;
const auto skip2 = (prev->state() == State::Current)
? st::mediaviewGroupSkipCurrent
: st::mediaviewGroupSkip;
animateTo(prev->finalLeft() + prev->finalWidth() + skip1 + skip2, width);
}
auto GroupThumbs::Thumb::state() const -> State {
return _state;
}
bool GroupThumbs::Thumb::removed() const {
return (_state == State::Dying) && _hiding && !_opacity.current();
}
void GroupThumbs::Thumb::paint(
Painter &p,
int x,
int y,
int outerWidth,
float64 progress) {
validateImage();
_opacity.update(progress, anim::linear);
_left.update(progress, anim::linear);
_width.update(progress, anim::linear);
const auto left = x + currentLeft();
const auto width = currentWidth();
const auto opacity = p.opacity();
p.setOpacity(_opacity.current() * opacity);
if (width == _fullWidth) {
p.drawPixmap(left, y, _full);
} else {
const auto takeWidth = width * cIntRetinaFactor();
const auto from = QRect(
(_full.width() - takeWidth) / 2,
0,
takeWidth,
_full.height());
const auto to = QRect(left, y, width, st::mediaviewGroupHeight);
p.drawPixmap(to, _full, from);
}
p.setOpacity(opacity);
}
ClickHandlerPtr GroupThumbs::Thumb::getState(QPoint point) const {
if (_state != State::Alive) {
return nullptr;
}
const auto left = finalLeft();
const auto width = finalWidth();
return QRect(left, 0, width, st::mediaviewGroupHeight).contains(point)
? _link
: nullptr;
}
GroupThumbs::GroupThumbs(Context context)
: _context(context) {
}
void GroupThumbs::updateContext(Context context) {
if (_context != context) {
clear();
_context = context;
}
}
template <typename Slice>
void GroupThumbs::RefreshFromSlice(
std::unique_ptr<GroupThumbs> &instance,
const Slice &slice,
int index,
int availableWidth) {
const auto context = ComputeContext(slice, index);
if (instance) {
instance->updateContext(context);
}
if (!context) {
if (instance) {
instance->resizeToWidth(availableWidth);
}
return;
}
const auto limit = ComputeThumbsLimit(availableWidth);
const auto from = [&] {
const auto edge = std::max(index - limit, 0);
for (auto result = index; result != edge; --result) {
if (ComputeContext(slice, result - 1) != context) {
return result;
}
}
return edge;
}();
const auto till = [&] {
const auto edge = std::min(index + limit + 1, slice.size());
for (auto result = index + 1; result != edge; ++result) {
if (ComputeContext(slice, result) != context) {
return result;
}
}
return edge;
}();
if (from + 1 < till) {
if (!instance) {
instance = std::make_unique<GroupThumbs>(context);
}
instance->fillItems(slice, from, index, till);
instance->resizeToWidth(availableWidth);
} else if (instance) {
instance->clear();
instance->resizeToWidth(availableWidth);
}
}
template <typename Slice>
void GroupThumbs::fillItems(
const Slice &slice,
int from,
int index,
int till) {
Expects(from <= index);
Expects(index < till);
Expects(from + 1 < till);
const auto current = (index - from);
const auto old = base::take(_items);
markCacheStale();
_items.reserve(till - from);
for (auto i = from; i != till; ++i) {
_items.push_back(validateCacheEntry(ComputeKey(slice, i)));
}
animateAliveItems(current);
fillDyingItems(old);
startDelayedAnimation();
}
void GroupThumbs::animateAliveItems(int current) {
Expects(current >= 0 && current < _items.size());
_items[current]->setState(Thumb::State::Current);
for (auto i = current; i != 0;) {
const auto prev = _items[i];
const auto item = _items[--i];
item->animateToLeft(prev);
}
for (auto i = current + 1; i != _items.size(); ++i) {
const auto prev = _items[i - 1];
const auto item = _items[i];
item->animateToRight(prev);
}
}
void GroupThumbs::fillDyingItems(const std::vector<not_null<Thumb*>> &old) {
_dying.reserve(_cache.size() - _items.size());
animatePreviouslyAlive(old);
markRestAsDying();
}
void GroupThumbs::markRestAsDying() {
_dying.reserve(_cache.size() - _items.size());
for (const auto &cacheItem : _cache) {
const auto &thumb = cacheItem.second;
const auto state = thumb->state();
if (state == Thumb::State::Unknown) {
markAsDying(thumb.get());
}
}
}
void GroupThumbs::markAsDying(not_null<Thumb*> thumb) {
thumb->setState(Thumb::State::Dying);
_dying.push_back(thumb.get());
}
void GroupThumbs::animatePreviouslyAlive(
const std::vector<not_null<Thumb*>> &old) {
auto toRight = false;
for (auto i = 0; i != old.size(); ++i) {
const auto item = old[i];
if (item->state() == Thumb::State::Unknown) {
if (toRight) {
markAsDying(item);
item->animateToRight(old[i - 1]);
}
} else if (!toRight) {
for (auto j = i; j != 0;) {
const auto next = old[j];
const auto prev = old[--j];
markAsDying(prev);
prev->animateToLeft(next);
}
toRight = true;
}
}
}
auto GroupThumbs::createThumb(Key key) -> std::unique_ptr<Thumb> {
if (const auto photoId = base::get_if<PhotoId>(&key)) {
const auto photo = App::photo(*photoId);
return createThumb(key, photo->date ? photo->thumb : ImagePtr());
} else if (const auto msgId = base::get_if<FullMsgId>(&key)) {
if (const auto item = App::histItemById(*msgId)) {
if (const auto media = item->getMedia()) {
if (const auto photo = media->getPhoto()) {
return createThumb(key, photo->thumb);
} else if (const auto document = media->getDocument()) {
return createThumb(key, document->thumb);
}
}
}
return createThumb(key, ImagePtr());
}
Unexpected("Value of Key in GroupThumbs::createThumb()");
}
auto GroupThumbs::createThumb(Key key, ImagePtr image)
-> std::unique_ptr<Thumb> {
const auto weak = base::make_weak(this);
return std::make_unique<Thumb>(key, image, [=] {
if (const auto strong = weak.get()) {
strong->_activateStream.fire_copy(key);
}
});
}
auto GroupThumbs::validateCacheEntry(Key key) -> not_null<Thumb*> {
const auto i = _cache.find(key);
return (i != _cache.end())
? i->second.get()
: _cache.emplace(key, createThumb(key)).first->second.get();
}
void GroupThumbs::markCacheStale() {
while (!_dying.empty()) {
_dying.pop_back();
}
for (const auto &cacheItem : _cache) {
const auto &thumb = cacheItem.second;
thumb->setState(Thumb::State::Unknown);
}
}
void GroupThumbs::Refresh(
std::unique_ptr<GroupThumbs> &instance,
const SharedMediaWithLastSlice &slice,
int index,
int availableWidth) {
RefreshFromSlice(instance, slice, index, availableWidth);
}
void GroupThumbs::Refresh(
std::unique_ptr<GroupThumbs> &instance,
const UserPhotosSlice &slice,
int index,
int availableWidth) {
RefreshFromSlice(instance, slice, index, availableWidth);
}
void GroupThumbs::clear() {
if (_items.empty()) {
return;
}
base::take(_items);
markCacheStale();
markRestAsDying();
startDelayedAnimation();
}
void GroupThumbs::startDelayedAnimation() {
_animation.finish();
_waitingForAnimationStart = true;
countUpdatedRect();
}
void GroupThumbs::resizeToWidth(int newWidth) {
_width = newWidth;
}
int GroupThumbs::height() const {
return st::mediaviewGroupPadding.top()
+ st::mediaviewGroupHeight
+ st::mediaviewGroupPadding.bottom();
}
bool GroupThumbs::hiding() const {
return _items.empty();
}
bool GroupThumbs::hidden() const {
return hiding() && !_waitingForAnimationStart && !_animation.animating();
}
void GroupThumbs::checkForAnimationStart() {
if (_waitingForAnimationStart) {
_waitingForAnimationStart = false;
_animation.start([this] { update(); }, 0., 1., kThumbDuration);
}
}
void GroupThumbs::update() {
if (_cache.empty()) {
return;
}
_updateRequests.fire_copy(_updatedRect);
}
void GroupThumbs::paint(
Painter &p,
int x,
int y,
int outerWidth,
TimeMs ms) {
const auto progress = _waitingForAnimationStart
? 0.
: _animation.current(ms, 1.);
x += (_width / 2);
y += st::mediaviewGroupPadding.top();
for (auto i = _cache.begin(); i != _cache.end();) {
const auto &thumb = i->second;
thumb->paint(p, x, y, outerWidth, progress);
if (thumb->removed()) {
_dying.erase(
ranges::remove(
_dying,
thumb.get(),
[](not_null<Thumb*> thumb) { return thumb.get(); }),
_dying.end());
i = _cache.erase(i);
} else {
++i;
}
}
}
ClickHandlerPtr GroupThumbs::getState(QPoint point) const {
point -= QPoint((_width / 2), st::mediaviewGroupPadding.top());
for (const auto &cacheItem : _cache) {
const auto &thumb = cacheItem.second;
if (auto link = thumb->getState(point)) {
return link;
}
}
return nullptr;
}
void GroupThumbs::countUpdatedRect() {
if (_cache.empty()) {
return;
}
auto min = _width;
auto max = 0;
const auto left = [](const auto &cacheItem) {
const auto &[key, thumb] = cacheItem;
return thumb->leftToUpdate();
};
const auto right = [](const auto &cacheItem) {
const auto &[key, thumb] = cacheItem;
return thumb->rightToUpdate();
};
accumulate_min(min, left(*ranges::max_element(
_cache,
std::greater<>(),
left)));
accumulate_max(max, right(*ranges::max_element(
_cache,
std::less<>(),
right)));
_updatedRect = QRect(
min,
st::mediaviewGroupPadding.top(),
max - min,
st::mediaviewGroupHeight);
}
GroupThumbs::~GroupThumbs() = default;
} // namespace View
} // namespace Media

View File

@@ -0,0 +1,116 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "history/history_item_components.h"
#include "base/weak_ptr.h"
class SharedMediaWithLastSlice;
class UserPhotosSlice;
namespace Media {
namespace View {
class GroupThumbs : public base::has_weak_ptr {
public:
using Key = base::variant<PhotoId, FullMsgId>;
static void Refresh(
std::unique_ptr<GroupThumbs> &instance,
const SharedMediaWithLastSlice &slice,
int index,
int availableWidth);
static void Refresh(
std::unique_ptr<GroupThumbs> &instance,
const UserPhotosSlice &slice,
int index,
int availableWidth);
void clear();
void resizeToWidth(int newWidth);
int height() const;
bool hiding() const;
bool hidden() const;
void checkForAnimationStart();
void paint(Painter &p, int x, int y, int outerWidth, TimeMs ms);
ClickHandlerPtr getState(QPoint point) const;
rpl::producer<QRect> updateRequests() const {
return _updateRequests.events();
}
rpl::producer<Key> activateRequests() const {
return _activateStream.events();
}
rpl::lifetime &lifetime() {
return _lifetime;
}
using Context = base::optional_variant<PeerId, MessageGroupId>;
GroupThumbs(Context context);
~GroupThumbs();
private:
class Thumb;
template <typename Slice>
static void RefreshFromSlice(
std::unique_ptr<GroupThumbs> &instance,
const Slice &slice,
int index,
int availableWidth);
template <typename Slice>
void fillItems(const Slice &slice, int from, int index, int till);
void updateContext(Context context);
void markCacheStale();
not_null<Thumb*> validateCacheEntry(Key key);
std::unique_ptr<Thumb> createThumb(Key key);
std::unique_ptr<Thumb> createThumb(Key key, ImagePtr image);
void update();
void countUpdatedRect();
void animateAliveItems(int current);
void fillDyingItems(const std::vector<not_null<Thumb*>> &old);
void markAsDying(not_null<Thumb*> thumb);
void markRestAsDying();
void animatePreviouslyAlive(const std::vector<not_null<Thumb*>> &old);
void startDelayedAnimation();
Context _context;
bool _waitingForAnimationStart = true;
Animation _animation;
std::vector<not_null<Thumb*>> _items;
std::vector<not_null<Thumb*>> _dying;
base::flat_map<Key, std::unique_ptr<Thumb>> _cache;
int _width = 0;
QRect _updatedRect;
rpl::event_stream<QRect> _updateRequests;
rpl::event_stream<Key> _activateStream;
rpl::lifetime _lifetime;
};
} // namespace View
} // namespace Media

View File

@@ -196,6 +196,13 @@ mediaviewCaptionPadding: margins(18px, 10px, 18px, 10px);
mediaviewCaptionMargin: size(11px, 11px);
mediaviewCaptionRadius: 2px;
mediaviewGroupPadding: margins(0px, 14px, 0px, 14px);
mediaviewGroupHeight: 80px;
mediaviewGroupWidth: 56px;
mediaviewGroupWidthMax: 160px;
mediaviewGroupSkip: 3px;
mediaviewGroupSkipCurrent: 12px;
themePreviewSize: size(903px, 584px);
themePreviewBg: windowBg;
themePreviewOverlayOpacity: 0.8;

View File

@@ -27,10 +27,10 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "core/file_utilities.h"
#include "ui/widgets/popup_menu.h"
#include "ui/widgets/buttons.h"
#include "ui/text_options.h"
#include "media/media_clip_reader.h"
#include "media/view/media_clip_controller.h"
#include "styles/style_mediaview.h"
#include "styles/style_history.h"
#include "media/view/media_view_group_thumbs.h"
#include "media/media_audio.h"
#include "history/history_message.h"
#include "history/history_media_types.h"
@@ -42,26 +42,15 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "messenger.h"
#include "storage/file_download.h"
#include "calls/calls_instance.h"
#include "styles/style_mediaview.h"
#include "styles/style_history.h"
namespace {
constexpr auto kPreloadCount = 4;
TextParseOptions _captionTextOptions = {
TextParseLinks | TextParseMentions | TextParseHashtags | TextParseMultiline | TextParseRichText, // flags
0, // maxw
0, // maxh
Qt::LayoutDirectionAuto, // dir
};
TextParseOptions _captionBotOptions = {
TextParseLinks | TextParseMentions | TextParseHashtags | TextParseMultiline | TextParseRichText | TextParseBotCommands, // flags
0, // maxw
0, // maxh
Qt::LayoutDirectionAuto, // dir
};
// Preload X message ids before and after current.
constexpr auto kIdsLimit = 32;
constexpr auto kIdsLimit = 48;
// Preload next messages if we went further from current than that.
constexpr auto kIdsPreloadAfter = 28;
@@ -84,7 +73,8 @@ struct MediaView::UserPhotos {
rpl::lifetime lifetime;
};
MediaView::MediaView() : TWidget(nullptr)
MediaView::MediaView()
: TWidget(nullptr)
, _transparentBrush(style::transparentPlaceholderBrush())
, _animStarted(getms())
, _docDownload(this, lang(lng_media_download), st::mediaviewFileLink)
@@ -99,7 +89,7 @@ MediaView::MediaView() : TWidget(nullptr)
TextCustomTagsMap custom;
custom.insert(QChar('c'), qMakePair(textcmdStartLink(1), textcmdStopLink()));
_saveMsgText.setRichText(st::mediaviewSaveMsgStyle, lang(lng_mediaview_saved), _textDlgOptions, custom);
_saveMsgText.setRichText(st::mediaviewSaveMsgStyle, lang(lng_mediaview_saved), Ui::DialogTextOptions(), custom);
_saveMsg = QRect(0, 0, _saveMsgText.maxWidth() + st::mediaviewSaveMsgPadding.left() + st::mediaviewSaveMsgPadding.right(), st::mediaviewSaveMsgStyle.font->height + st::mediaviewSaveMsgPadding.top() + st::mediaviewSaveMsgPadding.bottom());
_saveMsgText.setLink(1, std::make_shared<LambdaClickHandler>([this] { showSaveMsgFile(); }));
@@ -211,12 +201,22 @@ bool MediaView::fileBubbleShown() const {
bool MediaView::gifShown() const {
if (_gif && _gif->ready()) {
if (!_gif->started()) {
if (_doc && (_doc->isVideoFile() || _doc->isVideoMessage()) && _autoplayVideoDocument != _doc && !_gif->videoPaused()) {
_gif->pauseResumeVideo();
const_cast<MediaView*>(this)->_videoPaused = _gif->videoPaused();
const auto streamVideo = _doc
&& (_doc->isVideoFile() || _doc->isVideoMessage());
const auto pauseOnStart = (_autoplayVideoDocument != _doc);
if (streamVideo && pauseOnStart && !_gif->videoPaused()) {
const_cast<MediaView*>(this)->toggleVideoPaused();
}
auto rounding = (_doc && _doc->isVideoMessage()) ? ImageRoundRadius::Ellipse : ImageRoundRadius::None;
_gif->start(_gif->width() / cIntRetinaFactor(), _gif->height() / cIntRetinaFactor(), _gif->width() / cIntRetinaFactor(), _gif->height() / cIntRetinaFactor(), rounding, RectPart::AllCorners);
const auto rounding = (_doc && _doc->isVideoMessage())
? ImageRoundRadius::Ellipse
: ImageRoundRadius::None;
_gif->start(
_gif->width() / cIntRetinaFactor(),
_gif->height() / cIntRetinaFactor(),
_gif->width() / cIntRetinaFactor(),
_gif->height() / cIntRetinaFactor(),
rounding,
RectPart::AllCorners);
const_cast<MediaView*>(this)->_current = QPixmap();
updateMixerVideoVolume();
Global::RefVideoVolumeChanged().notify();
@@ -351,7 +351,7 @@ void MediaView::updateControls() {
_dateText = lng_mediaview_date_time(lt_date, d.date().toString(qsl("dd.MM.yy")), lt_time, d.time().toString(cTimeFormat()));
}
if (_from) {
_fromName.setText(st::mediaviewTextStyle, (_from->migrateTo() ? _from->migrateTo() : _from)->name, _textNameOptions);
_fromName.setText(st::mediaviewTextStyle, (_from->migrateTo() ? _from->migrateTo() : _from)->name, Ui::NameTextOptions());
_nameNav = myrtlrect(st::mediaviewTextLeft, height() - st::mediaviewTextTop, qMin(_fromName.maxWidth(), width() / 3), st::mediaviewFont->height);
_dateNav = myrtlrect(st::mediaviewTextLeft + _nameNav.width() + st::mediaviewTextSkip, height() - st::mediaviewTextTop, st::mediaviewFont->width(_dateText), st::mediaviewFont->height);
} else {
@@ -360,22 +360,65 @@ void MediaView::updateControls() {
}
updateHeader();
refreshNavVisibility();
resizeCenteredControls();
if (!_caption.isEmpty()) {
int32 skipw = qMax(_dateNav.left() + _dateNav.width(), _headerNav.left() + _headerNav.width());
int32 maxw = qMin(qMax(width() - 2 * skipw - st::mediaviewCaptionPadding.left() - st::mediaviewCaptionPadding.right() - 2 * st::mediaviewCaptionMargin.width(), int(st::msgMinWidth)), _caption.maxWidth());
int32 maxh = qMin(_caption.countHeight(maxw), int(height() / 4 - st::mediaviewCaptionPadding.top() - st::mediaviewCaptionPadding.bottom() - 2 * st::mediaviewCaptionMargin.height()));
_captionRect = QRect((width() - maxw) / 2, height() - maxh - st::mediaviewCaptionPadding.bottom() - st::mediaviewCaptionMargin.height(), maxw, maxh);
} else {
_captionRect = QRect();
}
if (_clipController) {
setClipControllerGeometry();
}
updateOver(mapFromGlobal(QCursor::pos()));
update();
}
void MediaView::resizeCenteredControls() {
const auto bottomSkip = std::max(
_dateNav.left() + _dateNav.width(),
_headerNav.left() + _headerNav.width())
+ st::mediaviewCaptionMargin.width();
_groupThumbsAvailableWidth = std::max(
width() - 2 * bottomSkip,
st::msgMinWidth
+ st::mediaviewCaptionPadding.left()
+ st::mediaviewCaptionPadding.right());
_groupThumbsLeft = (width() - _groupThumbsAvailableWidth) / 2;
refreshGroupThumbs();
_groupThumbsTop = _groupThumbs ? (height() - _groupThumbs->height()) : 0;
refreshClipControllerGeometry();
refreshCaptionGeometry();
}
void MediaView::refreshCaptionGeometry() {
if (_caption.isEmpty()) {
_captionRect = QRect();
return;
}
if (_groupThumbs && _groupThumbs->hiding()) {
_groupThumbs = nullptr;
_groupThumbsRect = QRect();
}
const auto captionBottom = _clipController
? (_clipController->y() - st::mediaviewCaptionMargin.height())
: _groupThumbs
? _groupThumbsTop
: height() - st::mediaviewCaptionMargin.height();
const auto captionWidth = std::min(
_groupThumbsAvailableWidth
- st::mediaviewCaptionPadding.left()
- st::mediaviewCaptionPadding.right(),
_caption.maxWidth());
const auto captionHeight = std::min(
_caption.countHeight(captionWidth),
height() / 4
- st::mediaviewCaptionPadding.top()
- st::mediaviewCaptionPadding.bottom()
- 2 * st::mediaviewCaptionMargin.height());
_captionRect = QRect(
(width() - captionWidth) / 2,
captionBottom
- captionHeight
- st::mediaviewCaptionPadding.bottom(),
captionWidth,
captionHeight);
}
void MediaView::updateActions() {
_actions.clear();
@@ -413,13 +456,27 @@ void MediaView::updateActions() {
}
_actions.push_back({ lang(lng_mediaview_save_as), SLOT(onSaveAs()) });
if (auto overviewType =
sharedMediaType()
| SharedMediaOverviewType) {
if (const auto overviewType = computeOverviewType()) {
_actions.push_back({ lang(_doc ? lng_mediaview_files_all : lng_mediaview_photos_all), SLOT(onOverview()) });
}
}
auto MediaView::computeOverviewType() const
-> base::optional<SharedMediaType> {
if (const auto mediaType = sharedMediaType()) {
if (const auto overviewType = SharedMediaOverviewType(*mediaType)) {
return overviewType;
} else if (mediaType == SharedMediaType::PhotoVideo) {
if (_photo) {
return SharedMediaOverviewType(SharedMediaType::Photo);
} else if (_doc) {
return SharedMediaOverviewType(SharedMediaType::Video);
}
}
}
return base::none;
}
void MediaView::step_state(TimeMs ms, bool timer) {
bool result = false;
for (Showing::iterator i = _animations.begin(); i != _animations.end();) {
@@ -454,7 +511,17 @@ void MediaView::step_state(TimeMs ms, bool timer) {
} else {
a_cOpacity.update(dt, anim::linear);
}
QRegion toUpdate = QRegion() + (_over == OverLeftNav ? _leftNav : _leftNavIcon) + (_over == OverRightNav ? _rightNav : _rightNavIcon) + (_over == OverClose ? _closeNav : _closeNavIcon) + _saveNavIcon + _moreNavIcon + _headerNav + _nameNav + _dateNav + _captionRect.marginsAdded(st::mediaviewCaptionPadding);
const auto toUpdate = QRegion()
+ (_over == OverLeftNav ? _leftNav : _leftNavIcon)
+ (_over == OverRightNav ? _rightNav : _rightNavIcon)
+ (_over == OverClose ? _closeNav : _closeNavIcon)
+ _saveNavIcon
+ _moreNavIcon
+ _headerNav
+ _nameNav
+ _dateNav
+ _captionRect.marginsAdded(st::mediaviewCaptionPadding)
+ _groupThumbsRect;
update(toUpdate);
if (dt < 1) result = true;
}
@@ -464,7 +531,9 @@ void MediaView::step_state(TimeMs ms, bool timer) {
}
void MediaView::updateCursor() {
setCursor(_controlsState == ControlsHidden ? Qt::BlankCursor : (_over == OverNone ? style::cur_default : style::cur_pointer));
setCursor(_controlsState == ControlsHidden
? Qt::BlankCursor
: (_over == OverNone ? style::cur_default : style::cur_pointer));
}
float64 MediaView::radialProgress() const {
@@ -512,21 +581,26 @@ void MediaView::step_radial(TimeMs ms, bool timer) {
_radial.stop();
return;
}
auto wasAnimating = _radial.animating();
const auto wasAnimating = _radial.animating();
_radial.update(radialProgress(), !radialLoading(), ms + radialTimeShift());
if (timer && (wasAnimating || _radial.animating())) {
update(radialRect());
}
if (_doc && _doc->loaded() && _doc->size < App::kImageSizeLimit && (!_radial.animating() || _doc->isAnimation() || _doc->isVideoFile())) {
const auto ready = _doc && _doc->loaded();
const auto streamVideo = ready && (_doc->isAnimation() || _doc->isVideoFile());
const auto tryOpenImage = ready && (_doc->size < App::kImageSizeLimit);
if (ready && ((tryOpenImage && !_radial.animating()) || streamVideo)) {
if (_doc->isVideoFile() || _doc->isVideoMessage()) {
_autoplayVideoDocument = _doc;
}
if (!_doc->data().isEmpty() && (_doc->isAnimation() || _doc->isVideoFile())) {
if (!_doc->data().isEmpty() && streamVideo) {
displayDocument(_doc, App::histItemById(_msgid));
} else {
auto &location = _doc->location(true);
if (location.accessEnable()) {
if (_doc->isAnimation() || _doc->isVideoFile() || _doc->isTheme() || QImageReader(location.name()).canRead()) {
if (streamVideo
|| _doc->isTheme()
|| QImageReader(location.name()).canRead()) {
displayDocument(_doc, App::histItemById(_msgid));
}
location.accessDisable();
@@ -684,6 +758,7 @@ void MediaView::onHideControls(bool force) {
}
if (_controlsState == ControlsHiding || _controlsState == ControlsHidden) return;
_lastMouseMovePos = mapFromGlobal(QCursor::pos());
_controlsState = ControlsHiding;
_controlsAnimStarted = getms();
a_cOpacity.start(0);
@@ -963,9 +1038,7 @@ void MediaView::onDelete() {
void MediaView::onOverview() {
if (_menu) _menu->hideMenu(true);
update();
if (auto overviewType =
sharedMediaType()
| SharedMediaOverviewType) {
if (const auto overviewType = computeOverviewType()) {
close();
SharedMediaShowOverview(*overviewType, _history);
}
@@ -991,14 +1064,14 @@ base::optional<MediaView::SharedMediaType> MediaView::sharedMediaType() const {
if (auto item = App::histItemById(_msgid)) {
if (_photo) {
if (item->toHistoryMessage()) {
return Type::Photo;
return Type::PhotoVideo;
}
return Type::ChatPhoto;
} else if (_doc) {
if (_doc->isGifv()) {
return Type::GIF;
} else if (_doc->isVideoFile()) {
return Type::Video;
return Type::PhotoVideo;
}
return Type::File;
}
@@ -1171,6 +1244,85 @@ void MediaView::refreshMediaViewer() {
preloadData(0);
}
void MediaView::refreshCaption(HistoryItem *item) {
_caption = Text();
const auto media = item ? item->getMedia() : nullptr;
if (!media) {
return;
}
const auto caption = media->getCaption();
if (caption.text.isEmpty()) {
return;
}
const auto asBot = [&] {
if (const auto author = item->author()->asUser()) {
return author->botInfo != nullptr;
}
return false;
}();
_caption = Text(st::msgMinWidth);
_caption.setMarkedText(
st::mediaviewCaptionStyle,
caption,
Ui::ItemTextOptions(item));
}
void MediaView::refreshGroupThumbs() {
const auto existed = (_groupThumbs != nullptr);
if (_index && _sharedMediaData) {
Media::View::GroupThumbs::Refresh(
_groupThumbs,
*_sharedMediaData,
*_index,
_groupThumbsAvailableWidth);
} else if (_index && _userPhotosData) {
Media::View::GroupThumbs::Refresh(
_groupThumbs,
*_userPhotosData,
*_index,
_groupThumbsAvailableWidth);
} else if (_groupThumbs) {
_groupThumbs->clear();
_groupThumbs->resizeToWidth(_groupThumbsAvailableWidth);
}
if (_groupThumbs && !existed) {
initGroupThumbs();
}
}
void MediaView::initGroupThumbs() {
Expects(_groupThumbs != nullptr);
_groupThumbs->updateRequests(
) | rpl::start_with_next([this](QRect rect) {
const auto shift = (width() / 2);
_groupThumbsRect = QRect(
shift + rect.x(),
_groupThumbsTop,
rect.width(),
_groupThumbs->height());
update(_groupThumbsRect);
}, _groupThumbs->lifetime());
_groupThumbs->activateRequests(
) | rpl::start_with_next([this](Media::View::GroupThumbs::Key key) {
if (const auto photoId = base::get_if<PhotoId>(&key)) {
const auto photo = App::photo(*photoId);
moveToEntity({ photo, nullptr });
} else if (const auto itemId = base::get_if<FullMsgId>(&key)) {
moveToEntity(entityForItemId(*itemId));
}
}, _groupThumbs->lifetime());
_groupThumbsRect = QRect(
_groupThumbsLeft,
_groupThumbsTop,
width() - 2 * _groupThumbsLeft,
height() - _groupThumbsTop);
}
void MediaView::showPhoto(not_null<PhotoData*> photo, HistoryItem *context) {
if (context) {
setContext(context);
@@ -1254,7 +1406,7 @@ void MediaView::showDocument(not_null<DocumentData*> document, HistoryItem *cont
void MediaView::displayPhoto(not_null<PhotoData*> photo, HistoryItem *item) {
stopGif();
destroyThemePreview();
_doc = nullptr;
_doc = _autoplayVideoDocument = nullptr;
_fullScreenVideo = false;
_photo = photo;
_radial.stop();
@@ -1265,21 +1417,7 @@ void MediaView::displayPhoto(not_null<PhotoData*> photo, HistoryItem *item) {
_zoom = 0;
_caption = Text();
if (const auto media = item ? item->getMedia() : nullptr) {
const auto caption = media->getCaption();
if (!caption.text.isEmpty()) {
auto asBot = (item->author()->isUser()
&& item->author()->asUser()->botInfo);
auto skipw = qMax(_dateNav.left() + _dateNav.width(), _headerNav.left() + _headerNav.width());
auto maxw = qMin(qMax(width() - 2 * skipw - st::mediaviewCaptionPadding.left() - st::mediaviewCaptionPadding.right() - 2 * st::mediaviewCaptionMargin.width(), int(st::msgMinWidth)), _caption.maxWidth());
_caption = Text(maxw);
_caption.setMarkedText(
st::mediaviewCaptionStyle,
caption,
itemTextOptions(item));
}
}
refreshCaption(item);
_zoomToScreen = 0;
Auth().downloader().clearPriorities();
@@ -1341,7 +1479,9 @@ void MediaView::displayDocument(DocumentData *doc, HistoryItem *item) { // empty
_autoplayVideoDocument = nullptr;
}
_caption = Text();
if (documentChanged) {
refreshCaption(item);
}
if (_doc) {
if (_doc->sticker()) {
_doc->checkSticker();
@@ -1611,7 +1751,7 @@ void MediaView::createClipController() {
if (!_doc->isVideoFile() && !_doc->isVideoMessage()) return;
_clipController.create(this);
setClipControllerGeometry();
refreshClipControllerGeometry();
_clipController->show();
connect(_clipController, SIGNAL(playPressed()), this, SLOT(onVideoPauseResume()));
@@ -1625,10 +1765,18 @@ void MediaView::createClipController() {
connect(Media::Player::mixer(), SIGNAL(updated(const AudioMsgId&)), this, SLOT(onVideoPlayProgress(const AudioMsgId&)));
}
void MediaView::setClipControllerGeometry() {
Assert(_clipController != nullptr);
void MediaView::refreshClipControllerGeometry() {
if (!_clipController) {
return;
}
int controllerBottom = _captionRect.isEmpty() ? height() : _captionRect.y();
if (_groupThumbs && _groupThumbs->hiding()) {
_groupThumbs = nullptr;
_groupThumbsRect = QRect();
}
const auto controllerBottom = _groupThumbs
? _groupThumbsTop
: height();
_clipController->setGeometry(
(width() - _clipController->width()) / 2,
controllerBottom - _clipController->height() - st::mediaviewCaptionPadding.bottom() - st::mediaviewCaptionMargin.height(),
@@ -1646,11 +1794,7 @@ void MediaView::onVideoPauseResume() {
} else if (_gif->state() == Media::Clip::State::Finished) {
restartVideoAtSeekPosition(0);
} else {
_gif->pauseResumeVideo();
_videoPaused = _gif->videoPaused();
if (_videoIsSilent) {
updateSilentVideoPlaybackState();
}
toggleVideoPaused();
}
} else {
stopGif();
@@ -1659,6 +1803,14 @@ void MediaView::onVideoPauseResume() {
}
}
void MediaView::toggleVideoPaused() {
_gif->pauseResumeVideo();
_videoPaused = _gif->videoPaused();
if (_videoIsSilent) {
updateSilentVideoPlaybackState();
}
}
void MediaView::restartVideoAtSeekPosition(TimeMs positionMs) {
_autoplayVideoDocument = _doc;
@@ -2045,6 +2197,27 @@ void MediaView::paintEvent(QPaintEvent *e) {
}
}
}
if (_groupThumbs && _groupThumbsRect.intersects(r)) {
p.setOpacity(co);
_groupThumbs->paint(
p,
_groupThumbsLeft,
_groupThumbsTop,
width(),
ms);
if (_groupThumbs->hidden()) {
_groupThumbs = nullptr;
_groupThumbsRect = QRect();
}
}
}
checkGroupThumbsAnimation();
}
void MediaView::checkGroupThumbsAnimation() {
if (_groupThumbs && (!_gif || _gif->started())) {
_groupThumbs->checkForAnimationStart();
}
}
@@ -2157,8 +2330,14 @@ void MediaView::keyPressEvent(QKeyEvent *e) {
onVideoPauseResume();
}
} else if (e->key() == Qt::Key_Left) {
if (_controlsHideTimer.isActive()) {
activateControls();
}
moveToNext(-1);
} else if (e->key() == Qt::Key_Right) {
if (_controlsHideTimer.isActive()) {
activateControls();
}
moveToNext(1);
} else if (e->modifiers().testFlag(Qt::ControlModifier) && (e->key() == Qt::Key_Plus || e->key() == Qt::Key_Equal || e->key() == ']' || e->key() == Qt::Key_Asterisk || e->key() == Qt::Key_Minus || e->key() == Qt::Key_Underscore || e->key() == Qt::Key_0)) {
if (e->key() == Qt::Key_Plus || e->key() == Qt::Key_Equal || e->key() == Qt::Key_Asterisk || e->key() == ']') {
@@ -2259,16 +2438,21 @@ MediaView::Entity MediaView::entityForSharedMedia(int index) const {
// Last peer photo.
return { *photo, nullptr };
} else if (const auto itemId = base::get_if<FullMsgId>(&value)) {
if (const auto item = App::histItemById(*itemId)) {
if (const auto media = item->getMedia()) {
if (const auto photo = media->getPhoto()) {
return { photo, item };
} else if (const auto document = media->getDocument()) {
return { document, item };
}
return entityForItemId(*itemId);
}
return { base::none, nullptr };
}
MediaView::Entity MediaView::entityForItemId(const FullMsgId &itemId) const {
if (const auto item = App::histItemById(itemId)) {
if (const auto media = item->getMedia()) {
if (const auto photo = media->getPhoto()) {
return { photo, item };
} else if (const auto document = media->getDocument()) {
return { document, item };
}
return { base::none, item };
}
return { base::none, item };
}
return { base::none, nullptr };
}
@@ -2319,12 +2503,14 @@ bool MediaView::moveToNext(int delta) {
return false;
}
auto newIndex = *_index + delta;
auto entity = entityByIndex(newIndex);
return moveToEntity(entityByIndex(newIndex));
}
bool MediaView::moveToEntity(const Entity &entity, int preloadDelta) {
if (!entity.data && !entity.item) {
return false;
}
_index = newIndex;
if (auto item = entity.item) {
if (const auto item = entity.item) {
setContext(item);
} else if (_peer) {
setContext(_peer);
@@ -2339,7 +2525,7 @@ bool MediaView::moveToNext(int delta) {
} else {
displayDocument(nullptr, entity.item);
}
preloadData(delta);
preloadData(preloadDelta);
return true;
}
@@ -2533,8 +2719,13 @@ void MediaView::updateOver(QPoint pos) {
auto textState = _caption.getState(pos - _captionRect.topLeft(), _captionRect.width());
lnk = textState.link;
lnkhost = this;
} else if (_groupThumbs && _groupThumbsRect.contains(pos)) {
const auto point = pos - QPoint(_groupThumbsLeft, _groupThumbsTop);
lnk = _groupThumbs->getState(point);
lnkhost = this;
}
// retina
if (pos.x() == width()) {
pos.setX(pos.x() - 1);
@@ -2629,7 +2820,9 @@ void MediaView::mouseReleaseEvent(QMouseEvent *e) {
_pressed = false;
}
_down = OverNone;
activateControls();
if (!isHidden()) {
activateControls();
}
}
void MediaView::contextMenuEvent(QContextMenuEvent *e) {
@@ -2738,10 +2931,13 @@ bool MediaView::eventFilter(QObject *obj, QEvent *e) {
auto type = e->type();
if ((type == QEvent::MouseMove || type == QEvent::MouseButtonPress || type == QEvent::MouseButtonRelease) && obj->isWidgetType()) {
if (isAncestorOf(static_cast<QWidget*>(obj))) {
auto mouseEvent = static_cast<QMouseEvent*>(e);
auto mousePosition = mapFromGlobal(mouseEvent->globalPos());
bool activate = (mousePosition != _lastMouseMovePos);
_lastMouseMovePos = mousePosition;
const auto mouseEvent = static_cast<QMouseEvent*>(e);
const auto mousePosition = mapFromGlobal(mouseEvent->globalPos());
const auto delta = (mousePosition - _lastMouseMovePos);
auto activate = delta.manhattanLength() >= st::mediaviewDeltaFromLastAction;
if (activate) {
_lastMouseMovePos = mousePosition;
}
if (type == QEvent::MouseButtonPress) {
_mousePressed = true;
activate = true;
@@ -2749,7 +2945,12 @@ bool MediaView::eventFilter(QObject *obj, QEvent *e) {
_mousePressed = false;
activate = true;
}
if (activate) activateControls();
if (activate) {
if (_controlsState == ControlsHiding || _controlsState == ControlsHidden) {
int a = 0;
}
activateControls();
}
}
}
return TWidget::eventFilter(obj, e);
@@ -2766,6 +2967,8 @@ void MediaView::setVisible(bool visible) {
_controlsHideTimer.stop();
_controlsState = ControlsShown;
a_cOpacity = anim::value(1, 1);
_groupThumbs = nullptr;
_groupThumbsRect = QRect();
}
TWidget::setVisible(visible);
if (visible) {
@@ -2857,9 +3060,7 @@ void MediaView::updateHeader() {
_headerText = lang(lng_mediaview_single_photo);
}
}
_headerHasLink = (
sharedMediaType()
| SharedMediaOverviewType) != base::none;
_headerHasLink = computeOverviewType() != base::none;
auto hwidth = st::mediaviewThickFont->width(_headerText);
if (hwidth > width() / 3) {
hwidth = width() / 3;

View File

@@ -32,6 +32,9 @@ struct TrackState;
namespace Clip {
class Controller;
} // namespace Clip
namespace View {
class GroupThumbs;
} // namespace View
} // namespace Media
namespace Ui {
@@ -50,7 +53,7 @@ namespace Notify {
struct PeerUpdate;
} // namespace Notify
class MediaView : public TWidget, private base::Subscriber, public RPCSender, public ClickHandlerHost {
class MediaView : public TWidget, private base::Subscriber, public ClickHandlerHost {
Q_OBJECT
public:
@@ -158,6 +161,8 @@ private:
Entity entityForUserPhotos(int index) const;
Entity entityForSharedMedia(int index) const;
Entity entityByIndex(int index) const;
Entity entityForItemId(const FullMsgId &itemId) const;
bool moveToEntity(const Entity &entity, int preloadDelta = 0);
void setContext(base::optional_variant<
not_null<HistoryItem*>,
not_null<PeerData*>> context);
@@ -171,6 +176,7 @@ private:
using SharedMediaKey = SharedMediaWithLastSlice::Key;
base::optional<SharedMediaType> sharedMediaType() const;
base::optional<SharedMediaKey> sharedMediaKey() const;
base::optional<SharedMediaType> computeOverviewType() const;
bool validSharedMedia() const;
void validateSharedMedia();
void handleSharedMediaUpdate(SharedMediaWithLastSlice &&update);
@@ -182,13 +188,16 @@ private:
void validateUserPhotos();
void handleUserPhotosUpdate(UserPhotosSlice &&update);
void refreshCaption(HistoryItem *item);
void refreshMediaViewer();
void refreshNavVisibility();
void refreshGroupThumbs();
void dropdownHidden();
void updateDocSize();
void updateControls();
void updateActions();
void resizeCenteredControls();
void displayPhoto(not_null<PhotoData*> photo, HistoryItem *item);
void displayDocument(DocumentData *document, HistoryItem *item);
@@ -201,9 +210,11 @@ private:
void updateVideoPlaybackState(const Media::Player::TrackState &state);
void updateSilentVideoPlaybackState();
void restartVideoAtSeekPosition(TimeMs positionMs);
void toggleVideoPaused();
void createClipController();
void setClipControllerGeometry();
void refreshClipControllerGeometry();
void refreshCaptionGeometry();
void initAnimation();
void createClipReader();
@@ -223,9 +234,6 @@ private:
void radialStart();
TimeMs radialTimeShift() const;
void deletePhotosDone(const MTPVector<MTPlong> &result);
bool deletePhotosFail(const RPCError &error);
void updateHeader();
void snapXY();
@@ -244,6 +252,9 @@ private:
bool updateOverState(OverState newState);
float64 overLevel(OverState control) const;
void checkGroupThumbsAnimation();
void initGroupThumbs();
QBrush _transparentBrush;
PhotoData *_photo = nullptr;
@@ -270,6 +281,11 @@ private:
bool _fullScreenVideo = false;
int _fullScreenZoomCache = 0;
std::unique_ptr<Media::View::GroupThumbs> _groupThumbs;
QRect _groupThumbsRect;
int _groupThumbsAvailableWidth = 0;
int _groupThumbsLeft = 0;
int _groupThumbsTop = 0;
Text _caption;
QRect _captionRect;

View File

@@ -47,6 +47,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "window/themes/window_theme.h"
#include "history/history_location_manager.h"
#include "ui/widgets/tooltip.h"
#include "ui/text_options.h"
#include "storage/serialize_common.h"
#include "window/window_controller.h"
#include "base/qthelp_regex.h"
@@ -115,7 +116,7 @@ Messenger::Messenger(not_null<Core::Launcher*> launcher)
style::startManager();
anim::startManager();
HistoryInit();
Ui::InitTextOptions();
Media::Player::start();
DEBUG_LOG(("Application Info: inited..."));
@@ -986,7 +987,6 @@ void Messenger::checkMediaViewActivation() {
void Messenger::loggedOut() {
if (_mediaView) {
hideMediaView();
_mediaView->rpcClear();
_mediaView->clearData();
}
}

View File

@@ -37,6 +37,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "history/history_media_types.h"
#include "history/history_item_components.h"
#include "ui/effects/round_checkbox.h"
#include "ui/text_options.h"
namespace Overview {
namespace Layout {
@@ -754,12 +755,12 @@ void Voice::updateName() {
auto version = 0;
if (const auto forwarded = parent()->Get<HistoryMessageForwarded>()) {
if (parent()->fromOriginal()->isChannel()) {
_name.setText(st::semiboldTextStyle, lng_forwarded_channel(lt_channel, App::peerName(parent()->fromOriginal())), _textNameOptions);
_name.setText(st::semiboldTextStyle, lng_forwarded_channel(lt_channel, App::peerName(parent()->fromOriginal())), Ui::NameTextOptions());
} else {
_name.setText(st::semiboldTextStyle, lng_forwarded(lt_user, App::peerName(parent()->fromOriginal())), _textNameOptions);
_name.setText(st::semiboldTextStyle, lng_forwarded(lt_user, App::peerName(parent()->fromOriginal())), Ui::NameTextOptions());
}
} else {
_name.setText(st::semiboldTextStyle, App::peerName(parent()->from()), _textNameOptions);
_name.setText(st::semiboldTextStyle, App::peerName(parent()->from()), Ui::NameTextOptions());
}
version = parent()->fromOriginal()->nameVersion;
_nameVersion = version;

View File

@@ -22,6 +22,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "ui/effects/ripple_animation.h"
#include "ui/widgets/popup_menu.h"
#include "ui/text_options.h"
#include "styles/style_profile.h"
#include "styles/style_widgets.h"
#include "auth_session.h"
@@ -100,7 +101,10 @@ void PeerListWidget::paintItem(Painter &p, int x, int y, Item *item, bool select
item->peer->paintUserpicLeft(p, x + _st.photoPosition.x(), y + _st.photoPosition.y(), width(), _st.photoSize);
if (item->name.isEmpty()) {
item->name.setText(st::msgNameStyle, App::peerName(item->peer), _textNameOptions);
item->name.setText(
st::msgNameStyle,
App::peerName(item->peer),
Ui::NameTextOptions());
}
int nameLeft = x + _st.namePosition.x();
int nameTop = y + _st.namePosition.y();

View File

@@ -872,10 +872,7 @@ void ParticipantsBoxController::kickMemberSure(not_null<UserData*> user) {
}
void ParticipantsBoxController::removeAdmin(not_null<UserData*> user) {
const auto phrase = _channel->isMegagroup()
? lng_profile_sure_remove_admin
: lng_profile_sure_remove_admin_channel;
const auto text = phrase(lt_user, user->firstName);
const auto text = lng_profile_sure_remove_admin(lt_user, user->firstName);
const auto weak = base::make_weak(this);
_editBox = Ui::show(Box<ConfirmBox>(text, lang(lng_box_remove), [=] {
if (const auto strong = weak.get()) {
@@ -1379,7 +1376,10 @@ void AddParticipantBoxController::showAdmin(not_null<UserData*> user, bool sure)
// The user is not in the group yet.
if (_channel->canAddMembers()) {
if (!sure) {
_editBox = Ui::show(Box<ConfirmBox>(lang(lng_sure_add_admin_invite), [weak, user] {
const auto text = lang(_channel->isMegagroup()
? lng_sure_add_admin_invite
: lng_sure_add_admin_invite_channel);
_editBox = Ui::show(Box<ConfirmBox>(text, [weak, user] {
if (weak) {
weak->showAdmin(user, true);
}

View File

@@ -476,7 +476,7 @@ void Uploader::partLoaded(const MTPBool &result, mtpRequestId requestId) {
if (document->uploading()) {
const auto doneParts = file.docSentParts
- int(docRequestsSent.size());
document->uploadingData->offset = std::max(
document->uploadingData->offset = std::min(
document->uploadingData->size,
doneParts * file.docPartSize);
}

View File

@@ -287,5 +287,34 @@ PreparedList PreparedList::Reordered(
return result;
}
void PreparedList::mergeToEnd(PreparedList &&other) {
if (error != Error::None) {
return;
}
if (other.error != Error::None) {
error = other.error;
errorData = other.errorData;
return;
}
allFilesForCompress = allFilesForCompress && other.allFilesForCompress;
files.reserve(files.size() + other.files.size());
for (auto &file : other.files) {
files.push_back(std::move(file));
}
if (files.size() > 1 && files.size() <= kMaxAlbumCount) {
const auto badIt = ranges::find(
files,
PreparedFile::AlbumType::None,
[](const PreparedFile &file) { return file.type; });
albumIsPossible = (badIt == files.end());
} else {
albumIsPossible = false;
}
}
int MaxAlbumItems() {
return kMaxAlbumCount;
}
} // namespace Storage

View File

@@ -71,6 +71,7 @@ struct PreparedList {
static PreparedList Reordered(
PreparedList &&list,
std::vector<int> order);
void mergeToEnd(PreparedList &&other);
Error error = Error::None;
QString errorData;
@@ -87,5 +88,6 @@ PreparedList PrepareMediaFromImage(
QImage &&image,
QByteArray &&content,
int previewWidth);
int MaxAlbumItems();
} // namespace Storage

View File

@@ -28,18 +28,19 @@ namespace Storage {
// Allow forward declarations.
enum class SharedMediaType : char {
Photo = 0,
Video = 1,
MusicFile = 2,
File = 3,
VoiceFile = 4,
Link = 5,
ChatPhoto = 6,
RoundVoiceFile = 7,
GIF = 8,
RoundFile = 9,
Photo,
Video,
PhotoVideo,
MusicFile,
File,
VoiceFile,
Link,
ChatPhoto,
RoundVoiceFile,
GIF,
RoundFile,
kCount = 10,
kCount,
};
constexpr auto kSharedMediaTypeCount = static_cast<int>(SharedMediaType::kCount);
constexpr bool IsValidSharedMediaType(SharedMediaType type) {

View File

@@ -59,6 +59,10 @@ inline Widget *CreateChild(
return new Widget(parent, std::forward<Args>(args)...);
}
inline void DestroyChild(QWidget *child) {
delete child;
}
template <typename Value>
inline void AttachAsChild(not_null<QObject*> parent, Value &&value) {
using PlainValue = std::decay_t<Value>;

View File

@@ -0,0 +1,257 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#include "ui/text_options.h"
#include "styles/style_window.h"
namespace Ui {
namespace {
TextParseOptions HistoryTextOptions = {
TextParseLinks
| TextParseMentions
| TextParseHashtags
| TextParseMultiline
| TextParseRichText
| TextParseMarkdown, // flags
0, // maxw
0, // maxh
Qt::LayoutDirectionAuto, // dir
};
TextParseOptions HistoryBotOptions = {
TextParseLinks
| TextParseMentions
| TextParseHashtags
| TextParseBotCommands
| TextParseMultiline
| TextParseRichText
| TextParseMarkdown, // flags
0, // maxw
0, // maxh
Qt::LayoutDirectionAuto, // dir
};
TextParseOptions HistoryServiceOptions = {
TextParseLinks
| TextParseMentions
| TextParseHashtags
//| TextParseMultiline
| TextParseRichText, // flags
0, // maxw
0, // maxh
Qt::LayoutDirectionAuto, // lang-dependent
};
TextParseOptions HistoryTextNoMonoOptions = {
TextParseLinks
| TextParseMentions
| TextParseHashtags
| TextParseMultiline
| TextParseRichText, // flags
0, // maxw
0, // maxh
Qt::LayoutDirectionAuto, // dir
};
TextParseOptions HistoryBotNoMonoOptions = {
TextParseLinks
| TextParseMentions
| TextParseHashtags
| TextParseBotCommands
| TextParseMultiline
| TextParseRichText, // flags
0, // maxw
0, // maxh
Qt::LayoutDirectionAuto, // dir
};
TextParseOptions TextNameOptions = {
0, // flags
4096, // maxw
1, // maxh
Qt::LayoutDirectionAuto, // lang-dependent
};
TextParseOptions TextDialogOptions = {
TextParseRichText, // flags
0, // maxw is style-dependent
1, // maxh
Qt::LayoutDirectionAuto, // lang-dependent
};
TextParseOptions WebpageTitleOptions = {
TextParseMultiline | TextParseRichText, // flags
0, // maxw
0, // maxh
Qt::LayoutDirectionAuto, // dir
};
TextParseOptions WebpageDescriptionOptions = {
TextParseLinks
| TextParseMentions
| TextParseHashtags
| TextParseMultiline
| TextParseRichText
| TextParseMarkdown, // flags
0, // maxw
0, // maxh
Qt::LayoutDirectionAuto, // dir
};
TextParseOptions TwitterDescriptionOptions = {
TextParseLinks
| TextParseMentions
| TextTwitterMentions
| TextParseHashtags
| TextTwitterHashtags
| TextParseMultiline
| TextParseRichText, // flags
0, // maxw
0, // maxh
Qt::LayoutDirectionAuto, // dir
};
TextParseOptions InstagramDescriptionOptions = {
TextParseLinks
| TextParseMentions
| TextInstagramMentions
| TextParseHashtags
| TextInstagramHashtags
| TextParseMultiline
| TextParseRichText, // flags
0, // maxw
0, // maxh
Qt::LayoutDirectionAuto, // dir
};
bool UseBotTextOptions(
not_null<History*> history,
not_null<PeerData*> author) {
if (const auto user = history->peer->asUser()) {
if (user->botInfo) {
return true;
}
} else if (const auto chat = history->peer->asChat()) {
if (chat->botStatus >= 0) {
return true;
}
} else if (const auto group = history->peer->asMegagroup()) {
if (group->mgInfo->botStatus >= 0) {
return true;
}
}
if (const auto user = author->asUser()) {
if (user->botInfo) {
return true;
}
}
return false;
}
} // namespace
void InitTextOptions() {
HistoryServiceOptions.dir
= TextNameOptions.dir
= TextDialogOptions.dir
= cLangDir();
TextDialogOptions.maxw = st::columnMaximalWidthLeft * 2;
WebpageTitleOptions.maxh = st::webPageTitleFont->height * 2;
WebpageTitleOptions.maxw
= WebpageDescriptionOptions.maxw
= TwitterDescriptionOptions.maxw
= InstagramDescriptionOptions.maxw
= st::msgMaxWidth
- st::msgPadding.left()
- st::webPageLeft
- st::msgPadding.right();
WebpageDescriptionOptions.maxh = st::webPageDescriptionFont->height * 3;
}
const TextParseOptions &ItemTextDefaultOptions() {
return HistoryTextOptions;
}
const TextParseOptions &ItemTextBotDefaultOptions() {
return HistoryBotOptions;
}
const TextParseOptions &ItemTextNoMonoOptions() {
return HistoryTextNoMonoOptions;
}
const TextParseOptions &ItemTextBotNoMonoOptions() {
return HistoryBotNoMonoOptions;
}
const TextParseOptions &ItemTextServiceOptions() {
return HistoryServiceOptions;
}
const TextParseOptions &WebpageTextTitleOptions() {
return WebpageTitleOptions;
}
const TextParseOptions &WebpageTextDescriptionOptions(
const QString &siteName) {
if (siteName == qstr("Twitter")) {
return TwitterDescriptionOptions;
} else if (siteName == qstr("Instagram")) {
return InstagramDescriptionOptions;
}
return WebpageDescriptionOptions;
}
const TextParseOptions &NameTextOptions() {
return TextNameOptions;
}
const TextParseOptions &DialogTextOptions() {
return TextDialogOptions;
}
const TextParseOptions &ItemTextOptions(
not_null<History*> history,
not_null<PeerData*> author) {
return UseBotTextOptions(history, author)
? HistoryBotOptions
: HistoryTextOptions;
}
const TextParseOptions &ItemTextOptions(not_null<const HistoryItem*> item) {
return ItemTextOptions(item->history(), item->author());
}
const TextParseOptions &ItemTextNoMonoOptions(
not_null<History*> history,
not_null<PeerData*> author) {
return UseBotTextOptions(history, author)
? HistoryBotNoMonoOptions
: HistoryTextNoMonoOptions;
}
const TextParseOptions &ItemTextNoMonoOptions(
not_null<const HistoryItem*> item) {
return ItemTextNoMonoOptions(item->history(), item->author());
}
} // namespace Ui

View File

@@ -0,0 +1,49 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#pragma once
namespace Ui {
void InitTextOptions();
const TextParseOptions &ItemTextDefaultOptions();
const TextParseOptions &ItemTextBotDefaultOptions();
const TextParseOptions &ItemTextNoMonoOptions();
const TextParseOptions &ItemTextBotNoMonoOptions();
const TextParseOptions &ItemTextServiceOptions();
const TextParseOptions &WebpageTextTitleOptions();
const TextParseOptions &WebpageTextDescriptionOptions(
const QString &siteName = QString());
const TextParseOptions &NameTextOptions();
const TextParseOptions &DialogTextOptions();
const TextParseOptions &ItemTextOptions(
not_null<History*> history,
not_null<PeerData*> author);
const TextParseOptions &ItemTextNoMonoOptions(
not_null<History*> history,
not_null<PeerData*> author);
const TextParseOptions &ItemTextOptions(not_null<const HistoryItem*> item);
const TextParseOptions &ItemTextNoMonoOptions(not_null<const HistoryItem*> item);
} // namespace Ui

View File

@@ -2467,6 +2467,24 @@ void InputArea::Inner::contextMenuEvent(QContextMenuEvent *e) {
}
}
bool InputArea::Inner::canInsertFromMimeData(const QMimeData *source) const {
if (source
&& f()->_mimeDataHook
&& f()->_mimeDataHook(source, MimeAction::Check)) {
return true;
}
return QTextEdit::canInsertFromMimeData(source);
}
void InputArea::Inner::insertFromMimeData(const QMimeData *source) {
if (source
&& f()->_mimeDataHook
&& f()->_mimeDataHook(source, MimeAction::Insert)) {
return;
}
return QTextEdit::insertFromMimeData(source);
}
void InputArea::resizeEvent(QResizeEvent *e) {
refreshPlaceholder();
_inner->setGeometry(rect().marginsRemoved(_st.textMargins));

View File

@@ -387,6 +387,17 @@ public:
_inner->clearFocus();
}
enum class MimeAction {
Check,
Insert,
};
using MimeDataHook = base::lambda<bool(
not_null<const QMimeData*> data,
MimeAction action)>;
void setMimeDataHook(MimeDataHook hook) {
_mimeDataHook = std::move(hook);
}
private slots:
void onTouchTimer();
@@ -441,6 +452,8 @@ private:
void keyPressEvent(QKeyEvent *e) override;
void contextMenuEvent(QContextMenuEvent *e) override;
bool canInsertFromMimeData(const QMimeData *source) const override;
void insertFromMimeData(const QMimeData *source) override;
QMimeData *createMimeDataFromSelection() const override;
private:
@@ -504,6 +517,7 @@ private:
QPoint _touchStart;
bool _correcting = false;
MimeDataHook _mimeDataHook;
};

View File

@@ -25,6 +25,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "ui/widgets/input_fields.h"
#include "ui/widgets/scroll_area.h"
#include "ui/effects/cross_animation.h"
#include "ui/text_options.h"
#include "lang/lang_keys.h"
namespace Ui {
@@ -43,7 +44,7 @@ MultiSelect::Item::Item(const style::MultiSelectItem &st, uint64 id, const QStri
}
void MultiSelect::Item::setText(const QString &text) {
_text.setText(_st.style, text, _textNameOptions);
_text.setText(_st.style, text, NameTextOptions());
_width = _st.height + _st.padding.left() + _text.maxWidth() + _st.padding.right();
accumulate_min(_width, _st.maxWidth);
}

View File

@@ -120,8 +120,9 @@ void SlideWrap<RpWidget>::animationStep() {
}
auto shouldBeHidden = !_toggled && !_animation.animating();
if (shouldBeHidden != isHidden()) {
const auto guard = make_weak(this);
setVisible(!shouldBeHidden);
if (shouldBeHidden) {
if (shouldBeHidden && guard) {
SendPendingMoveResizeEvents(this);
}
}

View File

@@ -23,6 +23,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "window/themes/window_theme.h"
#include "lang/lang_keys.h"
#include "platform/platform_window_title.h"
#include "ui/text_options.h"
#include "styles/style_widgets.h"
#include "styles/style_window.h"
#include "styles/style_mediaview.h"
@@ -213,13 +214,13 @@ void Generator::prepare() {
void Generator::addRow(QString name, int peerIndex, QString date, QString text) {
Row row;
row.name.setText(st::msgNameStyle, name, _textNameOptions);
row.name.setText(st::msgNameStyle, name, Ui::NameTextOptions());
row.letters = fillLetters(name);
row.peerIndex = peerIndex;
row.date = date;
row.text.setRichText(st::dialogsTextStyle, text, _textDlgOptions);
row.text.setRichText(st::dialogsTextStyle, text, Ui::DialogTextOptions());
_rows.push_back(std::move(row));
}
@@ -268,7 +269,7 @@ int Generator::computeInfoWidth(Status status, QString date) {
void Generator::addTextBubble(QString text, QString date, Status status) {
Bubble bubble;
auto skipBlock = computeSkipBlock(status, date);
bubble.text.setRichText(st::messageTextStyle, text + textcmdSkipBlock(skipBlock.width(), skipBlock.height()), _historyTextOptions);
bubble.text.setRichText(st::messageTextStyle, text + textcmdSkipBlock(skipBlock.width(), skipBlock.height()), Ui::ItemTextDefaultOptions());
auto width = _history.width() - st::msgMargin.left() - st::msgMargin.right();
accumulate_min(width, st::msgPadding.left() + bubble.text.maxWidth() + st::msgPadding.right());
@@ -292,7 +293,7 @@ void Generator::addPhotoBubble(QString image, QString caption, QString date, Sta
bubble.photoWidth = convertScale(bubble.photo.width() / 2);
bubble.photoHeight = convertScale(bubble.photo.height() / 2);
auto skipBlock = computeSkipBlock(status, date);
bubble.text.setRichText(st::messageTextStyle, caption + textcmdSkipBlock(skipBlock.width(), skipBlock.height()), _historyTextOptions);
bubble.text.setRichText(st::messageTextStyle, caption + textcmdSkipBlock(skipBlock.width(), skipBlock.height()), Ui::ItemTextDefaultOptions());
auto width = _history.width() - st::msgMargin.left() - st::msgMargin.right();
accumulate_min(width, bubble.photoWidth);
@@ -325,7 +326,7 @@ void Generator::generateData() {
_rows.back().status = Status::Received;
addRow("Davy Jones", 5, "4:00", textcmdLink(1, "Keynote.pdf"));
_topBarName.setText(st::msgNameStyle, "Eva Summer", _textNameOptions);
_topBarName.setText(st::msgNameStyle, "Eva Summer", Ui::NameTextOptions());
_topBarStatus = "online";
_topBarStatusActive = true;
@@ -345,8 +346,8 @@ void Generator::generateData() {
_bubbles.back().attached = true;
_bubbles.back().tail = true;
addTextBubble("Reminds me of a Chinese proverb: the best time to plant a tree was 20 years ago. The second best time is now.", "11:00", Status::None);
_bubbles.back().replyName.setText(st::msgNameStyle, "Alex Cassio", _textNameOptions);
_bubbles.back().replyText.setText(st::messageTextStyle, "Mark Twain said that " + QString() + QChar(9757) + QChar(55356) + QChar(57339), _textDlgOptions);
_bubbles.back().replyName.setText(st::msgNameStyle, "Alex Cassio", Ui::NameTextOptions());
_bubbles.back().replyText.setText(st::messageTextStyle, "Mark Twain said that " + QString() + QChar(9757) + QChar(55356) + QChar(57339), Ui::DialogTextOptions());
}
Generator::Generator(const Instance &theme, const CurrentData &current)

View File

@@ -1,6 +1,6 @@
AppVersion 1002004
AppVersion 1002006
AppVersionStrMajor 1.2
AppVersionStrSmall 1.2.4
AppVersionStr 1.2.4
AlphaChannel 1
AppVersionStrSmall 1.2.6
AppVersionStr 1.2.6
AlphaChannel 0
BetaVersion 0

View File

@@ -109,6 +109,9 @@
'OptimizeReferences': '2',
'LinkTimeCodeGeneration': '1', # /LTCG
},
'VCLibrarianTool': {
'LinkTimeCodeGeneration': 'true', # /LTCG
},
},
},
},

View File

@@ -139,6 +139,8 @@
<(src_loc)/chat_helpers/tabbed_selector.cpp
<(src_loc)/chat_helpers/tabbed_selector.h
<(src_loc)/core/basic_types.h
<(src_loc)/core/changelogs.cpp
<(src_loc)/core/changelogs.h
<(src_loc)/core/click_handler.cpp
<(src_loc)/core/click_handler.h
<(src_loc)/core/click_handler_types.cpp
@@ -343,6 +345,8 @@
<(src_loc)/media/view/media_clip_playback.h
<(src_loc)/media/view/media_clip_volume_controller.cpp
<(src_loc)/media/view/media_clip_volume_controller.h
<(src_loc)/media/view/media_view_group_thumbs.cpp
<(src_loc)/media/view/media_view_group_thumbs.h
<(src_loc)/media/media_audio.cpp
<(src_loc)/media/media_audio.h
<(src_loc)/media/media_audio_capture.cpp
@@ -628,6 +632,8 @@
<(src_loc)/ui/search_field_controller.h
<(src_loc)/ui/special_buttons.cpp
<(src_loc)/ui/special_buttons.h
<(src_loc)/ui/text_options.cpp
<(src_loc)/ui/text_options.h
<(src_loc)/ui/twidget.cpp
<(src_loc)/ui/twidget.h
<(src_loc)/ui/unread_badge.cpp

View File

@@ -1,3 +1,13 @@
1.2.6 (30.12.17)
- Grouped Photos. Group media into an album when sharing multiple photos and videos. Choose the exact order of media you send.
1.2.5 alpha (29.12.17)
- When viewing a photo from an album, you'll see other pictures from the same group as thumbnails in the lower part of the screen.
- When composing an album paste additional media from the clipboard.
- Bug fixes and other minor improvements.
1.2.4 alpha (26.12.17)
- Group media into an album when sharing multiple photos and videos.