Compare commits
58 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5024f1db8c | ||
|
|
a60385fc3d | ||
|
|
b20e2c37c1 | ||
|
|
acd40cbeb6 | ||
|
|
6a45a862dd | ||
|
|
5bd45e9a20 | ||
|
|
52e42f23ab | ||
|
|
96b7755cde | ||
|
|
c589ee1ca5 | ||
|
|
18c9ee093b | ||
|
|
b82fa3112c | ||
|
|
8d0f66d562 | ||
|
|
20a5e0ba73 | ||
|
|
5a2667c71e | ||
|
|
1f4a8d7eb6 | ||
|
|
4430bd0328 | ||
|
|
ab2e7f4c03 | ||
|
|
54e5c06b4d | ||
|
|
add5a6a0be | ||
|
|
89c2ba4293 | ||
|
|
dd100fb709 | ||
|
|
e8dd2b9e7b | ||
|
|
71e3cd227c | ||
|
|
1648c31a22 | ||
|
|
f8c820f319 | ||
|
|
300f35e78f | ||
|
|
2aa5849997 | ||
|
|
fb1b845211 | ||
|
|
3d2af9db8e | ||
|
|
6129e5a1cf | ||
|
|
7f70ee1227 | ||
|
|
c9f7da6e82 | ||
|
|
f956c0f227 | ||
|
|
df935e0477 | ||
|
|
841f1afe1e | ||
|
|
fb8b88557e | ||
|
|
2b502b22b9 | ||
|
|
5ac80d2655 | ||
|
|
8aa7499e63 | ||
|
|
8cd5e51982 | ||
|
|
e5c2133446 | ||
|
|
3dccdf2f05 | ||
|
|
8a708c6655 | ||
|
|
9e1d9eee4b | ||
|
|
f30aabc365 | ||
|
|
a5546d016f | ||
|
|
f79d70d112 | ||
|
|
ec28f258fb | ||
|
|
08f3a6fb40 | ||
|
|
8823d5256f | ||
|
|
60e7aa90d2 | ||
|
|
71357a9546 | ||
|
|
a5b06e9c56 | ||
|
|
0f94419f6d | ||
|
|
94e7aabea5 | ||
|
|
f6c816cafe | ||
|
|
4cf160e8dc | ||
|
|
9252be5e8c |
@@ -3349,6 +3349,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_context_promote_admin" = "Promote to admin";
|
||||
"lng_context_edit_permissions" = "Edit permissions";
|
||||
"lng_context_restrict_user" = "Restrict user";
|
||||
"lng_context_ban_user" = "Ban";
|
||||
"lng_context_remove_from_group" = "Remove from group";
|
||||
"lng_context_add_to_group" = "Add to group";
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
|
||||
ProcessorArchitecture="ARCHITECTURE"
|
||||
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
|
||||
Version="5.4.3.0" />
|
||||
Version="5.4.6.0" />
|
||||
<Properties>
|
||||
<DisplayName>Telegram Desktop</DisplayName>
|
||||
<PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName>
|
||||
|
||||
@@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 5,4,3,0
|
||||
PRODUCTVERSION 5,4,3,0
|
||||
FILEVERSION 5,4,6,0
|
||||
PRODUCTVERSION 5,4,6,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -62,10 +62,10 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Telegram FZ-LLC"
|
||||
VALUE "FileDescription", "Telegram Desktop"
|
||||
VALUE "FileVersion", "5.4.3.0"
|
||||
VALUE "FileVersion", "5.4.6.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2024"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "5.4.3.0"
|
||||
VALUE "ProductVersion", "5.4.6.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 5,4,3,0
|
||||
PRODUCTVERSION 5,4,3,0
|
||||
FILEVERSION 5,4,6,0
|
||||
PRODUCTVERSION 5,4,6,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -53,10 +53,10 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Telegram FZ-LLC"
|
||||
VALUE "FileDescription", "Telegram Desktop Updater"
|
||||
VALUE "FileVersion", "5.4.3.0"
|
||||
VALUE "FileVersion", "5.4.6.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2024"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "5.4.3.0"
|
||||
VALUE "ProductVersion", "5.4.6.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -12,6 +12,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "core/core_cloud_password.h"
|
||||
#include "passport/passport_encryption.h"
|
||||
|
||||
#include "base/unixtime.h"
|
||||
#include "base/call_delayed.h"
|
||||
|
||||
namespace Api {
|
||||
namespace {
|
||||
|
||||
|
||||
@@ -44,7 +44,10 @@ void InnerFillMessagePostFlags(
|
||||
if (ShouldSendSilent(peer, options)) {
|
||||
flags |= MessageFlag::Silent;
|
||||
}
|
||||
if (!peer->amAnonymous()) {
|
||||
if (!peer->amAnonymous()
|
||||
|| (!peer->isBroadcast()
|
||||
&& options.sendAs
|
||||
&& options.sendAs != peer)) {
|
||||
flags |= MessageFlag::HasFromId;
|
||||
}
|
||||
const auto channel = peer->asBroadcast();
|
||||
|
||||
@@ -214,7 +214,10 @@ struct State {
|
||||
|
||||
[[nodiscard]] QImage GenerateUserpic(Userpic &userpic, int size) {
|
||||
size *= style::DevicePixelRatio();
|
||||
auto result = userpic.peer->generateUserpicImage(userpic.view, size);
|
||||
auto result = PeerData::GenerateUserpicImage(
|
||||
userpic.peer,
|
||||
userpic.view,
|
||||
size);
|
||||
result.setDevicePixelRatio(style::DevicePixelRatio());
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -597,8 +597,6 @@ rightsHeaderLabel: FlatLabel(boxLabel) {
|
||||
}
|
||||
textFg: windowActiveTextFg;
|
||||
}
|
||||
rightsUntilMargin: margins(0px, 8px, 0px, 20px);
|
||||
rightsRankMargin: margins(0px, 7px, 0px, 20px);
|
||||
|
||||
groupStickersRemove: defaultMultiSelectSearchCancel;
|
||||
groupStickersRemovePosition: point(6px, 6px);
|
||||
@@ -787,7 +785,7 @@ backgroundConfirmPadding: margins(24px, 16px, 24px, 16px);
|
||||
backgroundConfirm: RoundButton(defaultActiveButton) {
|
||||
height: 44px;
|
||||
textTop: 12px;
|
||||
font: font(13px semibold);
|
||||
style: semiboldTextStyle;
|
||||
}
|
||||
backgroundConfirmCancel: RoundButton(backgroundConfirm) {
|
||||
textFg: mediaviewSaveMsgFg;
|
||||
@@ -799,7 +797,7 @@ backgroundConfirmCancel: RoundButton(backgroundConfirm) {
|
||||
|
||||
height: 44px;
|
||||
textTop: 12px;
|
||||
font: font(13px semibold);
|
||||
style: semiboldTextStyle;
|
||||
|
||||
ripple: RippleAnimation(defaultRippleAnimation) {
|
||||
color: shadowFg;
|
||||
@@ -951,7 +949,7 @@ sponsoredUrlButton: RoundButton(defaultActiveButton) {
|
||||
textFg: historyLinkInFg;
|
||||
textFgOver: historyLinkInFg;
|
||||
textTop: 7px;
|
||||
font: normalFont;
|
||||
style: defaultTextStyle;
|
||||
|
||||
ripple: RippleAnimation(defaultRippleAnimation) {
|
||||
color: windowBgOver;
|
||||
|
||||
@@ -195,7 +195,7 @@ PaintRoundImageCallback PremiumsRow::generatePaintUserpicCallback(
|
||||
const auto radius = size * Ui::ForumUserpicRadiusMultiplier();
|
||||
p.drawRoundedRect(x, y, size, size, radius, radius);
|
||||
}
|
||||
st::settingsPrivacyPremium.paintInCenter(p, { x, y, size, size });
|
||||
st::settingsPrivacyPremium.paintInCenter(p, QRect(x, y, size, size));
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/wrap/vertical_layout.h"
|
||||
#include "ui/wrap/slide_wrap.h"
|
||||
#include "ui/painter.h"
|
||||
#include "ui/rect.h"
|
||||
#include "ui/vertical_list.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "styles/style_info.h"
|
||||
@@ -965,9 +966,9 @@ void LinksController::rowPaintIcon(
|
||||
p.setBrush(*bg);
|
||||
{
|
||||
auto hq = PainterHighQualityEnabler(p);
|
||||
p.drawEllipse(QRect(0, 0, inner, inner));
|
||||
p.drawEllipse(Rect(Size(inner)));
|
||||
}
|
||||
st::inviteLinkIcon.paintInCenter(p, { 0, 0, inner, inner });
|
||||
st::inviteLinkIcon.paintInCenter(p, Rect(Size(inner)));
|
||||
}
|
||||
p.drawImage(x + skip, y + skip, icon);
|
||||
}
|
||||
|
||||
@@ -436,14 +436,16 @@ void CreateModerateMessagesBox(
|
||||
return result;
|
||||
}();
|
||||
|
||||
auto [checkboxes, getRestrictions, changes] = CreateEditRestrictions(
|
||||
box,
|
||||
Ui::AddSubsectionTitle(
|
||||
inner,
|
||||
rpl::conditional(
|
||||
rpl::single(isSingle),
|
||||
tr::lng_restrict_users_part_single_header(),
|
||||
tr::lng_restrict_users_part_header(
|
||||
lt_count,
|
||||
rpl::single(participants.size()) | tr::to_count())),
|
||||
rpl::single(participants.size()) | tr::to_count())));
|
||||
auto [checkboxes, getRestrictions, changes] = CreateEditRestrictions(
|
||||
box,
|
||||
prepareFlags,
|
||||
disabledMessages,
|
||||
{ .isForum = peer->isForum() });
|
||||
|
||||
@@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
#include "lang/lang_keys.h"
|
||||
#include "ui/controls/userpic_button.h"
|
||||
#include "ui/vertical_list.h"
|
||||
#include "ui/wrap/vertical_layout.h"
|
||||
#include "ui/wrap/padding_wrap.h"
|
||||
#include "ui/wrap/slide_wrap.h"
|
||||
@@ -63,6 +64,10 @@ public:
|
||||
template <typename Widget>
|
||||
Widget *addControl(object_ptr<Widget> widget, QMargins margin);
|
||||
|
||||
[[nodiscard]] not_null<Ui::VerticalLayout*> verticalLayout() const {
|
||||
return _rows;
|
||||
}
|
||||
|
||||
protected:
|
||||
int resizeGetHeight(int newWidth) override;
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
@@ -164,6 +169,10 @@ EditParticipantBox::EditParticipantBox(
|
||||
, _hasAdminRights(hasAdminRights) {
|
||||
}
|
||||
|
||||
not_null<Ui::VerticalLayout*> EditParticipantBox::verticalLayout() const {
|
||||
return _inner->verticalLayout();
|
||||
}
|
||||
|
||||
void EditParticipantBox::prepare() {
|
||||
_inner = setInnerWidget(object_ptr<Inner>(
|
||||
this,
|
||||
@@ -279,9 +288,8 @@ void EditAdminBox::prepare() {
|
||||
object_ptr<Ui::VerticalLayout>(this)));
|
||||
const auto inner = _adminControlsWrap->entity();
|
||||
|
||||
inner->add(
|
||||
object_ptr<Ui::BoxContentDivider>(inner),
|
||||
st::rightsDividerMargin);
|
||||
Ui::AddDivider(inner);
|
||||
Ui::AddSkip(inner);
|
||||
|
||||
const auto chat = peer()->asChat();
|
||||
const auto channel = peer()->asChannel();
|
||||
@@ -335,9 +343,9 @@ void EditAdminBox::prepare() {
|
||||
.isForum = peer()->isForum(),
|
||||
.anyoneCanAddMembers = anyoneCanAddMembers,
|
||||
};
|
||||
Ui::AddSubsectionTitle(inner, tr::lng_rights_edit_admin_header());
|
||||
auto [checkboxes, getChecked, changes] = CreateEditAdminRights(
|
||||
inner,
|
||||
tr::lng_rights_edit_admin_header(),
|
||||
prepareFlags,
|
||||
disabledMessages,
|
||||
options);
|
||||
@@ -441,9 +449,7 @@ void EditAdminBox::refreshButtons() {
|
||||
|
||||
not_null<Ui::InputField*> EditAdminBox::addRankInput(
|
||||
not_null<Ui::VerticalLayout*> container) {
|
||||
container->add(
|
||||
object_ptr<Ui::BoxContentDivider>(container),
|
||||
st::rightsRankMargin);
|
||||
Ui::AddDivider(container);
|
||||
|
||||
container->add(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
@@ -712,9 +718,8 @@ void EditRestrictedBox::prepare() {
|
||||
|
||||
setTitle(tr::lng_rights_user_restrictions());
|
||||
|
||||
addControl(
|
||||
object_ptr<Ui::BoxContentDivider>(this),
|
||||
st::rightsDividerMargin);
|
||||
Ui::AddDivider(verticalLayout());
|
||||
Ui::AddSkip(verticalLayout());
|
||||
|
||||
const auto chat = peer()->asChat();
|
||||
const auto channel = peer()->asChannel();
|
||||
@@ -749,16 +754,20 @@ void EditRestrictedBox::prepare() {
|
||||
return result;
|
||||
}();
|
||||
|
||||
Ui::AddSubsectionTitle(
|
||||
verticalLayout(),
|
||||
tr::lng_rights_user_restrictions_header());
|
||||
auto [checkboxes, getRestrictions, changes] = CreateEditRestrictions(
|
||||
this,
|
||||
tr::lng_rights_user_restrictions_header(),
|
||||
prepareFlags,
|
||||
disabledMessages,
|
||||
{ .isForum = peer()->isForum() });
|
||||
addControl(std::move(checkboxes), QMargins());
|
||||
|
||||
_until = prepareRights.until;
|
||||
addControl(object_ptr<Ui::BoxContentDivider>(this), st::rightsUntilMargin);
|
||||
addControl(
|
||||
object_ptr<Ui::FixedHeightWidget>(this, st::defaultVerticalListSkip));
|
||||
Ui::AddDivider(verticalLayout());
|
||||
addControl(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
this,
|
||||
|
||||
@@ -36,6 +36,8 @@ public:
|
||||
not_null<UserData*> user,
|
||||
bool hasAdminRights);
|
||||
|
||||
[[nodiscard]] not_null<Ui::VerticalLayout*> verticalLayout() const;
|
||||
|
||||
protected:
|
||||
void prepare() override;
|
||||
|
||||
|
||||
@@ -734,7 +734,7 @@ void LinksController::rowPaintIcon(
|
||||
} else {
|
||||
(color == Color::Revoked
|
||||
? st::inviteLinkRevokedIcon
|
||||
: st::inviteLinkIcon).paintInCenter(p, { 0, 0, inner, inner });
|
||||
: st::inviteLinkIcon).paintInCenter(p, Rect(Size(inner)));
|
||||
}
|
||||
}
|
||||
p.drawImage(x + skip, y + skip, icon);
|
||||
|
||||
@@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/wrap/vertical_layout.h"
|
||||
#include "ui/layers/generic_box.h"
|
||||
#include "ui/painter.h"
|
||||
#include "ui/vertical_list.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
@@ -583,14 +584,6 @@ template <typename Flags>
|
||||
ApplyDependencies(state->checkViews, dependencies, view);
|
||||
};
|
||||
|
||||
if (descriptor.header) {
|
||||
container->add(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
container,
|
||||
std::move(descriptor.header),
|
||||
st::rightsHeaderLabel),
|
||||
st::rightsHeaderMargin);
|
||||
}
|
||||
const auto addCheckbox = [&](
|
||||
not_null<Ui::VerticalLayout*> verticalLayout,
|
||||
bool isInner,
|
||||
@@ -1146,9 +1139,11 @@ void ShowEditPeerPermissionsBox(
|
||||
return result;
|
||||
}();
|
||||
|
||||
Ui::AddSubsectionTitle(
|
||||
inner,
|
||||
tr::lng_rights_default_restrictions_header());
|
||||
auto [checkboxes, getRestrictions, changes] = CreateEditRestrictions(
|
||||
inner,
|
||||
tr::lng_rights_default_restrictions_header(),
|
||||
restrictions,
|
||||
disabledMessages,
|
||||
{ .isForum = peer->isForum() });
|
||||
@@ -1312,7 +1307,6 @@ std::vector<AdminRightLabel> AdminRightLabels(
|
||||
|
||||
EditFlagsControl<ChatRestrictions> CreateEditRestrictions(
|
||||
QWidget *parent,
|
||||
rpl::producer<QString> header,
|
||||
ChatRestrictions restrictions,
|
||||
base::flat_map<ChatRestrictions, QString> disabledMessages,
|
||||
Data::RestrictionsSetOptions options) {
|
||||
@@ -1321,7 +1315,6 @@ EditFlagsControl<ChatRestrictions> CreateEditRestrictions(
|
||||
widget.data(),
|
||||
NegateRestrictions(restrictions),
|
||||
{
|
||||
.header = std::move(header),
|
||||
.labels = NestedRestrictionLabelsList(options),
|
||||
.disabledMessages = std::move(disabledMessages),
|
||||
});
|
||||
@@ -1338,7 +1331,6 @@ EditFlagsControl<ChatRestrictions> CreateEditRestrictions(
|
||||
|
||||
EditFlagsControl<ChatAdminRights> CreateEditAdminRights(
|
||||
QWidget *parent,
|
||||
rpl::producer<QString> header,
|
||||
ChatAdminRights rights,
|
||||
base::flat_map<ChatAdminRights, QString> disabledMessages,
|
||||
Data::AdminRightsSetOptions options) {
|
||||
@@ -1347,7 +1339,6 @@ EditFlagsControl<ChatAdminRights> CreateEditAdminRights(
|
||||
widget.data(),
|
||||
rights,
|
||||
{
|
||||
.header = std::move(header),
|
||||
.labels = NestedAdminRightLabels(options),
|
||||
.disabledMessages = std::move(disabledMessages),
|
||||
});
|
||||
|
||||
@@ -73,7 +73,6 @@ struct NestedEditFlagsLabels {
|
||||
|
||||
template <typename Flags>
|
||||
struct EditFlagsDescriptor {
|
||||
rpl::producer<QString> header;
|
||||
std::vector<NestedEditFlagsLabels<Flags>> labels;
|
||||
base::flat_map<Flags, QString> disabledMessages;
|
||||
const style::SettingsButton *st = nullptr;
|
||||
@@ -90,7 +89,6 @@ using AdminRightLabel = EditFlagsLabel<ChatAdminRights>;
|
||||
|
||||
[[nodiscard]] auto CreateEditRestrictions(
|
||||
QWidget *parent,
|
||||
rpl::producer<QString> header,
|
||||
ChatRestrictions restrictions,
|
||||
base::flat_map<ChatRestrictions, QString> disabledMessages,
|
||||
Data::RestrictionsSetOptions options)
|
||||
@@ -98,7 +96,6 @@ using AdminRightLabel = EditFlagsLabel<ChatAdminRights>;
|
||||
|
||||
[[nodiscard]] auto CreateEditAdminRights(
|
||||
QWidget *parent,
|
||||
rpl::producer<QString> header,
|
||||
ChatAdminRights rights,
|
||||
base::flat_map<ChatAdminRights, QString> disabledMessages,
|
||||
Data::AdminRightsSetOptions options)
|
||||
|
||||
@@ -241,8 +241,8 @@ RequestsBoxController::RowHelper::RowHelper(bool isGroup)
|
||||
? tr::lng_group_requests_add(tr::now)
|
||||
: tr::lng_group_requests_add_channel(tr::now))
|
||||
, _rejectText(tr::lng_group_requests_dismiss(tr::now))
|
||||
, _acceptTextWidth(st::requestsAcceptButton.font->width(_acceptText))
|
||||
, _rejectTextWidth(st::requestsRejectButton.font->width(_rejectText)) {
|
||||
, _acceptTextWidth(st::requestsAcceptButton.style.font->width(_acceptText))
|
||||
, _rejectTextWidth(st::requestsRejectButton.style.font->width(_rejectText)) {
|
||||
}
|
||||
|
||||
RequestsBoxController::RequestsBoxController(
|
||||
@@ -491,7 +491,7 @@ void RequestsBoxController::RowHelper::paintButton(
|
||||
const auto textLeft = geometry.x()
|
||||
+ ((geometry.width() - textWidth) / 2);
|
||||
const auto textTop = geometry.y() + st.textTop;
|
||||
p.setFont(st.font);
|
||||
p.setFont(st.style.font);
|
||||
p.setPen(over ? st.textFgOver : st.textFg);
|
||||
p.drawTextLeft(textLeft, textTop, outerWidth, text);
|
||||
}
|
||||
|
||||
@@ -79,7 +79,8 @@ void ProcessUserpic(
|
||||
if (!state->userpicView.cloud) {
|
||||
GenerateImage(
|
||||
state,
|
||||
peer->generateUserpicImage(
|
||||
PeerData::GenerateUserpicImage(
|
||||
peer,
|
||||
state->userpicView,
|
||||
st::shortInfoWidth * style::DevicePixelRatio(),
|
||||
0),
|
||||
|
||||
@@ -1216,11 +1216,12 @@ StickersBox::Inner::Inner(
|
||||
})
|
||||
, _itemsTop(st::lineWidth)
|
||||
, _addText(tr::lng_stickers_featured_add(tr::now))
|
||||
, _addWidth(st::stickersTrendingAdd.font->width(_addText))
|
||||
, _addWidth(st::stickersTrendingAdd.style.font->width(_addText))
|
||||
, _undoText(tr::lng_stickers_return(tr::now))
|
||||
, _undoWidth(st::stickersUndoRemove.font->width(_undoText))
|
||||
, _undoWidth(st::stickersUndoRemove.style.font->width(_undoText))
|
||||
, _installedText(tr::lng_stickers_featured_installed(tr::now))
|
||||
, _installedWidth(st::stickersTrendingInstalled.font->width(_installedText)) {
|
||||
, _installedWidth(st::stickersTrendingInstalled.style.font->width(
|
||||
_installedText)) {
|
||||
setup();
|
||||
}
|
||||
|
||||
@@ -1666,7 +1667,7 @@ void StickersBox::Inner::paintFakeButton(Painter &p, not_null<Row*> row, int ind
|
||||
row->ripple.reset();
|
||||
}
|
||||
}
|
||||
p.setFont(st.font);
|
||||
p.setFont(st.style.font);
|
||||
p.setPen(st.textFg);
|
||||
p.drawTextLeft(rect.x() - (st.width / 2), rect.y() + st.textTop, width(), text, textWidth);
|
||||
} else {
|
||||
@@ -1700,7 +1701,7 @@ void StickersBox::Inner::paintFakeButton(Painter &p, not_null<Row*> row, int ind
|
||||
row->ripple.reset();
|
||||
}
|
||||
}
|
||||
p.setFont(st.font);
|
||||
p.setFont(st.style.font);
|
||||
p.setPen(selected ? st.textFgOver : st.textFg);
|
||||
p.drawTextLeft(rect.x() - (st.width / 2), rect.y() + st.textTop, width(), text, textWidth);
|
||||
}
|
||||
|
||||
@@ -460,7 +460,8 @@ void Viewport::RendererGL::validateUserpicFrame(
|
||||
return;
|
||||
}
|
||||
const auto size = tile->trackOrUserpicSize();
|
||||
tileData.userpicFrame = tile->row()->peer()->generateUserpicImage(
|
||||
tileData.userpicFrame = PeerData::GenerateUserpicImage(
|
||||
tile->row()->peer(),
|
||||
tile->row()->ensureUserpicView(),
|
||||
size.width(),
|
||||
0);
|
||||
|
||||
@@ -77,7 +77,8 @@ void Viewport::RendererSW::validateUserpicFrame(
|
||||
}
|
||||
const auto size = tile->trackOrUserpicSize();
|
||||
data.userpicFrame = Images::BlurLargeImage(
|
||||
tile->row()->peer()->generateUserpicImage(
|
||||
PeerData::GenerateUserpicImage(
|
||||
tile->row()->peer(),
|
||||
tile->row()->ensureUserpicView(),
|
||||
size.width(),
|
||||
0),
|
||||
|
||||
@@ -296,7 +296,9 @@ emojiPanButton: RoundButton(defaultActiveButton) {
|
||||
textTop: 2px;
|
||||
}
|
||||
emojiPanExpand: RoundButton(defaultActiveButton) {
|
||||
font: font(12px bold);
|
||||
style: TextStyle(semiboldTextStyle) {
|
||||
font: font(12px bold);
|
||||
}
|
||||
width: -8px;
|
||||
height: 19px;
|
||||
textTop: 1px;
|
||||
@@ -1499,5 +1501,7 @@ pickLocationChooseOnMap: RoundButton(defaultActiveButton) {
|
||||
height: 44px;
|
||||
textTop: 11px;
|
||||
width: -96px;
|
||||
font: font(15px semibold);
|
||||
style: TextStyle(semiboldTextStyle) {
|
||||
font: font(15px semibold);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1450,17 +1450,17 @@ void EmojiListWidget::drawCollapsedBadge(
|
||||
int count) {
|
||||
const auto &st = st::emojiPanExpand;
|
||||
const auto text = u"+%1"_q.arg(count - _columnCount * kCollapsedRows + 1);
|
||||
const auto textWidth = st.font->width(text);
|
||||
const auto textWidth = st.style.font->width(text);
|
||||
const auto buttonw = std::max(textWidth - st.width, st.height);
|
||||
const auto buttonh = st.height;
|
||||
const auto buttonx = position.x() + (_singleSize.width() - buttonw) / 2;
|
||||
const auto buttony = position.y() + (_singleSize.height() - buttonh) / 2;
|
||||
_collapsedBg.paint(p, QRect(buttonx, buttony, buttonw, buttonh));
|
||||
p.setPen(this->st().bg);
|
||||
p.setFont(st.font);
|
||||
p.setFont(st.style.font);
|
||||
p.drawText(
|
||||
buttonx + (buttonw - textWidth) / 2,
|
||||
(buttony + st.textTop + st.font->ascent),
|
||||
(buttony + st.textTop + st.style.font->ascent),
|
||||
text);
|
||||
}
|
||||
|
||||
@@ -2546,12 +2546,12 @@ int EmojiListWidget::paintButtonGetWidth(
|
||||
: selected
|
||||
? st::emojiPanButton.textFgOver
|
||||
: st::emojiPanButton.textFg);
|
||||
p.setFont(st::emojiPanButton.font);
|
||||
p.setFont(st::emojiPanButton.style.font);
|
||||
p.drawText(
|
||||
rect.x() - (st::emojiPanButton.width / 2),
|
||||
(rect.y()
|
||||
+ st::emojiPanButton.textTop
|
||||
+ st::emojiPanButton.font->ascent),
|
||||
+ st::emojiPanButton.style.font->ascent),
|
||||
button.text);
|
||||
return emojiRight() - rect.x();
|
||||
}
|
||||
@@ -2678,7 +2678,7 @@ void EmojiListWidget::initButton(
|
||||
const QString &text,
|
||||
bool gradient) {
|
||||
button.text = text;
|
||||
button.textWidth = st::emojiPanButton.font->width(text);
|
||||
button.textWidth = st::emojiPanButton.style.font->width(text);
|
||||
const auto width = button.textWidth - st::emojiPanButton.width;
|
||||
const auto height = st::emojiPanButton.height;
|
||||
const auto factor = style::DevicePixelRatio();
|
||||
|
||||
@@ -213,11 +213,14 @@ StickersListWidget::StickersListWidget(
|
||||
st().pathBg,
|
||||
st().pathFg,
|
||||
[=] { update(); }))
|
||||
, _megagroupSetAbout(st::columnMinimalWidthThird - st::emojiScroll.width - st().headerLeft)
|
||||
, _megagroupSetAbout(st::columnMinimalWidthThird
|
||||
- st::emojiScroll.width
|
||||
- st().headerLeft)
|
||||
, _addText(tr::lng_stickers_featured_add(tr::now))
|
||||
, _addWidth(st::stickersTrendingAdd.font->width(_addText))
|
||||
, _addWidth(st::stickersTrendingAdd.style.font->width(_addText))
|
||||
, _installedText(tr::lng_stickers_featured_installed(tr::now))
|
||||
, _installedWidth(st::stickersTrendingInstalled.font->width(_installedText))
|
||||
, _installedWidth(
|
||||
st::stickersTrendingInstalled.style.font->width(_installedText))
|
||||
, _settings(this, tr::lng_stickers_you_have(tr::now))
|
||||
, _previewTimer([=] { showPreview(); })
|
||||
, _premiumMark(std::make_unique<StickerPremiumMark>(
|
||||
@@ -974,7 +977,7 @@ void StickersListWidget::paintStickers(Painter &p, QRect clip) {
|
||||
const auto &st = installedSet
|
||||
? st::stickersTrendingInstalled
|
||||
: st::stickersTrendingAdd;
|
||||
p.setFont(st.font);
|
||||
p.setFont(st.style.font);
|
||||
p.setPen(selected ? st.textFgOver : st.textFg);
|
||||
p.drawTextLeft(
|
||||
add.x() - (st.width / 2),
|
||||
@@ -1238,7 +1241,7 @@ void StickersListWidget::paintMegagroupEmptySet(Painter &p, int y, bool buttonSe
|
||||
_megagroupSetButtonRipple.reset();
|
||||
}
|
||||
}
|
||||
p.setFont(st::stickerGroupCategoryAdd.font);
|
||||
p.setFont(st::stickerGroupCategoryAdd.style.font);
|
||||
p.setPen(buttonSelected ? st::stickerGroupCategoryAdd.textFgOver : st::stickerGroupCategoryAdd.textFg);
|
||||
p.drawTextLeft(button.x() - (st::stickerGroupCategoryAdd.width / 2), button.y() + st::stickerGroupCategoryAdd.textTop, width(), _megagroupSetButtonText, _megagroupSetButtonTextWidth);
|
||||
}
|
||||
@@ -2734,7 +2737,7 @@ void StickersListWidget::refreshMegagroupSetGeometry() {
|
||||
auto left = megagroupSetInfoLeft();
|
||||
auto availableWidth = (width() - left);
|
||||
auto top = _megagroupSetAbout.countHeight(availableWidth) + st::stickerGroupCategoryAddMargin.top();
|
||||
_megagroupSetButtonTextWidth = st::stickerGroupCategoryAdd.font->width(_megagroupSetButtonText);
|
||||
_megagroupSetButtonTextWidth = st::stickerGroupCategoryAdd.style.font->width(_megagroupSetButtonText);
|
||||
auto buttonWidth = _megagroupSetButtonTextWidth - st::stickerGroupCategoryAdd.width;
|
||||
_megagroupSetButtonRect = QRect(left, top, buttonWidth, st::stickerGroupCategoryAdd.height);
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ constexpr auto AppId = "{53F49750-6209-4FBF-9CA8-7A333C87D1ED}"_cs;
|
||||
constexpr auto AppNameOld = "Telegram Win (Unofficial)"_cs;
|
||||
constexpr auto AppName = "Telegram Desktop"_cs;
|
||||
constexpr auto AppFile = "Telegram"_cs;
|
||||
constexpr auto AppVersion = 5004003;
|
||||
constexpr auto AppVersionStr = "5.4.3";
|
||||
constexpr auto AppVersion = 5004006;
|
||||
constexpr auto AppVersionStr = "5.4.6";
|
||||
constexpr auto AppBetaVersion = true;
|
||||
constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION;
|
||||
|
||||
@@ -70,7 +70,7 @@ constexpr auto kShowChatNamesCount = 8;
|
||||
);
|
||||
const auto wrapName = [](not_null<History*> history) {
|
||||
const auto name = history->peer->name();
|
||||
return TextWithEntities{
|
||||
return st::wrap_rtl(TextWithEntities{
|
||||
.text = name,
|
||||
.entities = (history->chatListBadgesState().unread
|
||||
? EntitiesInText{
|
||||
@@ -78,7 +78,7 @@ constexpr auto kShowChatNamesCount = 8;
|
||||
{ EntityType::Colorized, 0, int(name.size()), QString() },
|
||||
}
|
||||
: EntitiesInText{}),
|
||||
};
|
||||
});
|
||||
};
|
||||
const auto shown = int(peers.size());
|
||||
const auto accumulated = [&] {
|
||||
|
||||
@@ -437,11 +437,12 @@ InMemoryKey PeerData::userpicUniqueKey(Ui::PeerUserpicView &view) const {
|
||||
: inMemoryKey(_userpic.location());
|
||||
}
|
||||
|
||||
QImage PeerData::generateUserpicImage(
|
||||
QImage PeerData::GenerateUserpicImage(
|
||||
not_null<PeerData*> peer,
|
||||
Ui::PeerUserpicView &view,
|
||||
int size,
|
||||
std::optional<int> radius) const {
|
||||
if (const auto userpic = userpicCloudImage(view)) {
|
||||
std::optional<int> radius) {
|
||||
if (const auto userpic = peer->userpicCloudImage(view)) {
|
||||
auto image = userpic->scaled(
|
||||
{ size, size },
|
||||
Qt::IgnoreAspectRatio,
|
||||
@@ -455,7 +456,7 @@ QImage PeerData::generateUserpicImage(
|
||||
return image;
|
||||
} else if (radius) {
|
||||
return round(*radius);
|
||||
} else if (isForum()) {
|
||||
} else if (peer->isForum()) {
|
||||
return round(size * Ui::ForumUserpicRadiusMultiplier());
|
||||
} else {
|
||||
return Images::Circle(std::move(image));
|
||||
@@ -468,11 +469,12 @@ QImage PeerData::generateUserpicImage(
|
||||
|
||||
Painter p(&result);
|
||||
if (radius == 0) {
|
||||
ensureEmptyUserpic()->paintSquare(p, 0, 0, size, size);
|
||||
peer->ensureEmptyUserpic()->paintSquare(p, 0, 0, size, size);
|
||||
} else if (radius) {
|
||||
ensureEmptyUserpic()->paintRounded(p, 0, 0, size, size, *radius);
|
||||
} else if (isForum()) {
|
||||
ensureEmptyUserpic()->paintRounded(
|
||||
const auto r = *radius;
|
||||
peer->ensureEmptyUserpic()->paintRounded(p, 0, 0, size, size, r);
|
||||
} else if (peer->isForum()) {
|
||||
peer->ensureEmptyUserpic()->paintRounded(
|
||||
p,
|
||||
0,
|
||||
0,
|
||||
@@ -480,7 +482,7 @@ QImage PeerData::generateUserpicImage(
|
||||
size,
|
||||
size * Ui::ForumUserpicRadiusMultiplier());
|
||||
} else {
|
||||
ensureEmptyUserpic()->paintCircle(p, 0, 0, size, size);
|
||||
peer->ensureEmptyUserpic()->paintCircle(p, 0, 0, size, size);
|
||||
}
|
||||
p.end();
|
||||
|
||||
|
||||
@@ -333,10 +333,11 @@ public:
|
||||
[[nodiscard]] Ui::PeerUserpicView createUserpicView();
|
||||
[[nodiscard]] bool useEmptyUserpic(Ui::PeerUserpicView &view) const;
|
||||
[[nodiscard]] InMemoryKey userpicUniqueKey(Ui::PeerUserpicView &view) const;
|
||||
[[nodiscard]] QImage generateUserpicImage(
|
||||
[[nodiscard]] static QImage GenerateUserpicImage(
|
||||
not_null<PeerData*> peer,
|
||||
Ui::PeerUserpicView &view,
|
||||
int size,
|
||||
std::optional<int> radius = {}) const;
|
||||
std::optional<int> radius = {});
|
||||
[[nodiscard]] ImageLocation userpicLocation() const;
|
||||
|
||||
static constexpr auto kUnknownPhotoId = PhotoId(0xFFFFFFFFFFFFFFFFULL);
|
||||
|
||||
@@ -542,10 +542,12 @@ rpl::producer<QImage> PeerUserpicImageValue(
|
||||
}
|
||||
state->key = key;
|
||||
state->empty = false;
|
||||
consumer.put_next(peer->generateUserpicImage(
|
||||
state->view,
|
||||
size,
|
||||
radius));
|
||||
consumer.put_next(
|
||||
PeerData::GenerateUserpicImage(
|
||||
peer,
|
||||
state->view,
|
||||
size,
|
||||
radius));
|
||||
};
|
||||
peer->session().changes().peerFlagsValue(
|
||||
peer,
|
||||
|
||||
@@ -118,7 +118,7 @@ constexpr auto kBlurRadius = 24;
|
||||
const auto &partSize = partRect.width();
|
||||
const auto partSkip = fullSize - partSize;
|
||||
auto result = Images::Circle(BlurredDarkenedPart(
|
||||
peer->generateUserpicImage(view, fullSize * ratio, 0),
|
||||
PeerData::GenerateUserpicImage(peer, view, fullSize * ratio, 0),
|
||||
QRect(
|
||||
QPoint(partSkip, partSkip) * ratio,
|
||||
QSize(partSize, partSize) * ratio)));
|
||||
|
||||
@@ -3326,7 +3326,9 @@ void Widget::updateControlsGeometry() {
|
||||
}
|
||||
|
||||
const auto wasScrollTop = _scroll->scrollTop();
|
||||
const auto newScrollTop = (_topDelta < 0 && wasScrollTop <= 0)
|
||||
const auto newScrollTop = (wasScrollTop == 0)
|
||||
? wasScrollTop
|
||||
: (_topDelta < 0 && wasScrollTop <= 0)
|
||||
? wasScrollTop
|
||||
: (wasScrollTop + _topDelta);
|
||||
|
||||
|
||||
@@ -70,14 +70,18 @@ exportCancelButton: RoundButton(attentionBoxButton) {
|
||||
width: 200px;
|
||||
height: 44px;
|
||||
textTop: 12px;
|
||||
font: font(semibold 15px);
|
||||
style: TextStyle(semiboldTextStyle) {
|
||||
font: font(semibold 15px);
|
||||
}
|
||||
}
|
||||
exportCancelBottom: 30px;
|
||||
exportDoneButton: RoundButton(defaultActiveButton) {
|
||||
width: 200px;
|
||||
height: 44px;
|
||||
textTop: 12px;
|
||||
font: font(semibold 15px);
|
||||
style: TextStyle(semiboldTextStyle) {
|
||||
font: font(semibold 15px);
|
||||
}
|
||||
}
|
||||
|
||||
exportAboutLabel: FlatLabel(boxLabel) {
|
||||
|
||||
@@ -362,9 +362,9 @@ void ProgressWidget::showDone() {
|
||||
tr::lng_export_done(),
|
||||
st::exportDoneButton);
|
||||
const auto desired = std::min(
|
||||
st::exportDoneButton.font->width(tr::lng_export_done(tr::now))
|
||||
st::exportDoneButton.style.font->width(tr::lng_export_done(tr::now))
|
||||
+ st::exportDoneButton.height
|
||||
- st::exportDoneButton.font->height,
|
||||
- st::exportDoneButton.style.font->height,
|
||||
st::exportPanelSize.width() - 2 * st::exportCancelBottom);
|
||||
if (_done->width() < desired) {
|
||||
_done->setFullWidth(desired);
|
||||
|
||||
@@ -39,6 +39,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/chat/chat_style.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "ui/widgets/expandable_peer_list.h"
|
||||
#include "ui/widgets/menu/menu_add_action_callback_factory.h"
|
||||
#include "ui/widgets/menu/menu_add_action_callback.h"
|
||||
#include "ui/widgets/popup_menu.h"
|
||||
#include "ui/image/image.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
@@ -52,6 +54,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "lang/lang_keys.h"
|
||||
#include "boxes/peers/edit_participant_box.h"
|
||||
#include "boxes/peers/edit_participants_box.h"
|
||||
#include "data/data_changes.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_photo.h"
|
||||
#include "data/data_photo_media.h"
|
||||
@@ -1564,6 +1567,30 @@ void InnerWidget::suggestRestrictParticipant(
|
||||
}).send();
|
||||
}
|
||||
}, &st::menuIconPermissions);
|
||||
|
||||
{
|
||||
const auto lifetime = std::make_shared<rpl::lifetime>();
|
||||
auto handler = [=, this] {
|
||||
participant->session().changes().peerUpdates(
|
||||
_channel,
|
||||
Data::PeerUpdate::Flag::Members
|
||||
) | rpl::start_with_next([=](const Data::PeerUpdate &update) {
|
||||
_downLoaded = false;
|
||||
preloadMore(Direction::Down);
|
||||
lifetime->destroy();
|
||||
}, *lifetime);
|
||||
participant->session().api().chatParticipants().kick(
|
||||
_channel,
|
||||
participant,
|
||||
{ _channel->restrictions(), 0 });
|
||||
};
|
||||
Ui::Menu::CreateAddActionCallback(_menu)({
|
||||
.text = tr::lng_context_ban_user(tr::now),
|
||||
.handler = std::move(handler),
|
||||
.icon = &st::menuIconBlockAttention,
|
||||
.isAttention = true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void InnerWidget::restrictParticipant(
|
||||
|
||||
@@ -27,6 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "history/view/history_view_emoji_interactions.h"
|
||||
#include "history/history_item_components.h"
|
||||
#include "history/history_item_text.h"
|
||||
#include "history/history_view_swipe.h"
|
||||
#include "payments/payments_reaction_process.h"
|
||||
#include "ui/widgets/menu/menu_add_action_callback_factory.h"
|
||||
#include "ui/widgets/menu/menu_multiline_action.h"
|
||||
@@ -123,6 +124,15 @@ int BinarySearchBlocksOrItems(const T &list, int edge) {
|
||||
return start;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool CanSendReply(not_null<const HistoryItem*> item) {
|
||||
const auto peer = item->history()->peer;
|
||||
const auto topic = item->topic();
|
||||
return topic
|
||||
? Data::CanSendAnything(topic)
|
||||
: (Data::CanSendAnything(peer)
|
||||
&& (!peer->isChannel() || peer->asChannel()->amIn()));
|
||||
}
|
||||
|
||||
void FillSponsoredMessagesMenu(
|
||||
not_null<Window::SessionController*> controller,
|
||||
FullMsgId itemId,
|
||||
@@ -485,6 +495,7 @@ HistoryInner::HistoryInner(
|
||||
}, _scroll->lifetime());
|
||||
|
||||
setupSharingDisallowed();
|
||||
setupSwipeReply();
|
||||
}
|
||||
|
||||
void HistoryInner::reactionChosen(const ChosenReaction &reaction) {
|
||||
@@ -567,6 +578,72 @@ void HistoryInner::setupSharingDisallowed() {
|
||||
}, lifetime());
|
||||
}
|
||||
|
||||
void HistoryInner::setupSwipeReply() {
|
||||
HistoryView::SetupSwipeHandler(this, _scroll, [=, history = _history](
|
||||
HistoryView::ChatPaintGestureHorizontalData data) {
|
||||
const auto changed = (_gestureHorizontal.msgBareId != data.msgBareId)
|
||||
|| (_gestureHorizontal.translation != data.translation)
|
||||
|| (_gestureHorizontal.reachRatio != data.reachRatio);
|
||||
_gestureHorizontal = data;
|
||||
if (changed) {
|
||||
const auto item = history->peer->owner().message(
|
||||
history->peer->id,
|
||||
MsgId{ data.msgBareId });
|
||||
if (item) {
|
||||
repaintItem(item);
|
||||
}
|
||||
}
|
||||
}, [=, show = _controller->uiShow()](int cursorTop) {
|
||||
auto result = HistoryView::SwipeHandlerFinishData();
|
||||
if (inSelectionMode()) {
|
||||
return result;
|
||||
}
|
||||
enumerateItems<EnumItemsDirection::BottomToTop>([&](
|
||||
not_null<Element*> view,
|
||||
int itemtop,
|
||||
int itembottom) {
|
||||
if ((cursorTop < itemtop)
|
||||
|| (cursorTop > itembottom)
|
||||
|| !view->data()->isRegular()
|
||||
|| view->data()->isService()) {
|
||||
return true;
|
||||
}
|
||||
const auto item = view->data();
|
||||
const auto canSendReply = CanSendReply(item);
|
||||
const auto canReply = (canSendReply || item->allowsForward());
|
||||
if (!canReply) {
|
||||
return true;
|
||||
}
|
||||
result.msgBareId = item->fullId().msg.bare;
|
||||
result.callback = [=, itemId = item->fullId()] {
|
||||
const auto still = show->session().data().message(itemId);
|
||||
const auto selected = selectedQuote(still);
|
||||
const auto replyToItemId = (selected.item
|
||||
? selected.item
|
||||
: still)->fullId();
|
||||
if (canSendReply) {
|
||||
_widget->replyToMessage({
|
||||
.messageId = replyToItemId,
|
||||
.quote = selected.text,
|
||||
.quoteOffset = selected.offset,
|
||||
});
|
||||
if (!selected.text.empty()) {
|
||||
_widget->clearSelected();
|
||||
}
|
||||
} else {
|
||||
HistoryView::Controls::ShowReplyToChatBox(show, {
|
||||
.messageId = replyToItemId,
|
||||
.quote = selected.text,
|
||||
.quoteOffset = selected.offset,
|
||||
});
|
||||
}
|
||||
};
|
||||
return false;
|
||||
});
|
||||
return result;
|
||||
}, _touchMaybeSelecting.value());
|
||||
}
|
||||
|
||||
bool HistoryInner::hasSelectRestriction() const {
|
||||
if (!_sharingDisallowed.current()) {
|
||||
return false;
|
||||
@@ -944,6 +1021,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
|
||||
auto clip = e->rect();
|
||||
|
||||
auto context = preparePaintContext(clip);
|
||||
context.gestureHorizontal = _gestureHorizontal;
|
||||
context.highlightPathCache = &_highlightPathCache;
|
||||
_pathGradient->startFrame(
|
||||
0,
|
||||
@@ -1157,6 +1235,20 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
|
||||
// paint the userpic if it intersects the painted rect
|
||||
if (userpicTop + st::msgPhotoSize > clip.top()) {
|
||||
const auto item = view->data();
|
||||
const auto hasTranslation = context.gestureHorizontal.translation
|
||||
&& (context.gestureHorizontal.msgBareId
|
||||
== item->fullId().msg.bare);
|
||||
if (hasTranslation) {
|
||||
p.translate(context.gestureHorizontal.translation, 0);
|
||||
update(
|
||||
QRect(
|
||||
st::historyPhotoLeft
|
||||
+ context.gestureHorizontal.translation,
|
||||
userpicTop,
|
||||
st::msgPhotoSize
|
||||
- context.gestureHorizontal.translation,
|
||||
st::msgPhotoSize));
|
||||
}
|
||||
if (const auto from = item->displayFrom()) {
|
||||
Dialogs::Ui::PaintUserpic(
|
||||
p,
|
||||
@@ -1192,6 +1284,9 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
|
||||
} else {
|
||||
Unexpected("Corrupt forwarded information in message.");
|
||||
}
|
||||
if (hasTranslation) {
|
||||
p.translate(-_gestureHorizontal.translation, 0);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
@@ -1412,6 +1507,7 @@ void HistoryInner::touchEvent(QTouchEvent *e) {
|
||||
_touchScroll = _touchSelect = false;
|
||||
_horizontalScrollLocked = false;
|
||||
_touchScrollState = Ui::TouchScrollState::Manual;
|
||||
_touchMaybeSelecting = false;
|
||||
mouseActionCancel();
|
||||
return;
|
||||
}
|
||||
@@ -1434,6 +1530,7 @@ void HistoryInner::touchEvent(QTouchEvent *e) {
|
||||
_touchInProgress = true;
|
||||
_horizontalScrollLocked = false;
|
||||
if (_touchScrollState == Ui::TouchScrollState::Auto) {
|
||||
_touchMaybeSelecting = false;
|
||||
_touchScrollState = Ui::TouchScrollState::Acceleration;
|
||||
_touchWaitingAcceleration = true;
|
||||
_touchAccelerationTime = crl::now();
|
||||
@@ -1441,6 +1538,7 @@ void HistoryInner::touchEvent(QTouchEvent *e) {
|
||||
_touchStart = _touchPos;
|
||||
} else {
|
||||
_touchScroll = false;
|
||||
_touchMaybeSelecting = true;
|
||||
_touchSelectTimer.callOnce(QApplication::startDragTime());
|
||||
}
|
||||
_touchSelect = false;
|
||||
@@ -1454,6 +1552,7 @@ void HistoryInner::touchEvent(QTouchEvent *e) {
|
||||
mouseActionUpdate(_touchPos);
|
||||
} else if (!_touchScroll && (_touchPos - _touchStart).manhattanLength() >= QApplication::startDragDistance()) {
|
||||
_touchSelectTimer.cancel();
|
||||
_touchMaybeSelecting = false;
|
||||
_touchScroll = true;
|
||||
touchUpdateSpeed();
|
||||
}
|
||||
@@ -1475,11 +1574,18 @@ void HistoryInner::touchEvent(QTouchEvent *e) {
|
||||
return;
|
||||
}
|
||||
_touchInProgress = false;
|
||||
const auto notMoved = (_touchPos - _touchStart).manhattanLength()
|
||||
< QApplication::startDragDistance();
|
||||
auto weak = Ui::MakeWeak(this);
|
||||
if (_touchSelect) {
|
||||
mouseActionFinish(_touchPos, Qt::RightButton);
|
||||
QContextMenuEvent contextMenu(QContextMenuEvent::Mouse, mapFromGlobal(_touchPos), _touchPos);
|
||||
showContextMenu(&contextMenu, true);
|
||||
if (notMoved || _touchMaybeSelecting.current()) {
|
||||
mouseActionFinish(_touchPos, Qt::RightButton);
|
||||
auto contextMenu = QContextMenuEvent(
|
||||
QContextMenuEvent::Mouse,
|
||||
mapFromGlobal(_touchPos),
|
||||
_touchPos);
|
||||
showContextMenu(&contextMenu, true);
|
||||
}
|
||||
_touchScroll = false;
|
||||
} else if (_touchScroll) {
|
||||
if (_touchScrollState == Ui::TouchScrollState::Manual) {
|
||||
@@ -1497,12 +1603,13 @@ void HistoryInner::touchEvent(QTouchEvent *e) {
|
||||
_touchWaitingAcceleration = false;
|
||||
_touchPrevPosValid = false;
|
||||
}
|
||||
} else { // One short tap is like left mouse click.
|
||||
} else if (notMoved) { // One short tap is like left mouse click.
|
||||
mouseActionStart(_touchPos, Qt::LeftButton);
|
||||
mouseActionFinish(_touchPos, Qt::LeftButton);
|
||||
}
|
||||
if (weak) {
|
||||
_touchSelectTimer.cancel();
|
||||
_touchMaybeSelecting = false;
|
||||
_touchSelect = false;
|
||||
}
|
||||
} break;
|
||||
@@ -2461,14 +2568,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
||||
if (!item || !item->isRegular()) {
|
||||
return;
|
||||
}
|
||||
const auto canSendReply = [&] {
|
||||
const auto peer = item->history()->peer;
|
||||
const auto topic = item->topic();
|
||||
return topic
|
||||
? Data::CanSendAnything(topic)
|
||||
: (Data::CanSendAnything(peer)
|
||||
&& (!peer->isChannel() || peer->asChannel()->amIn()));
|
||||
}();
|
||||
const auto canSendReply = CanSendReply(item);
|
||||
const auto canReply = canSendReply || item->allowsForward();
|
||||
if (canReply) {
|
||||
const auto selected = selectedQuote(item);
|
||||
@@ -3711,6 +3811,7 @@ MessageIdsList HistoryInner::getSelectedItems() const {
|
||||
|
||||
void HistoryInner::onTouchSelect() {
|
||||
_touchSelect = true;
|
||||
_touchMaybeSelecting = true;
|
||||
mouseActionStart(_touchPos, Qt::LeftButton);
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/dragging_scroll_manager.h"
|
||||
#include "ui/widgets/tooltip.h"
|
||||
#include "ui/widgets/scroll_area.h"
|
||||
#include "history/history_view_swipe_data.h"
|
||||
#include "history/view/history_view_top_bar_widget.h"
|
||||
|
||||
#include <QtGui/QPainterPath>
|
||||
@@ -419,6 +420,7 @@ private:
|
||||
void reactionChosen(const ChosenReaction &reaction);
|
||||
|
||||
void setupSharingDisallowed();
|
||||
void setupSwipeReply();
|
||||
[[nodiscard]] bool hasCopyRestriction(HistoryItem *item = nullptr) const;
|
||||
[[nodiscard]] bool hasCopyMediaRestriction(
|
||||
not_null<HistoryItem*> item) const;
|
||||
@@ -511,6 +513,7 @@ private:
|
||||
bool _touchSelect = false;
|
||||
bool _touchInProgress = false;
|
||||
QPoint _touchStart, _touchPrevPos, _touchPos;
|
||||
rpl::variable<bool> _touchMaybeSelecting;
|
||||
base::Timer _touchSelectTimer;
|
||||
|
||||
Ui::DraggingScrollManager _selectScroll;
|
||||
@@ -526,6 +529,8 @@ private:
|
||||
crl::time _touchTime = 0;
|
||||
base::Timer _touchScrollTimer;
|
||||
|
||||
HistoryView::ChatPaintGestureHorizontalData _gestureHorizontal;
|
||||
|
||||
// _menu must be destroyed before _whoReactedMenuLifetime.
|
||||
rpl::lifetime _whoReactedMenuLifetime;
|
||||
base::unique_qptr<Ui::PopupMenu> _menu;
|
||||
|
||||
@@ -1847,6 +1847,12 @@ void HistoryItem::applyEdition(
|
||||
}
|
||||
|
||||
void HistoryItem::applySentMessage(const MTPDmessage &data) {
|
||||
if (data.is_invert_media()) {
|
||||
_flags |= MessageFlag::InvertMedia;
|
||||
} else {
|
||||
_flags &= ~MessageFlag::InvertMedia;
|
||||
}
|
||||
|
||||
updateSentContent({
|
||||
qs(data.vmessage()),
|
||||
Api::EntitiesFromMTP(
|
||||
@@ -3454,9 +3460,10 @@ ItemPreview HistoryItem::toPreview(ToPreviewOptions options) const {
|
||||
return _media->toPreview(options);
|
||||
} else if (!emptyText()) {
|
||||
return {
|
||||
.text = st::wrap_rtl(options.translated
|
||||
? translatedText()
|
||||
: _text)
|
||||
// wrap_rtl "adds" a newline in case text starts with quote.
|
||||
// So we remove those by DialogsPreviewText call.
|
||||
.text = st::wrap_rtl(Dialogs::Ui::DialogsPreviewText(
|
||||
options.translated ? translatedText() : _text))
|
||||
};
|
||||
}
|
||||
return {};
|
||||
|
||||
247
Telegram/SourceFiles/history/history_view_swipe.cpp
Normal file
247
Telegram/SourceFiles/history/history_view_swipe.cpp
Normal file
@@ -0,0 +1,247 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "history/history_view_swipe.h"
|
||||
|
||||
#include "base/platform/base_platform_haptic.h"
|
||||
#include "base/platform/base_platform_info.h"
|
||||
#include "base/qt/qt_common_adapters.h"
|
||||
#include "base/event_filter.h"
|
||||
#include "history/history_view_swipe_data.h"
|
||||
#include "ui/chat/chat_style.h"
|
||||
#include "ui/ui_utility.h"
|
||||
#include "ui/widgets/elastic_scroll.h"
|
||||
#include "ui/widgets/scroll_area.h"
|
||||
|
||||
#include <QtWidgets/QApplication>
|
||||
|
||||
namespace HistoryView {
|
||||
|
||||
void SetupSwipeHandler(
|
||||
not_null<Ui::RpWidget*> widget,
|
||||
not_null<Ui::ScrollArea*> scroll,
|
||||
Fn<void(ChatPaintGestureHorizontalData)> update,
|
||||
Fn<SwipeHandlerFinishData(int)> generateFinishByTop,
|
||||
rpl::producer<bool> dontStart) {
|
||||
constexpr auto kThresholdWidth = 50;
|
||||
constexpr auto kMaxRatio = 1.5;
|
||||
const auto threshold = style::ConvertFloatScale(kThresholdWidth);
|
||||
struct State {
|
||||
base::unique_qptr<QObject> filter;
|
||||
Ui::Animations::Simple animationReach;
|
||||
Ui::Animations::Simple animationEnd;
|
||||
ChatPaintGestureHorizontalData data;
|
||||
SwipeHandlerFinishData finishByTopData;
|
||||
std::optional<Qt::Orientation> orientation;
|
||||
QPointF startAt;
|
||||
QPointF delta;
|
||||
int cursorTop = 0;
|
||||
bool dontStart = false;
|
||||
bool started = false;
|
||||
bool reached = false;
|
||||
bool touch = false;
|
||||
|
||||
rpl::lifetime lifetime;
|
||||
};
|
||||
const auto state = widget->lifetime().make_state<State>();
|
||||
std::move(
|
||||
dontStart
|
||||
) | rpl::start_with_next([=](bool dontStart) {
|
||||
state->dontStart = dontStart;
|
||||
}, state->lifetime);
|
||||
|
||||
const auto updateRatio = [=](float64 ratio) {
|
||||
state->data.ratio = std::clamp(ratio, 0., kMaxRatio),
|
||||
state->data.msgBareId = state->finishByTopData.msgBareId;
|
||||
state->data.translation = int(
|
||||
base::SafeRound(-std::clamp(ratio, 0., kMaxRatio) * threshold));
|
||||
state->data.cursorTop = state->cursorTop;
|
||||
update(state->data);
|
||||
};
|
||||
const auto setOrientation = [=](std::optional<Qt::Orientation> o) {
|
||||
state->orientation = o;
|
||||
const auto isHorizontal = (o == Qt::Horizontal);
|
||||
scroll->viewport()->setAttribute(
|
||||
Qt::WA_AcceptTouchEvents,
|
||||
!isHorizontal);
|
||||
scroll->disableScroll(isHorizontal);
|
||||
};
|
||||
const auto processEnd = [=](std::optional<QPointF> delta = {}) {
|
||||
if (state->orientation == Qt::Horizontal) {
|
||||
const auto ratio = std::clamp(
|
||||
delta.value_or(state->delta).x() / threshold,
|
||||
0.,
|
||||
kMaxRatio);
|
||||
if ((ratio >= 1) && state->finishByTopData.callback) {
|
||||
Ui::PostponeCall(
|
||||
widget,
|
||||
state->finishByTopData.callback);
|
||||
}
|
||||
state->animationEnd.stop();
|
||||
state->animationEnd.start(
|
||||
updateRatio,
|
||||
ratio,
|
||||
0.,
|
||||
std::min(1., ratio) * st::slideWrapDuration);
|
||||
}
|
||||
setOrientation(std::nullopt);
|
||||
state->started = false;
|
||||
state->reached = false;
|
||||
};
|
||||
scroll->scrolls() | rpl::start_with_next([=] {
|
||||
if (state->orientation != Qt::Vertical) {
|
||||
processEnd();
|
||||
}
|
||||
}, state->lifetime);
|
||||
const auto animationReachCallback = [=](float64 value) {
|
||||
state->data.reachRatio = value;
|
||||
update(state->data);
|
||||
};
|
||||
struct UpdateArgs {
|
||||
QPoint globalCursor;
|
||||
QPointF position;
|
||||
QPointF delta;
|
||||
bool touch = false;
|
||||
};
|
||||
const auto updateWith = [=](UpdateArgs &&args) {
|
||||
if (!state->started || state->touch != args.touch) {
|
||||
state->started = true;
|
||||
state->touch = args.touch;
|
||||
state->startAt = args.position;
|
||||
state->delta = QPointF();
|
||||
state->cursorTop = widget->mapFromGlobal(args.globalCursor).y();
|
||||
state->finishByTopData = generateFinishByTop(
|
||||
state->cursorTop);
|
||||
if (!state->finishByTopData.callback) {
|
||||
setOrientation(Qt::Vertical);
|
||||
}
|
||||
} else if (!state->orientation) {
|
||||
state->delta = args.delta;
|
||||
const auto diffXtoY = std::abs(args.delta.x())
|
||||
- std::abs(args.delta.y());
|
||||
constexpr auto kOrientationThreshold = 1.;
|
||||
if (diffXtoY > kOrientationThreshold) {
|
||||
if (!state->dontStart) {
|
||||
setOrientation(Qt::Horizontal);
|
||||
}
|
||||
} else if (diffXtoY < -kOrientationThreshold) {
|
||||
setOrientation(Qt::Vertical);
|
||||
} else {
|
||||
setOrientation(std::nullopt);
|
||||
}
|
||||
} else if (*state->orientation == Qt::Horizontal) {
|
||||
state->delta = args.delta;
|
||||
const auto ratio = args.delta.x() / threshold;
|
||||
updateRatio(ratio);
|
||||
constexpr auto kResetReachedOn = 0.95;
|
||||
constexpr auto kBounceDuration = crl::time(500);
|
||||
if (!state->reached && ratio >= 1.) {
|
||||
state->reached = true;
|
||||
state->animationReach.stop();
|
||||
state->animationReach.start(
|
||||
animationReachCallback,
|
||||
0.,
|
||||
1.,
|
||||
kBounceDuration);
|
||||
base::Platform::Haptic();
|
||||
} else if (state->reached
|
||||
&& ratio < kResetReachedOn) {
|
||||
state->reached = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
const auto filter = [=](not_null<QEvent*> e) {
|
||||
const auto type = e->type();
|
||||
switch (type) {
|
||||
case QEvent::Leave: {
|
||||
if (state->orientation == Qt::Horizontal) {
|
||||
processEnd();
|
||||
}
|
||||
} break;
|
||||
case QEvent::MouseMove: {
|
||||
if (state->orientation == Qt::Horizontal) {
|
||||
const auto m = static_cast<QMouseEvent*>(e.get());
|
||||
if (std::abs(m->pos().y() - state->cursorTop)
|
||||
> QApplication::startDragDistance()) {
|
||||
processEnd();
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case QEvent::TouchBegin:
|
||||
case QEvent::TouchUpdate:
|
||||
case QEvent::TouchEnd:
|
||||
case QEvent::TouchCancel: {
|
||||
const auto t = static_cast<QTouchEvent*>(e.get());
|
||||
const auto touchscreen = t->device()
|
||||
&& (t->device()->type() == base::TouchDevice::TouchScreen);
|
||||
if (!Platform::IsMac() && !touchscreen) {
|
||||
break;
|
||||
} else if (type == QEvent::TouchBegin) {
|
||||
// Reset state in case we lost some TouchEnd.
|
||||
processEnd();
|
||||
}
|
||||
const auto &touches = t->touchPoints();
|
||||
const auto released = [&](int index) {
|
||||
return (touches.size() > index)
|
||||
&& (touches.at(index).state() & Qt::TouchPointReleased);
|
||||
};
|
||||
const auto cancel = released(0)
|
||||
|| released(1)
|
||||
|| (touchscreen
|
||||
? (touches.size() != 1)
|
||||
: (touches.size() <= 0 || touches.size() > 2))
|
||||
|| (type == QEvent::TouchEnd)
|
||||
|| (type == QEvent::TouchCancel);
|
||||
if (cancel) {
|
||||
processEnd(touches.empty()
|
||||
? std::optional<QPointF>()
|
||||
: (state->startAt - touches[0].pos()));
|
||||
} else {
|
||||
updateWith({
|
||||
.globalCursor = (touchscreen
|
||||
? touches[0].screenPos().toPoint()
|
||||
: QCursor::pos()),
|
||||
.position = touches[0].pos(),
|
||||
.delta = state->startAt - touches[0].pos(),
|
||||
.touch = true,
|
||||
});
|
||||
}
|
||||
return (touchscreen && state->orientation != Qt::Horizontal)
|
||||
? base::EventFilterResult::Continue
|
||||
: base::EventFilterResult::Cancel;
|
||||
} break;
|
||||
case QEvent::Wheel: {
|
||||
const auto w = static_cast<QWheelEvent*>(e.get());
|
||||
const auto phase = w->phase();
|
||||
if (Platform::IsMac() || phase == Qt::NoScrollPhase) {
|
||||
break;
|
||||
} else if (phase == Qt::ScrollBegin) {
|
||||
// Reset state in case we lost some TouchEnd.
|
||||
processEnd();
|
||||
}
|
||||
const auto cancel = w->buttons()
|
||||
|| (phase == Qt::ScrollEnd)
|
||||
|| (phase == Qt::ScrollMomentum);
|
||||
if (cancel) {
|
||||
processEnd();
|
||||
} else {
|
||||
updateWith({
|
||||
.globalCursor = w->globalPosition().toPoint(),
|
||||
.position = QPointF(),
|
||||
.delta = state->delta - Ui::ScrollDelta(w),
|
||||
.touch = false,
|
||||
});
|
||||
}
|
||||
} break;
|
||||
}
|
||||
return base::EventFilterResult::Continue;
|
||||
};
|
||||
state->filter = base::make_unique_q<QObject>(
|
||||
base::install_event_filter(widget, filter));
|
||||
}
|
||||
|
||||
} // namespace HistoryView
|
||||
31
Telegram/SourceFiles/history/history_view_swipe.h
Normal file
31
Telegram/SourceFiles/history/history_view_swipe.h
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
namespace Ui {
|
||||
class RpWidget;
|
||||
class ScrollArea;
|
||||
} // namespace Ui
|
||||
|
||||
namespace HistoryView {
|
||||
|
||||
struct ChatPaintGestureHorizontalData;
|
||||
|
||||
struct SwipeHandlerFinishData {
|
||||
Fn<void(void)> callback;
|
||||
int64 msgBareId = 0;
|
||||
};
|
||||
|
||||
void SetupSwipeHandler(
|
||||
not_null<Ui::RpWidget*> widget,
|
||||
not_null<Ui::ScrollArea*> scroll,
|
||||
Fn<void(ChatPaintGestureHorizontalData)> update,
|
||||
Fn<SwipeHandlerFinishData(int)> generateFinishByTop,
|
||||
rpl::producer<bool> dontStart = nullptr);
|
||||
|
||||
} // namespace HistoryView
|
||||
20
Telegram/SourceFiles/history/history_view_swipe_data.h
Normal file
20
Telegram/SourceFiles/history/history_view_swipe_data.h
Normal file
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
namespace HistoryView {
|
||||
|
||||
struct ChatPaintGestureHorizontalData {
|
||||
float64 ratio = 0.;
|
||||
float64 reachRatio = 0.;
|
||||
int64 msgBareId = 0;
|
||||
int translation = 0;
|
||||
int cursorTop = 0;
|
||||
};
|
||||
|
||||
} // namespace HistoryView
|
||||
@@ -6469,8 +6469,11 @@ void HistoryWidget::updateBotKeyboard(History *h, bool force) {
|
||||
: nullptr;
|
||||
changed = _keyboard->updateMarkup(keyboardItem, force);
|
||||
}
|
||||
updateCmdStartShown();
|
||||
const auto controlsChanged = updateCmdStartShown();
|
||||
if (!changed) {
|
||||
if (controlsChanged) {
|
||||
updateControlsGeometry();
|
||||
}
|
||||
return;
|
||||
} else if (_keyboard->forMsgId() != wasMsgId) {
|
||||
_kbScroll->scrollTo({ 0, 0 });
|
||||
|
||||
@@ -315,6 +315,10 @@ void UnreadBar::paint(
|
||||
int y,
|
||||
int w,
|
||||
bool chatWide) const {
|
||||
const auto previousTranslation = p.transform().dx();
|
||||
if (previousTranslation != 0) {
|
||||
p.translate(-previousTranslation, 0);
|
||||
}
|
||||
const auto st = context.st;
|
||||
const auto bottom = y + height();
|
||||
y += marginTop();
|
||||
@@ -350,6 +354,9 @@ void UnreadBar::paint(
|
||||
(w - width) / 2,
|
||||
y + (skip / 2) + st::historyUnreadBarFont->ascent,
|
||||
text);
|
||||
if (previousTranslation != 0) {
|
||||
p.translate(previousTranslation, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void DateBadge::init(const QString &date) {
|
||||
|
||||
@@ -142,7 +142,8 @@ rpl::producer<Ui::GroupCallBarContent> GroupCallBarContentByCall(
|
||||
state->someUserpicsNotLoaded = false;
|
||||
for (auto &userpic : state->userpics) {
|
||||
userpic.peer->loadUserpic();
|
||||
auto image = userpic.peer->generateUserpicImage(
|
||||
auto image = PeerData::GenerateUserpicImage(
|
||||
userpic.peer,
|
||||
userpic.view,
|
||||
userpicSize * style::DevicePixelRatio());
|
||||
userpic.uniqueKey = userpic.peer->userpicUniqueKey(userpic.view);
|
||||
|
||||
@@ -2305,6 +2305,20 @@ void ListWidget::paintUserpics(
|
||||
// paint the userpic if it intersects the painted rect
|
||||
if (userpicTop + st::msgPhotoSize > clip.top()) {
|
||||
const auto item = view->data();
|
||||
const auto hasTranslation = context.gestureHorizontal.translation
|
||||
&& (context.gestureHorizontal.msgBareId
|
||||
== item->fullId().msg.bare);
|
||||
if (hasTranslation) {
|
||||
p.translate(context.gestureHorizontal.translation, 0);
|
||||
update(
|
||||
QRect(
|
||||
st::historyPhotoLeft
|
||||
+ context.gestureHorizontal.translation,
|
||||
userpicTop,
|
||||
st::msgPhotoSize
|
||||
- context.gestureHorizontal.translation,
|
||||
st::msgPhotoSize));
|
||||
}
|
||||
if (const auto from = item->displayFrom()) {
|
||||
from->paintUserpicLeft(
|
||||
p,
|
||||
@@ -2337,6 +2351,9 @@ void ListWidget::paintUserpics(
|
||||
} else {
|
||||
Unexpected("Corrupt forwarded information in message.");
|
||||
}
|
||||
if (hasTranslation) {
|
||||
p.translate(-context.gestureHorizontal.translation, 0);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
@@ -2932,6 +2949,7 @@ void ListWidget::touchEvent(QTouchEvent *e) {
|
||||
_touchSelectTimer.cancel();
|
||||
_touchScroll = _touchSelect = false;
|
||||
_touchScrollState = Ui::TouchScrollState::Manual;
|
||||
_touchMaybeSelecting = false;
|
||||
mouseActionCancel();
|
||||
return;
|
||||
}
|
||||
@@ -2952,6 +2970,7 @@ void ListWidget::touchEvent(QTouchEvent *e) {
|
||||
|
||||
_touchInProgress = true;
|
||||
if (_touchScrollState == Ui::TouchScrollState::Auto) {
|
||||
_touchMaybeSelecting = false;
|
||||
_touchScrollState = Ui::TouchScrollState::Acceleration;
|
||||
_touchWaitingAcceleration = true;
|
||||
_touchAccelerationTime = crl::now();
|
||||
@@ -2959,6 +2978,7 @@ void ListWidget::touchEvent(QTouchEvent *e) {
|
||||
_touchStart = _touchPos;
|
||||
} else {
|
||||
_touchScroll = false;
|
||||
_touchMaybeSelecting = true;
|
||||
_touchSelectTimer.callOnce(QApplication::startDragTime());
|
||||
}
|
||||
_touchSelect = false;
|
||||
@@ -2971,6 +2991,7 @@ void ListWidget::touchEvent(QTouchEvent *e) {
|
||||
mouseActionUpdate(_touchPos);
|
||||
} else if (!_touchScroll && (_touchPos - _touchStart).manhattanLength() >= QApplication::startDragDistance()) {
|
||||
_touchSelectTimer.cancel();
|
||||
_touchMaybeSelecting = false;
|
||||
_touchScroll = true;
|
||||
touchUpdateSpeed();
|
||||
}
|
||||
@@ -2988,13 +3009,22 @@ void ListWidget::touchEvent(QTouchEvent *e) {
|
||||
} break;
|
||||
|
||||
case QEvent::TouchEnd: {
|
||||
if (!_touchInProgress) return;
|
||||
if (!_touchInProgress) {
|
||||
return;
|
||||
}
|
||||
_touchInProgress = false;
|
||||
auto weak = Ui::MakeWeak(this);
|
||||
const auto notMoved = (_touchPos - _touchStart).manhattanLength()
|
||||
< QApplication::startDragDistance();
|
||||
if (_touchSelect) {
|
||||
mouseActionFinish(_touchPos, Qt::RightButton);
|
||||
QContextMenuEvent contextMenu(QContextMenuEvent::Mouse, mapFromGlobal(_touchPos), _touchPos);
|
||||
showContextMenu(&contextMenu, true);
|
||||
if (notMoved || _touchMaybeSelecting.current()) {
|
||||
mouseActionFinish(_touchPos, Qt::RightButton);
|
||||
auto contextMenu = QContextMenuEvent(
|
||||
QContextMenuEvent::Mouse,
|
||||
mapFromGlobal(_touchPos),
|
||||
_touchPos);
|
||||
showContextMenu(&contextMenu, true);
|
||||
}
|
||||
_touchScroll = false;
|
||||
} else if (_touchScroll) {
|
||||
if (_touchScrollState == Ui::TouchScrollState::Manual) {
|
||||
@@ -3011,12 +3041,13 @@ void ListWidget::touchEvent(QTouchEvent *e) {
|
||||
_touchWaitingAcceleration = false;
|
||||
_touchPrevPosValid = false;
|
||||
}
|
||||
} else { // One short tap is like left mouse click.
|
||||
} else if (notMoved) { // One short tap is like left mouse click.
|
||||
mouseActionStart(_touchPos, Qt::LeftButton);
|
||||
mouseActionFinish(_touchPos, Qt::LeftButton);
|
||||
}
|
||||
if (weak) {
|
||||
_touchSelectTimer.cancel();
|
||||
_touchMaybeSelecting = false;
|
||||
_touchSelect = false;
|
||||
}
|
||||
} break;
|
||||
@@ -3056,6 +3087,10 @@ void ListWidget::touchScrollUpdated(const QPoint &screenPos) {
|
||||
touchUpdateSpeed();
|
||||
}
|
||||
|
||||
rpl::producer<bool> ListWidget::touchMaybeSelectingValue() const {
|
||||
return _touchMaybeSelecting.value();
|
||||
}
|
||||
|
||||
void ListWidget::enterEventHook(QEnterEvent *e) {
|
||||
mouseActionUpdate(QCursor::pos());
|
||||
return TWidget::enterEventHook(e);
|
||||
@@ -3112,6 +3147,7 @@ void ListWidget::updateDragSelection() {
|
||||
|
||||
void ListWidget::onTouchSelect() {
|
||||
_touchSelect = true;
|
||||
_touchMaybeSelecting = true;
|
||||
mouseActionStart(_touchPos, Qt::LeftButton);
|
||||
}
|
||||
|
||||
|
||||
@@ -327,6 +327,7 @@ public:
|
||||
void selectItemAsGroup(not_null<HistoryItem*> item);
|
||||
|
||||
void touchScrollUpdated(const QPoint &screenPos);
|
||||
[[nodiscard]] rpl::producer<bool> touchMaybeSelectingValue() const;
|
||||
|
||||
[[nodiscard]] bool loadedAtTopKnown() const;
|
||||
[[nodiscard]] bool loadedAtTop() const;
|
||||
@@ -830,6 +831,7 @@ private:
|
||||
bool _touchSelect = false;
|
||||
bool _touchInProgress = false;
|
||||
QPoint _touchStart, _touchPrevPos, _touchPos;
|
||||
rpl::variable<bool> _touchMaybeSelecting;
|
||||
base::Timer _touchSelectTimer;
|
||||
|
||||
Ui::DraggingScrollManager _selectScroll;
|
||||
|
||||
@@ -40,6 +40,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "payments/payments_reaction_process.h" // TryAddingPaidReaction.
|
||||
#include "ui/text/text_options.h"
|
||||
#include "ui/painter.h"
|
||||
#include "window/themes/window_theme.h" // IsNightMode.
|
||||
#include "window/window_session_controller.h"
|
||||
#include "apiwrap.h"
|
||||
#include "styles/style_chat.h"
|
||||
@@ -1091,6 +1092,12 @@ void Message::draw(Painter &p, const PaintContext &context) const {
|
||||
const auto item = data();
|
||||
const auto media = this->media();
|
||||
|
||||
const auto hasGesture = context.gestureHorizontal.translation
|
||||
&& (context.gestureHorizontal.msgBareId == item->fullId().msg.bare);
|
||||
if (hasGesture) {
|
||||
p.translate(context.gestureHorizontal.translation, 0);
|
||||
}
|
||||
|
||||
if (item->hasUnrequestedFactcheck()) {
|
||||
item->history()->session().factchecks().requestFor(item);
|
||||
}
|
||||
@@ -1481,6 +1488,77 @@ void Message::draw(Painter &p, const PaintContext &context) const {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hasGesture) {
|
||||
p.translate(-context.gestureHorizontal.translation, 0);
|
||||
|
||||
constexpr auto kShiftRatio = 1.5;
|
||||
constexpr auto kBouncePart = 0.25;
|
||||
constexpr auto kMaxHeightRatio = 3.5;
|
||||
constexpr auto kStrokeWidth = 2.;
|
||||
constexpr auto kWaveWidth = 10.;
|
||||
const auto isLeftSize = (!context.outbg)
|
||||
|| delegate()->elementIsChatWide();
|
||||
const auto ratio = std::min(context.gestureHorizontal.ratio, 1.);
|
||||
const auto reachRatio = context.gestureHorizontal.reachRatio;
|
||||
const auto size = st::historyFastShareSize;
|
||||
const auto outerWidth = st::historySwipeIconSkip
|
||||
+ (isLeftSize ? rect::right(g) : width())
|
||||
+ ((g.height() < size * kMaxHeightRatio)
|
||||
? rightActionSize().value_or(QSize()).width()
|
||||
: 0);
|
||||
const auto rect = QRectF(
|
||||
outerWidth
|
||||
- (size * kShiftRatio * context.gestureHorizontal.ratio)
|
||||
- (st::historySwipeIconSkip * ratio * (isLeftSize ? .7 : 1.)),
|
||||
g.y() + (g.height() - size) / 2,
|
||||
size,
|
||||
size);
|
||||
const auto center = rect::center(rect);
|
||||
const auto spanAngle = ratio * arc::kFullLength;
|
||||
const auto strokeWidth = style::ConvertFloatScale(kStrokeWidth);
|
||||
|
||||
const auto reachScale = std::clamp(
|
||||
(reachRatio > kBouncePart)
|
||||
? (kBouncePart * 2 - reachRatio)
|
||||
: reachRatio,
|
||||
0.,
|
||||
1.);
|
||||
auto pen = Window::Theme::IsNightMode()
|
||||
? QPen(anim::with_alpha(context.st->msgServiceFg()->c, 0.3))
|
||||
: QPen(context.st->msgServiceBg());
|
||||
pen.setWidthF(strokeWidth - (1. * (reachScale / kBouncePart)));
|
||||
const auto arcRect = rect - Margins(strokeWidth);
|
||||
p.save();
|
||||
{
|
||||
auto hq = PainterHighQualityEnabler(p);
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(context.st->msgServiceBg());
|
||||
p.setOpacity(ratio);
|
||||
p.translate(center);
|
||||
if (reachScale) {
|
||||
p.scale(-(1. + 1. * reachScale), (1. + 1. * reachScale));
|
||||
} else {
|
||||
p.scale(-1., 1.);
|
||||
}
|
||||
p.translate(-center);
|
||||
// All the next draws are mirrored.
|
||||
p.drawEllipse(rect);
|
||||
context.st->historyFastShareIcon().paintInCenter(p, rect);
|
||||
p.setPen(pen);
|
||||
p.setBrush(Qt::NoBrush);
|
||||
p.drawArc(arcRect, arc::kQuarterLength, spanAngle);
|
||||
// p.drawArc(arcRect, arc::kQuarterLength, spanAngle);
|
||||
if (reachRatio) {
|
||||
const auto w = style::ConvertFloatScale(kWaveWidth);
|
||||
p.setOpacity(ratio - reachRatio);
|
||||
p.drawArc(
|
||||
arcRect + Margins(reachRatio * reachRatio * w),
|
||||
arc::kQuarterLength,
|
||||
spanAngle);
|
||||
}
|
||||
}
|
||||
p.restore();
|
||||
}
|
||||
}
|
||||
|
||||
void Message::paintCommentsButton(
|
||||
@@ -1933,6 +2011,7 @@ void Message::paintText(
|
||||
.pausedSpoiler = context.paused || On(PowerSaving::kChatSpoiler),
|
||||
.selection = context.selection,
|
||||
.highlight = highlightRequest ? &*highlightRequest : nullptr,
|
||||
.useFullWidth = true,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3830,10 +3909,10 @@ void Message::drawRightAction(
|
||||
} else if (_rightAction->second) {
|
||||
st->historyFastCloseIcon().paintInCenter(
|
||||
p,
|
||||
{ left, top, size->width(), size->width() });
|
||||
QRect(left, top, size->width(), size->width()));
|
||||
st->historyFastMoreIcon().paintInCenter(
|
||||
p,
|
||||
{ left, size->width() + top, size->width(), size->width() });
|
||||
QRect(left, size->width() + top, size->width(), size->width()));
|
||||
} else {
|
||||
const auto &icon = data()->isSponsored()
|
||||
? st->historyFastCloseIcon()
|
||||
@@ -3842,7 +3921,7 @@ void Message::drawRightAction(
|
||||
&& this->context() != Context::SavedSublist)
|
||||
? st->historyFastShareIcon()
|
||||
: st->historyGoToOriginalIcon();
|
||||
icon.paintInCenter(p, { left, top, size->width(), size->height() });
|
||||
icon.paintInCenter(p, Rect(left, top, *size));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ constexpr auto kPremiumToastDuration = 5 * crl::time(1000);
|
||||
result->paintRequest() | rpl::start_with_next([=] {
|
||||
auto p = QPainter(result);
|
||||
|
||||
const auto font = st::historyPremiumViewSet.font;
|
||||
const auto font = st::historyPremiumViewSet.style.font;
|
||||
const auto top = (result->height() - font->height) / 2;
|
||||
auto pen = st::historyPremiumViewSet.textFg->p;
|
||||
p.setPen(pen);
|
||||
@@ -229,7 +229,7 @@ void PaidReactionToast::showFor(
|
||||
child->show();
|
||||
|
||||
const auto leftSkip = skip + size + skip - st.padding.left();
|
||||
const auto undoFont = st::historyPremiumViewSet.font;
|
||||
const auto undoFont = st::historyPremiumViewSet.style.font;
|
||||
|
||||
const auto rightSkip = undoFont->width(undoText)
|
||||
+ st::toastUndoSpace
|
||||
|
||||
@@ -24,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "history/history_drag_area.h"
|
||||
#include "history/history_item_components.h"
|
||||
#include "history/history_item_helpers.h" // GetErrorTextForSending.
|
||||
#include "history/history_view_swipe.h"
|
||||
#include "ui/chat/pinned_bar.h"
|
||||
#include "ui/chat/chat_style.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
@@ -399,6 +400,7 @@ RepliesWidget::RepliesWidget(
|
||||
|
||||
setupTopicViewer();
|
||||
setupComposeControls();
|
||||
setupSwipeReply();
|
||||
orderWidgets();
|
||||
|
||||
if (_pinnedBar) {
|
||||
@@ -865,6 +867,66 @@ void RepliesWidget::setupComposeControls() {
|
||||
}
|
||||
}
|
||||
|
||||
void RepliesWidget::setupSwipeReply() {
|
||||
const auto can = [=](not_null<HistoryItem*> still) {
|
||||
const auto canSendReply = _topic
|
||||
? Data::CanSendAnything(_topic)
|
||||
: Data::CanSendAnything(_history->peer);
|
||||
const auto allowInAnotherChat = still && still->allowsForward();
|
||||
if (allowInAnotherChat && (_joinGroup || !canSendReply)) {
|
||||
return true;
|
||||
} else if (!_joinGroup && canSendReply) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
HistoryView::SetupSwipeHandler(_inner, _scroll.get(), [=](
|
||||
HistoryView::ChatPaintGestureHorizontalData data) {
|
||||
const auto changed = (_gestureHorizontal.msgBareId != data.msgBareId)
|
||||
|| (_gestureHorizontal.translation != data.translation)
|
||||
|| (_gestureHorizontal.reachRatio != data.reachRatio);
|
||||
_gestureHorizontal = data;
|
||||
if (changed) {
|
||||
const auto item = _history->peer->owner().message(
|
||||
_history->peer->id,
|
||||
MsgId{ data.msgBareId });
|
||||
if (item) {
|
||||
_history->owner().requestItemRepaint(item);
|
||||
}
|
||||
}
|
||||
}, [=, show = controller()->uiShow()](int cursorTop) {
|
||||
auto result = HistoryView::SwipeHandlerFinishData();
|
||||
if (_inner->elementInSelectionMode()) {
|
||||
return result;
|
||||
}
|
||||
const auto view = _inner->lookupItemByY(cursorTop);
|
||||
if (!view->data()->isRegular()
|
||||
|| view->data()->isService()) {
|
||||
return result;
|
||||
}
|
||||
if (!can(view->data())) {
|
||||
return result;
|
||||
}
|
||||
|
||||
result.msgBareId = view->data()->fullId().msg.bare;
|
||||
result.callback = [=, itemId = view->data()->fullId()] {
|
||||
const auto still = show->session().data().message(itemId);
|
||||
const auto view = _inner->viewByPosition(still->position());
|
||||
const auto selected = view->selectedQuote(
|
||||
_inner->getSelectedTextRange(still));
|
||||
const auto replyToItemId = (selected.item
|
||||
? selected.item
|
||||
: still)->fullId();
|
||||
_inner->replyToMessageRequestNotify({
|
||||
.messageId = replyToItemId,
|
||||
.quote = selected.text,
|
||||
.quoteOffset = selected.offset,
|
||||
});
|
||||
};
|
||||
return result;
|
||||
}, _inner->touchMaybeSelectingValue());
|
||||
}
|
||||
|
||||
void RepliesWidget::chooseAttach(
|
||||
std::optional<bool> overrideSendImagesAsPhotos) {
|
||||
_choosingAttach = false;
|
||||
@@ -2631,6 +2693,14 @@ void RepliesWidget::listAddTranslatedItems(
|
||||
}
|
||||
}
|
||||
|
||||
Ui::ChatPaintContext RepliesWidget::listPreparePaintContext(
|
||||
Ui::ChatPaintContextArgs &&args) {
|
||||
auto context = WindowListDelegate::listPreparePaintContext(
|
||||
std::move(args));
|
||||
context.gestureHorizontal = _gestureHorizontal;
|
||||
return context;
|
||||
}
|
||||
|
||||
void RepliesWidget::setupEmptyPainter() {
|
||||
Expects(_topic != nullptr);
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "window/section_memento.h"
|
||||
#include "history/view/history_view_corner_buttons.h"
|
||||
#include "history/view/history_view_list_widget.h"
|
||||
#include "history/history_view_swipe_data.h"
|
||||
#include "data/data_messages.h"
|
||||
#include "base/timer.h"
|
||||
|
||||
@@ -180,6 +181,8 @@ public:
|
||||
History *listTranslateHistory() override;
|
||||
void listAddTranslatedItems(
|
||||
not_null<TranslateTracker*> tracker) override;
|
||||
Ui::ChatPaintContext listPreparePaintContext(
|
||||
Ui::ChatPaintContextArgs &&args) override;
|
||||
|
||||
// CornerButtonsDelegate delegate.
|
||||
void cornerButtonsShowAtPosition(
|
||||
@@ -221,6 +224,7 @@ private:
|
||||
void finishSending();
|
||||
|
||||
void setupComposeControls();
|
||||
void setupSwipeReply();
|
||||
|
||||
void setupRoot();
|
||||
void setupRootView();
|
||||
@@ -369,6 +373,8 @@ private:
|
||||
HistoryView::CornerButtons _cornerButtons;
|
||||
rpl::lifetime _topicLifetime;
|
||||
|
||||
HistoryView::ChatPaintGestureHorizontalData _gestureHorizontal;
|
||||
|
||||
int _lastScrollTop = 0;
|
||||
int _topicReopenBarHeight = 0;
|
||||
int _scrollTopDelta = 0;
|
||||
|
||||
@@ -86,7 +86,8 @@ rpl::producer<Ui::RequestsBarContent> RequestsBarContentByPeer(
|
||||
state->someUserpicsNotLoaded = false;
|
||||
for (auto &userpic : state->userpics) {
|
||||
userpic.peer->loadUserpic();
|
||||
auto image = userpic.peer->generateUserpicImage(
|
||||
auto image = PeerData::GenerateUserpicImage(
|
||||
userpic.peer,
|
||||
userpic.view,
|
||||
userpicSize * style::DevicePixelRatio());
|
||||
userpic.uniqueKey = userpic.peer->userpicUniqueKey(userpic.view);
|
||||
|
||||
@@ -154,7 +154,7 @@ void StickerToast::showWithTitle(const QString &title) {
|
||||
? tr::lng_animated_emoji_saved_open(tr::now)
|
||||
: tr::lng_sticker_premium_view(tr::now);
|
||||
_st.padding.setLeft(skip + size + skip);
|
||||
_st.padding.setRight(st::historyPremiumViewSet.font->width(view)
|
||||
_st.padding.setRight(st::historyPremiumViewSet.style.font->width(view)
|
||||
- st::historyPremiumViewSet.width);
|
||||
|
||||
clearHiddenHiding();
|
||||
|
||||
@@ -518,7 +518,7 @@ void TranslateBar::showToast(
|
||||
const QString &buttonText,
|
||||
Fn<void()> buttonCallback) {
|
||||
const auto st = std::make_shared<style::Toast>(st::historyPremiumToast);
|
||||
st->padding.setRight(st::historyPremiumViewSet.font->width(buttonText)
|
||||
st->padding.setRight(st::historyPremiumViewSet.style.font->width(buttonText)
|
||||
- st::historyPremiumViewSet.width);
|
||||
|
||||
const auto weak = Ui::Toast::Show(_wrap.window(), Ui::Toast::Config{
|
||||
|
||||
@@ -64,9 +64,12 @@ constexpr auto kMaxGifForwardedBarLines = 4;
|
||||
constexpr auto kUseNonBlurredThreshold = 240;
|
||||
constexpr auto kMaxInlineArea = 1920 * 1080;
|
||||
|
||||
int gifMaxStatusWidth(DocumentData *document) {
|
||||
auto result = st::normalFont->width(Ui::FormatDownloadText(document->size, document->size));
|
||||
accumulate_max(result, st::normalFont->width(Ui::FormatGifAndSizeText(document->size)));
|
||||
[[nodiscard]] int GifMaxStatusWidth(not_null<DocumentData*> document) {
|
||||
auto result = st::normalFont->width(
|
||||
Ui::FormatDownloadText(document->size, document->size));
|
||||
accumulate_max(
|
||||
result,
|
||||
st::normalFont->width(Ui::FormatGifAndSizeText(document->size)));
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -264,7 +267,10 @@ QSize Gif::countOptimalSize() {
|
||||
thumbMaxWidth);
|
||||
auto minHeight = qMax(scaled.height(), st::minPhotoSize);
|
||||
if (!activeCurrentStreamed()) {
|
||||
accumulate_max(maxWidth, gifMaxStatusWidth(_data) + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x()));
|
||||
accumulate_max(
|
||||
maxWidth,
|
||||
GifMaxStatusWidth(_data)
|
||||
+ 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x()));
|
||||
}
|
||||
if (_parent->hasBubble()) {
|
||||
maxWidth = qMax(maxWidth, _parent->textualMaxWidth());
|
||||
@@ -298,7 +304,10 @@ QSize Gif::countCurrentSize(int newWidth) {
|
||||
thumbMaxWidth);
|
||||
auto newHeight = qMax(scaled.height(), st::minPhotoSize);
|
||||
if (!activeCurrentStreamed()) {
|
||||
accumulate_max(newWidth, gifMaxStatusWidth(_data) + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x()));
|
||||
accumulate_max(
|
||||
newWidth,
|
||||
GifMaxStatusWidth(_data)
|
||||
+ 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x()));
|
||||
}
|
||||
if (_parent->hasBubble()) {
|
||||
accumulate_max(newWidth, _parent->minWidthForMedia());
|
||||
|
||||
@@ -488,6 +488,10 @@ void Manager::paint(QPainter &p, const PaintContext &context) {
|
||||
paintButton(p, context, button.get());
|
||||
}
|
||||
if (const auto current = _button.get()) {
|
||||
if (context.gestureHorizontal.ratio) {
|
||||
current->applyState(ButtonState::Hidden);
|
||||
_buttonHiding.push_back(std::move(_button));
|
||||
}
|
||||
paintButton(p, context, current);
|
||||
}
|
||||
|
||||
|
||||
@@ -462,19 +462,10 @@ void CreateGiveawayBox(
|
||||
const auto &padding = st::giveawayGiftCodeSliderPadding;
|
||||
Ui::AddSkip(sliderContainer, padding.top());
|
||||
|
||||
class Slider : public Ui::MediaSlider {
|
||||
public:
|
||||
using Ui::MediaSlider::MediaSlider;
|
||||
|
||||
protected:
|
||||
void wheelEvent(QWheelEvent *e) override {
|
||||
e->ignore();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const auto slider = sliderContainer->add(
|
||||
object_ptr<Slider>(sliderContainer, st::settingsScale),
|
||||
object_ptr<Ui::MediaSliderWheelless>(
|
||||
sliderContainer,
|
||||
st::settingsScale),
|
||||
st::boxRowPadding);
|
||||
Ui::AddSkip(sliderContainer, padding.bottom());
|
||||
slider->resize(slider->width(), st::settingsScale.seekSize.height());
|
||||
|
||||
@@ -60,9 +60,7 @@ giveawayGiftCodeQuantitySubtitle: FlatLabel(defaultFlatLabel) {
|
||||
align: align(right);
|
||||
}
|
||||
giveawayGiftCodeQuantityFloat: FlatLabel(defaultFlatLabel) {
|
||||
style: TextStyle(semiboldTextStyle) {
|
||||
font: font(13px);
|
||||
}
|
||||
style: semiboldTextStyle;
|
||||
textFg: windowActiveTextFg;
|
||||
minWidth: 50px;
|
||||
align: align(center);
|
||||
@@ -164,7 +162,7 @@ giveawayGiftCodeBox: Box(defaultBox) {
|
||||
button: RoundButton(defaultActiveButton) {
|
||||
height: 42px;
|
||||
textTop: 12px;
|
||||
font: font(13px semibold);
|
||||
style: semiboldTextStyle;
|
||||
}
|
||||
shadowIgnoreTopSkip: true;
|
||||
}
|
||||
|
||||
@@ -32,8 +32,7 @@ channelEarnOverviewSubMinorLabel: FlatLabel(channelEarnOverviewMinorLabel) {
|
||||
}
|
||||
channelEarnOverviewSubMinorLabelPos: point(4px, 2px);
|
||||
channelEarnSemiboldLabel: FlatLabel(channelEarnOverviewMajorLabel) {
|
||||
style: TextStyle(semiboldTextStyle) {
|
||||
}
|
||||
style: semiboldTextStyle;
|
||||
}
|
||||
channelEarnHeaderLabel: FlatLabel(channelEarnOverviewMajorLabel) {
|
||||
style: TextStyle(statisticsHeaderTitleTextStyle) {
|
||||
@@ -91,7 +90,7 @@ channelEarnHistoryRecipientButton: RoundButton {
|
||||
|
||||
iconPosition: point(0px, 0px);
|
||||
|
||||
font: semiboldFont;
|
||||
style: semiboldTextStyle;
|
||||
|
||||
ripple: RippleAnimation(defaultRippleAnimation) {
|
||||
color: windowBgRipple;
|
||||
|
||||
@@ -368,7 +368,9 @@ infoProfileCover: InfoProfileCover {
|
||||
width: -12px;
|
||||
height: 18px;
|
||||
textTop: 0px;
|
||||
font: font(12px);
|
||||
style: TextStyle(defaultTextStyle) {
|
||||
font: font(12px);
|
||||
}
|
||||
ripple: defaultRippleAnimation;
|
||||
}
|
||||
showLastSeenPosition: point(3px, 58px);
|
||||
@@ -607,8 +609,7 @@ infoMembersCancelSearch: CrossButton {
|
||||
}
|
||||
infoMembersSearchTop: 15px;
|
||||
|
||||
infoMediaHeaderStyle: TextStyle(semiboldTextStyle) {
|
||||
}
|
||||
infoMediaHeaderStyle: semiboldTextStyle;
|
||||
infoMediaHeaderHeight: 28px;
|
||||
infoMediaHeaderPosition: point(14px, 6px);
|
||||
infoMediaSkip: 2px;
|
||||
@@ -1100,7 +1101,7 @@ similarChannelsLockFade: 58px;
|
||||
similarChannelsLock: RoundButton(defaultActiveButton) {
|
||||
height: 44px;
|
||||
textTop: 12px;
|
||||
font: font(13px semibold);
|
||||
style: semiboldTextStyle;
|
||||
}
|
||||
similarChannelsLockLabel: FlatLabel(defaultFlatLabel) {
|
||||
textFg: premiumButtonFg;
|
||||
|
||||
@@ -24,13 +24,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "info/profile/info_profile_emoji_status_panel.h"
|
||||
#include "info/info_controller.h"
|
||||
#include "boxes/peers/edit_forum_topic_box.h"
|
||||
#include "boxes/report_messages_box.h"
|
||||
#include "history/view/media/history_view_sticker_player.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "ui/boxes/show_or_premium_box.h"
|
||||
#include "ui/controls/userpic_button.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "ui/widgets/popup_menu.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "base/event_filter.h"
|
||||
#include "base/unixtime.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "main/main_session.h"
|
||||
@@ -41,6 +44,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_info.h"
|
||||
#include "styles/style_dialogs.h"
|
||||
#include "styles/style_menu_icons.h"
|
||||
|
||||
namespace Info::Profile {
|
||||
namespace {
|
||||
@@ -504,7 +508,7 @@ void Cover::refreshUploadPhotoOverlay() {
|
||||
return;
|
||||
}
|
||||
|
||||
_userpic->switchChangePhotoOverlay([&] {
|
||||
const auto canChange = [&] {
|
||||
if (const auto chat = _peer->asChat()) {
|
||||
return chat->canEditInformation();
|
||||
} else if (const auto channel = _peer->asChannel()) {
|
||||
@@ -516,7 +520,10 @@ void Cover::refreshUploadPhotoOverlay() {
|
||||
&& !user->isServiceUser());
|
||||
}
|
||||
Unexpected("Peer type in Info::Profile::Cover.");
|
||||
}(), [=](Ui::UserpicButton::ChosenImage chosen) {
|
||||
}();
|
||||
|
||||
_userpic->switchChangePhotoOverlay(canChange, [=](
|
||||
Ui::UserpicButton::ChosenImage chosen) {
|
||||
using ChosenType = Ui::UserpicButton::ChosenType;
|
||||
auto result = Api::PeerPhoto::UserPhoto{
|
||||
base::take<QImage>(chosen.image), // Strange MSVC bug with take.
|
||||
@@ -538,6 +545,53 @@ void Cover::refreshUploadPhotoOverlay() {
|
||||
}
|
||||
});
|
||||
|
||||
const auto canReport = [=, peer = _peer] {
|
||||
if (!peer->hasUserpic()) {
|
||||
return false;
|
||||
}
|
||||
const auto user = peer->asUser();
|
||||
if (!user) {
|
||||
if (canChange) {
|
||||
return false;
|
||||
}
|
||||
} else if (user->hasPersonalPhoto()
|
||||
|| user->isSelf()
|
||||
|| user->isInaccessible()
|
||||
|| user->isRepliesChat()
|
||||
|| (user->botInfo && user->botInfo->canEditInformation)
|
||||
|| user->isServiceUser()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
const auto contextMenu = _userpic->lifetime()
|
||||
.make_state<base::unique_qptr<Ui::PopupMenu>>();
|
||||
const auto showMenu = [=, peer = _peer, controller = _controller](
|
||||
not_null<Ui::RpWidget*> parent) {
|
||||
if (!canReport()) {
|
||||
return false;
|
||||
}
|
||||
*contextMenu = base::make_unique_q<Ui::PopupMenu>(
|
||||
parent,
|
||||
st::popupMenuWithIcons);
|
||||
contextMenu->get()->addAction(tr::lng_profile_report(tr::now), [=] {
|
||||
controller->show(
|
||||
ReportProfilePhotoBox(
|
||||
peer,
|
||||
peer->owner().photo(peer->userpicPhotoId())),
|
||||
Ui::LayerOption::CloseOther);
|
||||
}, &st::menuIconReport);
|
||||
contextMenu->get()->popup(QCursor::pos());
|
||||
return true;
|
||||
};
|
||||
base::install_event_filter(_userpic, [showMenu, raw = _userpic.data()](
|
||||
not_null<QEvent*> e) {
|
||||
return (e->type() == QEvent::ContextMenu && showMenu(raw))
|
||||
? base::EventFilterResult::Cancel
|
||||
: base::EventFilterResult::Continue;
|
||||
});
|
||||
|
||||
if (const auto user = _peer->asUser()) {
|
||||
_userpic->resetPersonalRequests(
|
||||
) | rpl::start_with_next([=] {
|
||||
|
||||
@@ -149,7 +149,7 @@ private:
|
||||
const std::unique_ptr<Badge> _badge;
|
||||
rpl::variable<int> _onlineCount;
|
||||
|
||||
object_ptr<Ui::UserpicButton> _userpic;
|
||||
const object_ptr<Ui::UserpicButton> _userpic;
|
||||
Ui::UserpicButton *_changePersonal = nullptr;
|
||||
std::optional<QImage> _personalChosen;
|
||||
object_ptr<TopicIconButton> _iconButton;
|
||||
|
||||
@@ -536,7 +536,7 @@ PaintRoundImageCallback BoostRow::generatePaintUserpicCallback(bool force) {
|
||||
? st::boostsListUnclaimedIcon
|
||||
: st::boostsListUnknownIcon).paintInCenter(
|
||||
p,
|
||||
{ x, y, size, size });
|
||||
Rect(x, y, Size(size)));
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -81,7 +81,9 @@ introNextButton: RoundButton(defaultActiveButton) {
|
||||
height: 42px;
|
||||
radius: 6px;
|
||||
textTop: 11px;
|
||||
font: font(boxFontSize semibold);
|
||||
style: TextStyle(semiboldTextStyle) {
|
||||
font: font(boxFontSize semibold);
|
||||
}
|
||||
}
|
||||
introFragmentIcon: icon{{ "fragment", activeButtonFg }};
|
||||
introFragmentIconOver: icon{{ "fragment", activeButtonFgOver }};
|
||||
|
||||
@@ -432,7 +432,7 @@ QByteArray Parser::block(const MTPDpageBlockFooter &data) {
|
||||
}
|
||||
|
||||
QByteArray Parser::block(const MTPDpageBlockDivider &data) {
|
||||
return tag("hr", { { "class", "divider" } });
|
||||
return tag("hr", Attributes{ { "class", "divider" } });
|
||||
}
|
||||
|
||||
QByteArray Parser::block(const MTPDpageBlockAnchor &data) {
|
||||
|
||||
@@ -670,8 +670,9 @@ void Widget::updateTimeLabel() {
|
||||
void Widget::handleSongChange() {
|
||||
const auto current = instance()->current(_type);
|
||||
const auto document = current.audio();
|
||||
_lastSongFromAnotherSession = (document->session().uniqueId()
|
||||
!= _controller->session().uniqueId());
|
||||
_lastSongFromAnotherSession = document
|
||||
&& (document->session().uniqueId()
|
||||
!= _controller->session().uniqueId());
|
||||
if (!current
|
||||
|| !document
|
||||
|| ((_lastSongId.audio() == document)
|
||||
|
||||
@@ -54,7 +54,8 @@ constexpr auto kLoadViewsPages = 2;
|
||||
static const auto size = st::storiesWhoViewed.userpics.size;
|
||||
|
||||
static const auto GenerateUserpic = [](Userpic &userpic) {
|
||||
auto result = userpic.peer->generateUserpicImage(
|
||||
auto result = PeerData::GenerateUserpicImage(
|
||||
userpic.peer,
|
||||
userpic.view,
|
||||
size * style::DevicePixelRatio());
|
||||
result.setDevicePixelRatio(style::DevicePixelRatio());
|
||||
@@ -522,7 +523,8 @@ void RecentViews::addMenuRow(Data::StoryView entry, const QDateTime &now) {
|
||||
const auto show = _controller->uiShow();
|
||||
const auto prepare = [&](Ui::PeerUserpicView &view) {
|
||||
const auto size = st::storiesWhoViewed.photoSize;
|
||||
auto userpic = peer->generateUserpicImage(
|
||||
auto userpic = PeerData::GenerateUserpicImage(
|
||||
peer,
|
||||
view,
|
||||
size * style::DevicePixelRatio());
|
||||
userpic.setDevicePixelRatio(style::DevicePixelRatio());
|
||||
@@ -636,7 +638,8 @@ void RecentViews::subscribeToMenuUserpicsLoading(
|
||||
const auto update = (entry.key != key);
|
||||
if (update) {
|
||||
const auto size = st::storiesWhoViewed.photoSize;
|
||||
auto userpic = peer->generateUserpicImage(
|
||||
auto userpic = PeerData::GenerateUserpicImage(
|
||||
peer,
|
||||
view,
|
||||
size * style::DevicePixelRatio());
|
||||
userpic.setDevicePixelRatio(style::DevicePixelRatio());
|
||||
|
||||
@@ -336,7 +336,10 @@ QImage Sibling::userpicImage(const SiblingLayout &layout) {
|
||||
const auto key = _peer->userpicUniqueKey(_userpicView);
|
||||
if (_userpicImage.width() != size || _userpicKey != key) {
|
||||
_userpicKey = key;
|
||||
_userpicImage = _peer->generateUserpicImage(_userpicView, size);
|
||||
_userpicImage = PeerData::GenerateUserpicImage(
|
||||
_peer,
|
||||
_userpicView,
|
||||
size);
|
||||
_userpicImage.setDevicePixelRatio(ratio);
|
||||
}
|
||||
return _userpicImage;
|
||||
|
||||
@@ -371,11 +371,15 @@ themePreviewLoadingFont: font(16px);
|
||||
themePreviewLoadingFg: windowSubTextFg;
|
||||
themePreviewApplyButton: RoundButton(defaultActiveButton) {
|
||||
height: 38px;
|
||||
font: font(15px semibold);
|
||||
style: TextStyle(semiboldTextStyle) {
|
||||
font: font(15px semibold);
|
||||
}
|
||||
}
|
||||
themePreviewCancelButton: RoundButton(defaultLightButton) {
|
||||
height: 38px;
|
||||
font: font(15px semibold);
|
||||
style: TextStyle(semiboldTextStyle) {
|
||||
font: font(15px semibold);
|
||||
}
|
||||
}
|
||||
themePreviewButtonsSkip: 20px;
|
||||
themePreviewDialogsWidth: 312px;
|
||||
@@ -796,7 +800,9 @@ storiesComposeControls: ComposeControls(defaultComposeControls) {
|
||||
width: -12px;
|
||||
height: 18px;
|
||||
textTop: 0px;
|
||||
font: font(12px);
|
||||
style: TextStyle(defaultTextStyle) {
|
||||
font: font(12px);
|
||||
}
|
||||
ripple: storiesComposeRipple;
|
||||
}
|
||||
buttonSkip: 6px;
|
||||
@@ -959,7 +965,7 @@ storiesStealthBox: Box(defaultBox) {
|
||||
button: RoundButton(defaultBoxButton) {
|
||||
height: 42px;
|
||||
textTop: 12px;
|
||||
font: font(13px semibold);
|
||||
style: semiboldTextStyle;
|
||||
|
||||
textFg: storiesComposeWhiteText;
|
||||
textFgOver: storiesComposeWhiteText;
|
||||
|
||||
@@ -34,6 +34,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/text/format_values.h"
|
||||
#include "ui/item_text_options.h"
|
||||
#include "ui/painter.h"
|
||||
#include "ui/rect.h"
|
||||
#include "ui/power_saving.h"
|
||||
#include "ui/cached_round_corners.h"
|
||||
#include "ui/gl/gl_window.h"
|
||||
@@ -100,7 +101,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include <QtGui/QGuiApplication>
|
||||
#include <QtGui/QWindow>
|
||||
#include <QtGui/QScreen>
|
||||
#include <QGraphicsOpacityEffect>
|
||||
|
||||
#include <kurlmimedata.h>
|
||||
|
||||
@@ -228,6 +228,73 @@ QWidget *PipDelegate::pipParentWidget() {
|
||||
|
||||
} // namespace
|
||||
|
||||
class OverlayWidget::SponsoredButton : public Ui::RippleButton {
|
||||
public:
|
||||
SponsoredButton(QWidget *parent)
|
||||
: Ui::RippleButton(parent, st::mediaviewSponsoredButton.ripple) {
|
||||
}
|
||||
|
||||
void setText(QString text) {
|
||||
_text = Ui::Text::String(
|
||||
st::mediaviewSponsoredButton.style,
|
||||
std::move(text),
|
||||
kDefaultTextOptions,
|
||||
width());
|
||||
resize(width(), _text.minHeight() * 2);
|
||||
}
|
||||
void setOpacity(float opacity) {
|
||||
_opacity = opacity;
|
||||
}
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *e) override {
|
||||
auto p = QPainter(this);
|
||||
const auto &st = st::mediaviewSponsoredButton;
|
||||
|
||||
p.setOpacity(_opacity);
|
||||
|
||||
const auto over = Ui::AbstractButton::isOver();
|
||||
const auto down = Ui::AbstractButton::isDown();
|
||||
{
|
||||
auto hq = PainterHighQualityEnabler(p);
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush((over || down) ? st.textBgOver : st.textBg);
|
||||
p.drawRoundedRect(
|
||||
rect(),
|
||||
st::mediaviewCaptionRadius,
|
||||
st::mediaviewCaptionRadius);
|
||||
}
|
||||
|
||||
Ui::RippleButton::paintRipple(p, 0, 0);
|
||||
|
||||
p.setPen(st.textFg);
|
||||
p.setBrush(Qt::NoBrush);
|
||||
_text.draw(p, {
|
||||
.position = QPoint(
|
||||
(width() - _text.maxWidth()) / 2,
|
||||
(height() - _text.minHeight()) / 2),
|
||||
.outerWidth = width(),
|
||||
.availableWidth = width(),
|
||||
});
|
||||
}
|
||||
|
||||
QImage prepareRippleMask() const override {
|
||||
return Ui::RippleAnimation::RoundRectMask(
|
||||
size(),
|
||||
st::mediaviewCaptionRadius);
|
||||
}
|
||||
QPoint prepareRippleStartPosition() const override {
|
||||
return mapFromGlobal(QCursor::pos())
|
||||
- rect::m::pos::tl(st::mediaviewSponsoredButton.padding);
|
||||
}
|
||||
|
||||
private:
|
||||
Ui::Text::String _text;
|
||||
float64 _opacity = 1.;
|
||||
|
||||
};
|
||||
|
||||
|
||||
struct OverlayWidget::SharedMedia {
|
||||
SharedMedia(SharedMediaKey key) : key(key) {
|
||||
}
|
||||
@@ -816,6 +883,7 @@ void OverlayWidget::moveToScreen(bool inMove) {
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
_window->setScreen(activeWindowScreen);
|
||||
#else // Qt >= 6.0.0
|
||||
_window->createWinId();
|
||||
window()->setScreen(activeWindowScreen);
|
||||
#endif // Qt < 6.0.0
|
||||
DEBUG_LOG(("Viewer Pos: New actual screen: %1")
|
||||
@@ -1811,9 +1879,9 @@ bool OverlayWidget::updateControlsAnimation(crl::time now) {
|
||||
} else {
|
||||
_controlsOpacity.update(dt, anim::linear);
|
||||
}
|
||||
if (_sponsoredButtonOpacity && _sponsoredButton) {
|
||||
if (_sponsoredButton) {
|
||||
const auto value = _controlsOpacity.current();
|
||||
_sponsoredButtonOpacity->setOpacity(value);
|
||||
_sponsoredButton->setOpacity(value);
|
||||
_sponsoredButton->setAttribute(
|
||||
Qt::WA_TransparentForMouseEvents,
|
||||
value < 1);
|
||||
@@ -3677,19 +3745,14 @@ void OverlayWidget::initSponsoredButton() {
|
||||
}
|
||||
const auto &component = _session->sponsoredMessages();
|
||||
const auto details = component.lookupDetails(_message->fullId());
|
||||
_sponsoredButton = base::make_unique_q<Ui::RoundButton>(
|
||||
_body,
|
||||
rpl::single(details.buttonText),
|
||||
st::mediaviewSponsoredButton);
|
||||
_sponsoredButton = base::make_unique_q<SponsoredButton>(_body);
|
||||
_sponsoredButton->setText(details.buttonText);
|
||||
_sponsoredButton->setOpacity(1.0);
|
||||
|
||||
_sponsoredButton->setClickedCallback([=, link = details.link] {
|
||||
UrlClickHandler::Open(link);
|
||||
hide();
|
||||
});
|
||||
_sponsoredButtonOpacity = base::make_unique_q<QGraphicsOpacityEffect>(
|
||||
_sponsoredButton.get());
|
||||
_sponsoredButtonOpacity->setOpacity(1.0);
|
||||
_sponsoredButton->setGraphicsEffect(_sponsoredButtonOpacity.get());
|
||||
}
|
||||
|
||||
void OverlayWidget::updateThemePreviewGeometry() {
|
||||
@@ -4917,7 +4980,7 @@ void OverlayWidget::paintThemePreviewContent(
|
||||
+ (_themeShare->y() - _themePreviewRect.y())
|
||||
+ st::themePreviewCancelButton.padding.top()
|
||||
+ st::themePreviewCancelButton.textTop
|
||||
+ st::themePreviewCancelButton.font->ascent;
|
||||
+ st::themePreviewCancelButton.style.font->ascent;
|
||||
p.drawText(
|
||||
left,
|
||||
baseline,
|
||||
@@ -6298,7 +6361,6 @@ void OverlayWidget::clearBeforeHide() {
|
||||
_helper->setControlsOpacity(1.);
|
||||
_groupThumbs = nullptr;
|
||||
_groupThumbsRect = QRect();
|
||||
_sponsoredButtonOpacity = nullptr;
|
||||
_sponsoredButton = nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,8 +20,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "media/view/media_view_open_common.h"
|
||||
#include "media/stories/media_stories_delegate.h"
|
||||
|
||||
class QGraphicsOpacityEffect;
|
||||
|
||||
class History;
|
||||
|
||||
namespace anim {
|
||||
@@ -143,6 +141,7 @@ private:
|
||||
class Renderer;
|
||||
class RendererSW;
|
||||
class RendererGL;
|
||||
class SponsoredButton;
|
||||
|
||||
// If changing, see paintControls()!
|
||||
enum class Over {
|
||||
@@ -700,8 +699,7 @@ private:
|
||||
object_ptr<Ui::DropdownMenu> _dropdown;
|
||||
base::Timer _dropdownShowTimer;
|
||||
|
||||
base::unique_qptr<Ui::RoundButton> _sponsoredButton;
|
||||
base::unique_qptr<QGraphicsOpacityEffect> _sponsoredButtonOpacity;
|
||||
base::unique_qptr<SponsoredButton> _sponsoredButton;
|
||||
|
||||
bool _receiveMouse = true;
|
||||
bool _processingKeyPress = false;
|
||||
|
||||
@@ -49,7 +49,9 @@ passportPasswordSubmit: RoundButton(defaultActiveButton) {
|
||||
width: 200px;
|
||||
height: 44px;
|
||||
textTop: 12px;
|
||||
font: font(semibold 15px);
|
||||
style: TextStyle(semiboldTextStyle) {
|
||||
font: font(semibold 15px);
|
||||
}
|
||||
}
|
||||
passportPasswordSubmitBottom: 72px;
|
||||
passportPasswordForgotBottom: 36px;
|
||||
|
||||
@@ -14,12 +14,16 @@ paymentsPanelSize: size(392px, 600px);
|
||||
paymentsPanelButton: RoundButton(defaultBoxButton) {
|
||||
width: -36px;
|
||||
height: 36px;
|
||||
font: boxButtonFont;
|
||||
style: TextStyle(defaultTextStyle) {
|
||||
font: boxButtonFont;
|
||||
}
|
||||
}
|
||||
paymentsPanelSubmit: RoundButton(defaultActiveButton) {
|
||||
width: -36px;
|
||||
height: 36px;
|
||||
font: boxButtonFont;
|
||||
style: TextStyle(defaultTextStyle) {
|
||||
font: boxButtonFont;
|
||||
}
|
||||
}
|
||||
paymentsPanelPadding: margins(8px, 12px, 15px, 12px);
|
||||
|
||||
@@ -139,6 +143,8 @@ paymentsLoading: InfiniteRadialAnimation(defaultInfiniteRadialAnimation) {
|
||||
botWebViewPanelSize: size(384px, 694px);
|
||||
botWebViewBottomButton: RoundButton(paymentsPanelSubmit) {
|
||||
height: 56px;
|
||||
font: boxButtonFont;
|
||||
style: TextStyle(defaultTextStyle) {
|
||||
font: boxButtonFont;
|
||||
}
|
||||
textTop: 19px;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "platform/mac/main_window_mac.h"
|
||||
|
||||
#include "data/data_session.h"
|
||||
#include "mainwidget.h"
|
||||
#include "core/application.h"
|
||||
#include "core/sandbox.h"
|
||||
#include "main/main_session.h"
|
||||
@@ -25,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "platform/platform_specific.h"
|
||||
#include "platform/platform_notifications_manager.h"
|
||||
#include "base/platform/base_platform_info.h"
|
||||
#include "base/options.h"
|
||||
#include "boxes/peer_list_controllers.h"
|
||||
#include "boxes/about_box.h"
|
||||
#include "lang/lang_keys.h"
|
||||
@@ -300,7 +300,12 @@ void MainWindow::initHook() {
|
||||
if (auto view = reinterpret_cast<NSView*>(winId())) {
|
||||
if (auto window = [view window]) {
|
||||
_private->setNativeWindow(window, view);
|
||||
_private->initTouchBar(window, &controller());
|
||||
if (!base::options::lookup<bool>(
|
||||
Window::kOptionDisableTouchbar).value()) {
|
||||
_private->initTouchBar(window, &controller());
|
||||
} else {
|
||||
LOG(("Touch Bar was disabled from Experimental Settings."));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ void BackButton::paintEvent(QPaintEvent *e) {
|
||||
p.fillRect(e->rect(), st::profileBg);
|
||||
st::topBarBack.paint(p, (st::topBarArrowPadding.left() - st::topBarBack.width()) / 2, (st::topBarHeight - st::topBarBack.height()) / 2, width());
|
||||
|
||||
p.setFont(st::topBarButton.font);
|
||||
p.setFont(st::topBarButton.style.font);
|
||||
p.setPen(st::topBarButton.textFg);
|
||||
p.drawTextLeft(st::topBarArrowPadding.left(), st::topBarButton.padding.top() + st::topBarButton.textTop, width(), _text);
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/wrap/slide_wrap.h"
|
||||
#include "ui/wrap/vertical_layout.h"
|
||||
#include "ui/painter.h"
|
||||
#include "ui/rect.h"
|
||||
#include "ui/vertical_list.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "styles/style_chat.h"
|
||||
@@ -688,7 +689,7 @@ void LinksController::rowPaintIcon(
|
||||
auto rect = QRect(0, 0, inner, inner);
|
||||
p.drawEllipse(rect);
|
||||
}
|
||||
st::inviteLinkIcon.paintInCenter(p, { 0, 0, inner, inner });
|
||||
st::inviteLinkIcon.paintInCenter(p, Rect(Size(inner)));
|
||||
}
|
||||
p.drawImage(x + skip, y + skip, _icon);
|
||||
}
|
||||
|
||||
@@ -263,7 +263,18 @@ void Input::setupContent() {
|
||||
}
|
||||
close();
|
||||
_requestLifetime = cloudPassword().resetPassword(
|
||||
) | rpl::start_with_error_done([=](const QString &type) {
|
||||
) | rpl::start_with_next_error_done([=](
|
||||
Api::CloudPassword::ResetRetryDate retryDate) {
|
||||
_requestLifetime.destroy();
|
||||
const auto left = std::max(
|
||||
retryDate - base::unixtime::now(),
|
||||
60);
|
||||
controller()->show(Ui::MakeInformBox(
|
||||
tr::lng_cloud_password_reset_later(
|
||||
tr::now,
|
||||
lt_duration,
|
||||
Ui::FormatResetCloudPasswordIn(left))));
|
||||
}, [=](const QString &type) {
|
||||
_requestLifetime.destroy();
|
||||
}, [=] {
|
||||
_requestLifetime.destroy();
|
||||
|
||||
@@ -176,7 +176,7 @@ settingLocalPasscodeInputField: InputField(defaultInputField) {
|
||||
settingLocalPasscodeDescription: FlatLabel(changePhoneDescription) {
|
||||
minWidth: 256px;
|
||||
}
|
||||
settingLocalPasscodeDescriptionHeight: 52px;
|
||||
settingLocalPasscodeDescriptionHeight: 53px;
|
||||
settingLocalPasscodeError: FlatLabel(changePhoneError) {
|
||||
minWidth: 256px;
|
||||
}
|
||||
@@ -531,7 +531,7 @@ filterInviteBox: Box(defaultBox) {
|
||||
button: RoundButton(defaultActiveButton) {
|
||||
height: 42px;
|
||||
textTop: 12px;
|
||||
font: font(13px semibold);
|
||||
style: semiboldTextStyle;
|
||||
}
|
||||
}
|
||||
filterInviteButtonStyle: TextStyle(defaultTextStyle) {
|
||||
|
||||
@@ -265,14 +265,17 @@ SliderWithLabel MakeSliderWithLabel(
|
||||
const style::MediaSlider &sliderSt,
|
||||
const style::FlatLabel &labelSt,
|
||||
int skip,
|
||||
int minLabelWidth) {
|
||||
int minLabelWidth,
|
||||
bool ignoreWheel) {
|
||||
auto result = object_ptr<Ui::RpWidget>(parent);
|
||||
const auto raw = result.data();
|
||||
const auto height = std::max(
|
||||
sliderSt.seekSize.height(),
|
||||
labelSt.style.font->height);
|
||||
raw->resize(sliderSt.seekSize.width(), height);
|
||||
const auto slider = Ui::CreateChild<Ui::MediaSlider>(raw, sliderSt);
|
||||
const auto slider = ignoreWheel
|
||||
? Ui::CreateChild<Ui::MediaSliderWheelless>(raw, sliderSt)
|
||||
: Ui::CreateChild<Ui::MediaSlider>(raw, sliderSt);
|
||||
const auto label = Ui::CreateChild<Ui::FlatLabel>(raw, labelSt);
|
||||
slider->resize(slider->width(), sliderSt.seekSize.height());
|
||||
rpl::combine(
|
||||
|
||||
@@ -214,6 +214,7 @@ struct SliderWithLabel {
|
||||
const style::MediaSlider &sliderSt,
|
||||
const style::FlatLabel &labelSt,
|
||||
int skip,
|
||||
int minLabelWidth = 0);
|
||||
int minLabelWidth = 0,
|
||||
bool ignoreWheel = false);
|
||||
|
||||
} // namespace Settings
|
||||
|
||||
@@ -156,6 +156,7 @@ void SetupExperimental(
|
||||
addToggle(Core::kOptionSkipUrlSchemeRegister);
|
||||
addToggle(Data::kOptionExternalVideoPlayer);
|
||||
addToggle(Window::kOptionNewWindowsSizeAsFirst);
|
||||
addToggle(Window::kOptionDisableTouchbar);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -598,7 +598,8 @@ void SetupInterfaceScale(
|
||||
st::settingsScale,
|
||||
st::settingsScaleLabel,
|
||||
st::normalFont->spacew * 2,
|
||||
st::settingsScaleLabel.style.font->width("300%"));
|
||||
st::settingsScaleLabel.style.font->width("300%"),
|
||||
true);
|
||||
container->add(
|
||||
std::move(sliderWithLabel.widget),
|
||||
icon ? st::settingsScalePadding : st::settingsBigScalePadding);
|
||||
|
||||
@@ -1429,7 +1429,7 @@ not_null<Ui::RoundButton*> CreateLockedButton(
|
||||
|
||||
const auto labelSt = result->lifetime().make_state<style::FlatLabel>(
|
||||
st::defaultFlatLabel);
|
||||
labelSt->style.font = st.font;
|
||||
labelSt->style.font = st.style.font;
|
||||
labelSt->textFg = st.textFg;
|
||||
|
||||
const auto label = Ui::CreateChild<Ui::FlatLabel>(
|
||||
|
||||
@@ -235,7 +235,8 @@ PaintRoundImageCallback Row::generatePaintUserpicCallback(bool forceRound) {
|
||||
p.drawImage(QRect(x, y, size, size), userpic.cached);
|
||||
} else {
|
||||
if (_emptyUserpic.isNull()) {
|
||||
_emptyUserpic = peer->generateUserpicImage(
|
||||
_emptyUserpic = PeerData::GenerateUserpicImage(
|
||||
peer,
|
||||
_userpic,
|
||||
size * ratio,
|
||||
size * ratio * Ui::ForumUserpicRadiusMultiplier());
|
||||
|
||||
@@ -1156,20 +1156,12 @@ void ChartWidget::setupDetails() {
|
||||
_chartArea->update();
|
||||
return;
|
||||
}
|
||||
const auto maxAbsoluteValue = [&] {
|
||||
auto maxValue = ChartValue(0);
|
||||
for (const auto &l : _chartData.lines) {
|
||||
maxValue = std::max(l.maxValue, maxValue);
|
||||
}
|
||||
return maxValue;
|
||||
}();
|
||||
if (hasLocalZoom()) {
|
||||
_zoomEnabled = true;
|
||||
}
|
||||
_details.widget = base::make_unique_q<PointDetailsWidget>(
|
||||
this,
|
||||
_chartData,
|
||||
maxAbsoluteValue,
|
||||
_zoomEnabled);
|
||||
_details.widget->setClickedCallback([=] {
|
||||
const auto index = _details.widget->xIndex();
|
||||
|
||||
@@ -68,7 +68,9 @@ statisticsHeaderButton: RoundButton(defaultLightButton) {
|
||||
width: -14px;
|
||||
height: 20px;
|
||||
textTop: 2px;
|
||||
font: font(11px semibold);
|
||||
style: TextStyle(semiboldTextStyle) {
|
||||
font: font(11px semibold);
|
||||
}
|
||||
}
|
||||
|
||||
statisticsLoadingSubtext: FlatLabel(changePhoneDescription) {
|
||||
|
||||
@@ -130,7 +130,6 @@ void PaintDetails(
|
||||
PointDetailsWidget::PointDetailsWidget(
|
||||
not_null<Ui::RpWidget*> parent,
|
||||
const Data::StatisticalChart &chartData,
|
||||
float64 maxAbsoluteValue,
|
||||
bool zoomEnabled)
|
||||
: Ui::AbstractButton(parent)
|
||||
, _zoomEnabled(zoomEnabled)
|
||||
@@ -173,12 +172,44 @@ PointDetailsWidget::PointDetailsWidget(
|
||||
return 0;
|
||||
}();
|
||||
|
||||
const auto calculatedWidth = [&]{
|
||||
const auto hasUsdLine = (_chartData.currencyRate != 0)
|
||||
&& (_chartData.currency != Data::StatisticalCurrency::None)
|
||||
&& (_chartData.lines.size() == 1);
|
||||
|
||||
const auto maxValueTextWidth = [&] {
|
||||
if (hasUsdLine) {
|
||||
auto maxValueWidth = 0;
|
||||
const auto multiplier = float64(Data::kEarnMultiplier);
|
||||
for (const auto &value : _chartData.lines.front().y) {
|
||||
const auto valueText = Ui::Text::String(
|
||||
_textStyle,
|
||||
QString::number(value / multiplier));
|
||||
const auto usdText = Ui::Text::String(
|
||||
_textStyle,
|
||||
Info::ChannelEarn::ToUsd(value, _chartData.currencyRate));
|
||||
const auto width = std::max(
|
||||
usdText.maxWidth(),
|
||||
valueText.maxWidth());
|
||||
if (width > maxValueWidth) {
|
||||
maxValueWidth = width;
|
||||
}
|
||||
}
|
||||
return maxValueWidth;
|
||||
}
|
||||
const auto maxAbsoluteValue = [&] {
|
||||
auto maxValue = ChartValue(0);
|
||||
for (const auto &l : _chartData.lines) {
|
||||
maxValue = std::max(l.maxValue, maxValue);
|
||||
}
|
||||
return maxValue;
|
||||
}();
|
||||
const auto maxValueText = Ui::Text::String(
|
||||
_textStyle,
|
||||
Lang::FormatCountDecimal(maxAbsoluteValue));
|
||||
const auto maxValueTextWidth = maxValueText.maxWidth();
|
||||
return maxValueText.maxWidth();
|
||||
}();
|
||||
|
||||
const auto calculatedWidth = [&]{
|
||||
auto maxNameTextWidth = 0;
|
||||
for (const auto &dataLine : _chartData.lines) {
|
||||
const auto maxNameText = Ui::Text::String(
|
||||
@@ -187,6 +218,19 @@ PointDetailsWidget::PointDetailsWidget(
|
||||
maxNameTextWidth = std::max(
|
||||
maxNameText.maxWidth(),
|
||||
maxNameTextWidth);
|
||||
if (hasUsdLine) {
|
||||
const auto currency = Ui::Text::String(
|
||||
_textStyle,
|
||||
tr::lng_channel_earn_chart_overriden_detail_currency(
|
||||
tr::now));
|
||||
const auto usd = Ui::Text::String(
|
||||
_textStyle,
|
||||
tr::lng_channel_earn_chart_overriden_detail_usd(
|
||||
tr::now));
|
||||
maxNameTextWidth = std::max(
|
||||
std::max(currency.maxWidth(), usd.maxWidth()),
|
||||
maxNameTextWidth);
|
||||
}
|
||||
}
|
||||
{
|
||||
const auto maxHeaderText = Ui::Text::String(
|
||||
|
||||
@@ -27,7 +27,6 @@ public:
|
||||
PointDetailsWidget(
|
||||
not_null<Ui::RpWidget*> parent,
|
||||
const Data::StatisticalChart &chartData,
|
||||
float64 maxAbsoluteValue,
|
||||
bool zoomEnabled);
|
||||
|
||||
[[nodiscard]] int xIndex() const;
|
||||
|
||||
@@ -111,6 +111,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
#include "base/algorithm.h"
|
||||
#include "base/basic_types.h"
|
||||
#include "base/debug_destroy_informer.h" // _DEBUG only.
|
||||
#include "base/flat_set.h"
|
||||
#include "base/flat_map.h"
|
||||
#include "base/invoke_queued.h"
|
||||
|
||||
@@ -276,7 +276,7 @@ void Panel::Button::paintEvent(QPaintEvent *e) {
|
||||
paintRipple(p, rect().topLeft(), &ripple);
|
||||
}
|
||||
|
||||
p.setFont(_st.font);
|
||||
p.setFont(_st.style.font);
|
||||
|
||||
const auto height = rect().height();
|
||||
const auto progress = st::paymentsLoading.size;
|
||||
|
||||
@@ -570,6 +570,8 @@ historyFastTranscribeLockPos: point(18px, 13px);
|
||||
historyFastTranscribeLockOverlayPos: point(21px, 13px);
|
||||
historyFastTranscribeLockOverlaySize: size(6px, 10px);
|
||||
|
||||
historySwipeIconSkip: 80px;
|
||||
|
||||
historySavedFont: font(semibold 14px);
|
||||
|
||||
historyGroupWidthMax: maxMediaSize;
|
||||
|
||||
@@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/chat/message_bubble.h"
|
||||
#include "ui/chat/chat_style_radius.h"
|
||||
#include "ui/style/style_core_palette.h"
|
||||
#include "history/history_view_swipe_data.h"
|
||||
#include "layout/layout_selection.h"
|
||||
#include "styles/style_basic.h"
|
||||
|
||||
@@ -164,6 +165,7 @@ struct ChatPaintContext {
|
||||
QPainterPath *highlightPathCache = nullptr;
|
||||
mutable QRect highlightInterpolateTo;
|
||||
crl::time now = 0;
|
||||
HistoryView::ChatPaintGestureHorizontalData gestureHorizontal;
|
||||
|
||||
void translate(int x, int y) {
|
||||
viewport.translate(x, y);
|
||||
|
||||
@@ -82,6 +82,28 @@ constexpr auto kRadialFinishArcShift = 1200;
|
||||
: type;
|
||||
};
|
||||
|
||||
[[nodiscard]] QSize AdjustedLottieSize(
|
||||
not_null<const style::CallMuteButton*> st) {
|
||||
const auto &button = st->active.button;
|
||||
const auto left = (button.width - st->lottieSize.width()) / 2;
|
||||
const auto size = button.width - 2 * left;
|
||||
return QSize(size, size);
|
||||
}
|
||||
|
||||
[[nodiscard]] int AdjustedBgSize(
|
||||
not_null<const style::CallMuteButton*> st) {
|
||||
const auto &button = st->active.button;
|
||||
const auto left = (button.width - st->active.bgSize) / 2;
|
||||
return button.width - 2 * left;
|
||||
}
|
||||
|
||||
[[nodiscard]] int AdjustedBgSkip(
|
||||
not_null<const style::CallMuteButton*> st) {
|
||||
const auto &button = st->active.button;
|
||||
const auto bgSize = AdjustedBgSize(st);
|
||||
return (button.width - bgSize) / 2;
|
||||
}
|
||||
|
||||
auto MuteBlobs() {
|
||||
return std::vector<Paint::Blobs::BlobData>{
|
||||
{
|
||||
@@ -515,9 +537,12 @@ CallMuteButton::CallMuteButton(
|
||||
CallMuteButtonState initial)
|
||||
: _state(initial)
|
||||
, _st(&st)
|
||||
, _lottieSize(AdjustedLottieSize(_st))
|
||||
, _bgSize(AdjustedBgSize(_st))
|
||||
, _bgSkip(AdjustedBgSkip(_st))
|
||||
, _blobs(base::make_unique_q<BlobsWidget>(
|
||||
parent,
|
||||
_st->active.bgSize,
|
||||
_bgSize,
|
||||
rpl::combine(
|
||||
PowerSaving::OnValue(PowerSaving::kCalls),
|
||||
std::move(hideBlobs),
|
||||
@@ -593,13 +618,13 @@ void CallMuteButton::refreshIcons() {
|
||||
_icons[0].emplace(Lottie::IconDescriptor{
|
||||
.path = u":/icons/calls/voice.lottie"_q,
|
||||
.color = &st::groupCallIconFg,
|
||||
.sizeOverride = _st->lottieSize,
|
||||
.sizeOverride = _lottieSize,
|
||||
.frame = (_iconState.index ? 0 : _iconState.frameTo),
|
||||
});
|
||||
_icons[1].emplace(Lottie::IconDescriptor{
|
||||
.path = u":/icons/calls/hands.lottie"_q,
|
||||
.color = &st::groupCallIconFg,
|
||||
.sizeOverride = _st->lottieSize,
|
||||
.sizeOverride = _lottieSize,
|
||||
.frame = (_iconState.index ? _iconState.frameTo : 0),
|
||||
});
|
||||
|
||||
@@ -813,7 +838,7 @@ void CallMuteButton::init() {
|
||||
// Icon rect.
|
||||
_content->sizeValue(
|
||||
) | rpl::start_with_next([=](QSize size) {
|
||||
const auto icon = _st->lottieSize;
|
||||
const auto icon = _lottieSize;
|
||||
_muteIconRect = QRect(
|
||||
(size.width() - icon.width()) / 2,
|
||||
_st->lottieTop,
|
||||
@@ -858,8 +883,8 @@ void CallMuteButton::init() {
|
||||
InfiniteRadialAnimation::Draw(
|
||||
p,
|
||||
r,
|
||||
_st->active.bgPosition,
|
||||
QSize(_st->active.bgSize, _st->active.bgSize),
|
||||
QPoint(_bgSkip, _bgSkip),
|
||||
QSize(_bgSize, _bgSize),
|
||||
_content->width(),
|
||||
QPen(_radialInfo.st.color),
|
||||
_radialInfo.st.thickness);
|
||||
@@ -870,8 +895,8 @@ void CallMuteButton::init() {
|
||||
InfiniteRadialAnimation::Draw(
|
||||
p,
|
||||
std::move(state),
|
||||
_st->active.bgPosition,
|
||||
QSize(_st->active.bgSize, _st->active.bgSize),
|
||||
QPoint(_bgSkip, _bgSkip),
|
||||
QSize(_bgSize, _bgSize),
|
||||
_content->width(),
|
||||
QPen(_radialInfo.st.color),
|
||||
_radialInfo.st.thickness);
|
||||
@@ -1027,6 +1052,9 @@ void CallMuteButton::setStyle(const style::CallMuteButton &st) {
|
||||
return;
|
||||
}
|
||||
_st = &st;
|
||||
_lottieSize = AdjustedLottieSize(_st);
|
||||
_bgSize = AdjustedBgSize(_st);
|
||||
_bgSkip = AdjustedBgSkip(_st);
|
||||
const auto &button = _st->active.button;
|
||||
_content->resize(button.width, button.height);
|
||||
_blobs->setDiameter(_st->active.bgSize);
|
||||
@@ -1057,21 +1085,13 @@ rpl::producer<Qt::MouseButton> CallMuteButton::clicks() {
|
||||
}
|
||||
|
||||
QSize CallMuteButton::innerSize() const {
|
||||
return innerGeometry().size();
|
||||
}
|
||||
|
||||
QRect CallMuteButton::innerGeometry() const {
|
||||
const auto &skip = _st->active.outerRadius;
|
||||
return QRect(
|
||||
_content->x(),
|
||||
_content->y(),
|
||||
_content->width() - 2 * skip,
|
||||
_content->width() - 2 * skip);
|
||||
return QSize(
|
||||
_content->width() - 2 * _bgSkip,
|
||||
_content->width() - 2 * _bgSkip);
|
||||
}
|
||||
|
||||
void CallMuteButton::moveInner(QPoint position) {
|
||||
const auto &skip = _st->active.outerRadius;
|
||||
_content->move(position - QPoint(skip, skip));
|
||||
_content->move(position - QPoint(_bgSkip, _bgSkip));
|
||||
|
||||
{
|
||||
const auto offset = QPoint(
|
||||
|
||||
@@ -73,7 +73,6 @@ public:
|
||||
[[nodiscard]] rpl::producer<Qt::MouseButton> clicks();
|
||||
|
||||
[[nodiscard]] QSize innerSize() const;
|
||||
[[nodiscard]] QRect innerGeometry() const;
|
||||
void moveInner(QPoint position);
|
||||
|
||||
void shake();
|
||||
@@ -165,6 +164,9 @@ private:
|
||||
HandleMouseState _handleMouseState = HandleMouseState::Enabled;
|
||||
|
||||
not_null<const style::CallMuteButton*> _st;
|
||||
QSize _lottieSize;
|
||||
int _bgSize = 0;
|
||||
int _bgSkip = 0;
|
||||
|
||||
const base::unique_qptr<BlobsWidget> _blobs;
|
||||
const base::unique_qptr<AbstractButton> _content;
|
||||
|
||||
@@ -396,7 +396,7 @@ object_ptr<RoundButton> FilterLinkProcessButton(
|
||||
const auto label = result->lifetime().make_state<Label>(result.data());
|
||||
label->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
result->sizeValue() | rpl::start_with_next([=](QSize size) {
|
||||
const auto xskip = st->font->spacew;
|
||||
const auto xskip = st->style.font->spacew;
|
||||
const auto yskip = xskip / 2;
|
||||
label->setGeometry(QRect(QPoint(), size).marginsRemoved(
|
||||
{ xskip, yskip, xskip, yskip }));
|
||||
|
||||
@@ -1026,10 +1026,12 @@ void UserpicButton::prepareUserpicPixmap() {
|
||||
true);
|
||||
p.drawImage(QRect(0, 0, size, size), _userpicView.cached);
|
||||
} else {
|
||||
const auto empty = _peer->generateUserpicImage(
|
||||
const auto empty = PeerData::GenerateUserpicImage(
|
||||
_peer,
|
||||
_userpicView,
|
||||
size * ratio,
|
||||
size * ratio * Ui::ForumUserpicRadiusMultiplier());
|
||||
(size * ratio)
|
||||
* Ui::ForumUserpicRadiusMultiplier());
|
||||
p.drawImage(QRect(0, 0, size, size), empty);
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -245,7 +245,11 @@ QImage PeerUserpic::image(int size) {
|
||||
} else {
|
||||
const auto full = size * style::DevicePixelRatio();
|
||||
const auto r = full / 2.;
|
||||
const auto empty = _peer->generateUserpicImage(view, full, r);
|
||||
const auto empty = PeerData::GenerateUserpicImage(
|
||||
_peer,
|
||||
view,
|
||||
full,
|
||||
r);
|
||||
p.drawImage(QRect(0, 0, size, size), empty);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ premiumPreviewBox: Box(defaultBox) {
|
||||
button: RoundButton(defaultActiveButton) {
|
||||
height: 44px;
|
||||
textTop: 12px;
|
||||
font: font(13px semibold);
|
||||
style: semiboldTextStyle;
|
||||
}
|
||||
}
|
||||
premiumPreviewDoubledLimitsBox: Box(premiumPreviewBox) {
|
||||
@@ -295,7 +295,7 @@ boostBox: Box(premiumPreviewDoubledLimitsBox) {
|
||||
button: RoundButton(defaultActiveButton) {
|
||||
height: 42px;
|
||||
textTop: 12px;
|
||||
font: font(13px semibold);
|
||||
style: semiboldTextStyle;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -323,7 +323,7 @@ showOrShowButton: RoundButton(defaultActiveButton) {
|
||||
width: 308px;
|
||||
height: 42px;
|
||||
textTop: 12px;
|
||||
font: font(13px semibold);
|
||||
style: semiboldTextStyle;
|
||||
}
|
||||
showOrLabel: FlatLabel(boostText) {
|
||||
textFg: windowSubTextFg;
|
||||
@@ -372,7 +372,7 @@ paidReactBox: Box(boostBox) {
|
||||
button: RoundButton(defaultActiveButton) {
|
||||
height: 42px;
|
||||
textTop: 12px;
|
||||
font: font(13px semibold);
|
||||
style: semiboldTextStyle;
|
||||
}
|
||||
}
|
||||
paidReactBubbleIcon: icon{{ "settings/premium/star", premiumButtonFg }};
|
||||
|
||||
@@ -203,6 +203,7 @@ menuIconReportAttention: icon {{ "menu/report", menuIconAttentionColor }};
|
||||
menuIconRestoreAttention: icon {{ "menu/restore", menuIconAttentionColor }};
|
||||
menuIconTagRemoveAttention: icon {{ "menu/tag_remove", menuIconAttentionColor }};
|
||||
menuIconCancelAttention: icon {{ "menu/cancel", menuIconAttentionColor }};
|
||||
menuIconBlockAttention: icon {{ "menu/block", menuIconAttentionColor }};
|
||||
|
||||
menuIconBlockSettings: icon {{ "menu/block", windowBgActive }};
|
||||
menuIconInviteSettings: icon {{ "menu/invite", windowBgActive }};
|
||||
|
||||
@@ -230,4 +230,15 @@ private:
|
||||
|
||||
};
|
||||
|
||||
class MediaSliderWheelless : public MediaSlider {
|
||||
public:
|
||||
using Ui::MediaSlider::MediaSlider;
|
||||
|
||||
protected:
|
||||
void wheelEvent(QWheelEvent *e) override {
|
||||
e->ignore();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // namespace Ui
|
||||
|
||||
@@ -74,9 +74,23 @@ base::options::toggle OptionNewWindowsSizeAsFirst({
|
||||
.description = "Open new windows with a size of the main window.",
|
||||
});
|
||||
|
||||
base::options::toggle OptionDisableTouchbar({
|
||||
.id = kOptionDisableTouchbar,
|
||||
.name = "Disable Touch Bar (macOS only).",
|
||||
.scope = [] {
|
||||
#ifdef Q_OS_MAC
|
||||
return true;
|
||||
#else // !Q_OS_MAC
|
||||
return false;
|
||||
#endif // !Q_OS_MAC
|
||||
},
|
||||
.restartRequired = true,
|
||||
});
|
||||
|
||||
} // namespace.
|
||||
|
||||
const char kOptionNewWindowsSizeAsFirst[] = "new-windows-size-as-first";
|
||||
const char kOptionDisableTouchbar[] = "touchbar-disabled";
|
||||
|
||||
const QImage &Logo() {
|
||||
static const auto result = QImage(u":/gui/art/logo_256.png"_q);
|
||||
|
||||
@@ -55,6 +55,7 @@ struct CounterLayerArgs {
|
||||
};
|
||||
|
||||
extern const char kOptionNewWindowsSizeAsFirst[];
|
||||
extern const char kOptionDisableTouchbar[];
|
||||
|
||||
[[nodiscard]] QImage GenerateCounterLayer(CounterLayerArgs &&args);
|
||||
[[nodiscard]] QImage WithSmallCounter(QImage image, CounterLayerArgs &&args);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user