mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-07 15:43:55 +02:00
Improve emoji animation.
This commit is contained in:
parent
29d87f692a
commit
70378bfac8
6 changed files with 158 additions and 32 deletions
|
@ -4969,6 +4969,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_confcall_sure_remove" = "Remove {user} from the call?";
|
"lng_confcall_sure_remove" = "Remove {user} from the call?";
|
||||||
"lng_confcall_e2e_badge" = "End-to-End Encrypted";
|
"lng_confcall_e2e_badge" = "End-to-End Encrypted";
|
||||||
"lng_confcall_e2e_badge_small" = "E2E Encrypted";
|
"lng_confcall_e2e_badge_small" = "E2E Encrypted";
|
||||||
|
"lng_confcall_e2e_about" = "These four emoji represent the call's encryption key. They must match for all participants and change when someone joins or leaves.";
|
||||||
|
|
||||||
"lng_no_mic_permission" = "Telegram needs microphone access so that you can make calls and record voice messages.";
|
"lng_no_mic_permission" = "Telegram needs microphone access so that you can make calls and record voice messages.";
|
||||||
|
|
||||||
|
|
|
@ -1680,3 +1680,7 @@ confcallFingerprintText: FlatLabel(defaultFlatLabel) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
confcallFingerprintSkip: 2px;
|
confcallFingerprintSkip: 2px;
|
||||||
|
confcallFingerprintTooltipLabel: defaultImportantTooltipLabel;
|
||||||
|
confcallFingerprintTooltip: defaultImportantTooltip;
|
||||||
|
confcallFingerprintTooltipSkip: 12px;
|
||||||
|
confcallFingerprintTooltipMaxWidth: 220px;
|
||||||
|
|
|
@ -12,20 +12,22 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "calls/calls_signal_bars.h"
|
#include "calls/calls_signal_bars.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "data/data_user.h"
|
#include "data/data_user.h"
|
||||||
|
#include "ui/text/text_utilities.h"
|
||||||
#include "ui/widgets/labels.h"
|
#include "ui/widgets/labels.h"
|
||||||
#include "ui/widgets/tooltip.h"
|
#include "ui/widgets/tooltip.h"
|
||||||
#include "ui/abstract_button.h"
|
#include "ui/abstract_button.h"
|
||||||
#include "ui/emoji_config.h"
|
#include "ui/emoji_config.h"
|
||||||
#include "ui/painter.h"
|
#include "ui/painter.h"
|
||||||
#include "ui/rp_widget.h"
|
#include "ui/rp_widget.h"
|
||||||
|
#include "ui/ui_utility.h"
|
||||||
#include "styles/style_calls.h"
|
#include "styles/style_calls.h"
|
||||||
|
|
||||||
namespace Calls {
|
namespace Calls {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr auto kTooltipShowTimeoutMs = crl::time(1000);
|
constexpr auto kTooltipShowTimeoutMs = crl::time(1000);
|
||||||
constexpr auto kCarouselOneDuration = crl::time(1000);// crl::time(100);
|
constexpr auto kCarouselOneDuration = crl::time(100);
|
||||||
constexpr auto kStartTimeShift = crl::time(500);// crl::time(50);
|
constexpr auto kStartTimeShift = crl::time(50);
|
||||||
constexpr auto kEmojiInFingerprint = 4;
|
constexpr auto kEmojiInFingerprint = 4;
|
||||||
constexpr auto kEmojiInCarousel = 10;
|
constexpr auto kEmojiInCarousel = 10;
|
||||||
|
|
||||||
|
@ -317,6 +319,7 @@ FingerprintBadge SetupFingerprintBadge(
|
||||||
};
|
};
|
||||||
const auto state = on.make_state<State>();
|
const auto state = on.make_state<State>();
|
||||||
|
|
||||||
|
state->data.speed = 1. / kCarouselOneDuration;
|
||||||
state->update = [=](crl::time now) {
|
state->update = [=](crl::time now) {
|
||||||
// speed-up-duration = 2 * one / speed.
|
// speed-up-duration = 2 * one / speed.
|
||||||
const auto one = 1.;
|
const auto one = 1.;
|
||||||
|
@ -546,13 +549,112 @@ FingerprintBadge SetupFingerprintBadge(
|
||||||
return { .state = &state->data, .repaints = state->repaints.events() };
|
return { .state = &state->data, .repaints = state->repaints.events() };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SetupFingerprintTooltip(not_null<Ui::RpWidget*> widget) {
|
||||||
|
struct State {
|
||||||
|
std::unique_ptr<Ui::ImportantTooltip> tooltip;
|
||||||
|
Fn<void()> updateGeometry;
|
||||||
|
Fn<void(bool)> toggleTooltip;
|
||||||
|
};
|
||||||
|
const auto state = widget->lifetime().make_state<State>();
|
||||||
|
state->updateGeometry = [=] {
|
||||||
|
if (!state->tooltip.get()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto geometry = Ui::MapFrom(
|
||||||
|
widget->window(),
|
||||||
|
widget,
|
||||||
|
widget->rect());
|
||||||
|
if (geometry.isEmpty()) {
|
||||||
|
state->toggleTooltip(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto weak = QPointer<QWidget>(state->tooltip.get());
|
||||||
|
const auto countPosition = [=](QSize size) {
|
||||||
|
const auto result = geometry.bottomLeft()
|
||||||
|
+ QPoint(
|
||||||
|
geometry.width() / 2,
|
||||||
|
st::confcallFingerprintTooltipSkip)
|
||||||
|
- QPoint(size.width() / 2, 0);
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
state->tooltip.get()->pointAt(
|
||||||
|
geometry,
|
||||||
|
RectPart::Bottom,
|
||||||
|
countPosition);
|
||||||
|
};
|
||||||
|
state->toggleTooltip = [=](bool show) {
|
||||||
|
if (const auto was = state->tooltip.release()) {
|
||||||
|
was->toggleAnimated(false);
|
||||||
|
}
|
||||||
|
if (!show) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto text = tr::lng_confcall_e2e_about(
|
||||||
|
tr::now,
|
||||||
|
Ui::Text::WithEntities);
|
||||||
|
if (text.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
state->tooltip = std::make_unique<Ui::ImportantTooltip>(
|
||||||
|
widget->window(),
|
||||||
|
Ui::MakeNiceTooltipLabel(
|
||||||
|
widget,
|
||||||
|
rpl::single(text),
|
||||||
|
st::confcallFingerprintTooltipMaxWidth,
|
||||||
|
st::confcallFingerprintTooltipLabel),
|
||||||
|
st::confcallFingerprintTooltip);
|
||||||
|
const auto raw = state->tooltip.get();
|
||||||
|
const auto weak = QPointer<QWidget>(raw);
|
||||||
|
const auto destroy = [=] {
|
||||||
|
delete weak.data();
|
||||||
|
};
|
||||||
|
raw->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||||
|
raw->setHiddenCallback(destroy);
|
||||||
|
state->updateGeometry();
|
||||||
|
raw->toggleAnimated(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
widget->events() | rpl::start_with_next([=](not_null<QEvent*> e) {
|
||||||
|
const auto type = e->type();
|
||||||
|
if (type == QEvent::Enter) {
|
||||||
|
state->toggleTooltip(true);
|
||||||
|
} else if (type == QEvent::Leave) {
|
||||||
|
state->toggleTooltip(false);
|
||||||
|
}
|
||||||
|
}, widget->lifetime());
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage MakeVerticalShadow(int height) {
|
||||||
|
const auto ratio = style::DevicePixelRatio();
|
||||||
|
auto result = QImage(
|
||||||
|
QSize(1, height) * ratio,
|
||||||
|
QImage::Format_ARGB32_Premultiplied);
|
||||||
|
result.setDevicePixelRatio(ratio);
|
||||||
|
auto p = QPainter(&result);
|
||||||
|
auto g = QLinearGradient(0, 0, 0, height);
|
||||||
|
auto color = st::groupCallMembersBg->c;
|
||||||
|
auto trans = color;
|
||||||
|
trans.setAlpha(0);
|
||||||
|
g.setStops({
|
||||||
|
{ 0.0, color },
|
||||||
|
{ 0.4, trans },
|
||||||
|
{ 0.6, trans },
|
||||||
|
{ 1.0, color },
|
||||||
|
});
|
||||||
|
p.setCompositionMode(QPainter::CompositionMode_Source);
|
||||||
|
p.fillRect(0, 0, 1, height, g);
|
||||||
|
p.end();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
void SetupFingerprintBadgeWidget(
|
void SetupFingerprintBadgeWidget(
|
||||||
not_null<Ui::RpWidget*> widget,
|
not_null<Ui::RpWidget*> widget,
|
||||||
not_null<const FingerprintBadgeState*> state,
|
not_null<const FingerprintBadgeState*> state,
|
||||||
rpl::producer<> repaints) {
|
rpl::producer<> repaints) {
|
||||||
auto &lifetime = widget->lifetime();
|
auto &lifetime = widget->lifetime();
|
||||||
|
|
||||||
const auto button = Ui::CreateChild<Ui::AbstractButton>(widget);
|
const auto button = Ui::CreateChild<Ui::RpWidget>(widget);
|
||||||
button->show();
|
button->show();
|
||||||
|
|
||||||
const auto label = Ui::CreateChild<Ui::FlatLabel>(
|
const auto label = Ui::CreateChild<Ui::FlatLabel>(
|
||||||
|
@ -633,12 +735,19 @@ void SetupFingerprintBadgeWidget(
|
||||||
st::confcallFingerprintTextMargins);
|
st::confcallFingerprintTextMargins);
|
||||||
const auto count = int(state->entries.size());
|
const auto count = int(state->entries.size());
|
||||||
cache->entries.resize(count);
|
cache->entries.resize(count);
|
||||||
|
cache->shadow = MakeVerticalShadow(outer.height());
|
||||||
for (auto i = 0; i != count; ++i) {
|
for (auto i = 0; i != count; ++i) {
|
||||||
PaintFingerprintEntry(
|
const auto &entry = state->entries[i];
|
||||||
p,
|
auto &cached = cache->entries[i];
|
||||||
state->entries[i],
|
const auto shadowed = entry.speed / state->speed;
|
||||||
cache->entries[i],
|
PaintFingerprintEntry(p, entry, cached, esize);
|
||||||
esize);
|
if (shadowed > 0.) {
|
||||||
|
p.setOpacity(shadowed);
|
||||||
|
p.drawImage(
|
||||||
|
QRect(0, -st::confcallFingerprintMargins.top(), size, outer.height()),
|
||||||
|
cache->shadow);
|
||||||
|
p.setOpacity(1.);
|
||||||
|
}
|
||||||
if (i + 1 == count / 2) {
|
if (i + 1 == count / 2) {
|
||||||
p.translate(size + textOuter.width(), 0);
|
p.translate(size + textOuter.width(), 0);
|
||||||
} else {
|
} else {
|
||||||
|
@ -650,6 +759,8 @@ void SetupFingerprintBadgeWidget(
|
||||||
std::move(repaints) | rpl::start_with_next([=] {
|
std::move(repaints) | rpl::start_with_next([=] {
|
||||||
button->update();
|
button->update();
|
||||||
}, lifetime);
|
}, lifetime);
|
||||||
|
|
||||||
|
SetupFingerprintTooltip(button);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PaintFingerprintEntry(
|
void PaintFingerprintEntry(
|
||||||
|
|
|
@ -37,6 +37,7 @@ struct FingerprintBadgeState {
|
||||||
int added = 0;
|
int added = 0;
|
||||||
};
|
};
|
||||||
std::vector<Entry> entries;
|
std::vector<Entry> entries;
|
||||||
|
float64 speed = 1.;
|
||||||
};
|
};
|
||||||
struct FingerprintBadge {
|
struct FingerprintBadge {
|
||||||
not_null<const FingerprintBadgeState*> state;
|
not_null<const FingerprintBadgeState*> state;
|
||||||
|
@ -60,6 +61,7 @@ struct FingerprintBadgeCache {
|
||||||
std::vector<Emoji> emoji;
|
std::vector<Emoji> emoji;
|
||||||
};
|
};
|
||||||
std::vector<Entry> entries;
|
std::vector<Entry> entries;
|
||||||
|
QImage shadow;
|
||||||
};
|
};
|
||||||
void PaintFingerprintEntry(
|
void PaintFingerprintEntry(
|
||||||
QPainter &p,
|
QPainter &p,
|
||||||
|
|
|
@ -115,6 +115,7 @@ private:
|
||||||
|
|
||||||
struct PrioritizedSelector {
|
struct PrioritizedSelector {
|
||||||
object_ptr<Ui::RpWidget> content = { nullptr };
|
object_ptr<Ui::RpWidget> content = { nullptr };
|
||||||
|
Fn<void()> init;
|
||||||
Fn<bool(int, int, int)> overrideKey;
|
Fn<bool(int, int, int)> overrideKey;
|
||||||
Fn<void(PeerListRowId)> deselect;
|
Fn<void(PeerListRowId)> deselect;
|
||||||
Fn<void()> activate;
|
Fn<void()> activate;
|
||||||
|
@ -320,7 +321,7 @@ void ConfInviteRow::elementsPaint(
|
||||||
[[nodiscard]] PrioritizedSelector PrioritizedInviteSelector(
|
[[nodiscard]] PrioritizedSelector PrioritizedInviteSelector(
|
||||||
const ConfInviteStyles &st,
|
const ConfInviteStyles &st,
|
||||||
std::vector<not_null<UserData*>> users,
|
std::vector<not_null<UserData*>> users,
|
||||||
Fn<bool(not_null<PeerListRow*>, bool)> toggleGetChecked,
|
Fn<bool(not_null<PeerListRow*>, bool, anim::type)> toggleGetChecked,
|
||||||
Fn<bool()> lastSelectWithVideo,
|
Fn<bool()> lastSelectWithVideo,
|
||||||
Fn<void(bool)> setLastSelectWithVideo) {
|
Fn<void(bool)> setLastSelectWithVideo) {
|
||||||
class PrioritizedController final : public PeerListController {
|
class PrioritizedController final : public PeerListController {
|
||||||
|
@ -328,7 +329,10 @@ void ConfInviteRow::elementsPaint(
|
||||||
PrioritizedController(
|
PrioritizedController(
|
||||||
const ConfInviteStyles &st,
|
const ConfInviteStyles &st,
|
||||||
std::vector<not_null<UserData*>> users,
|
std::vector<not_null<UserData*>> users,
|
||||||
Fn<bool(not_null<PeerListRow*>, bool)> toggleGetChecked,
|
Fn<bool(
|
||||||
|
not_null<PeerListRow*>,
|
||||||
|
bool,
|
||||||
|
anim::type)> toggleGetChecked,
|
||||||
Fn<bool()> lastSelectWithVideo,
|
Fn<bool()> lastSelectWithVideo,
|
||||||
Fn<void(bool)> setLastSelectWithVideo)
|
Fn<void(bool)> setLastSelectWithVideo)
|
||||||
: _st(st)
|
: _st(st)
|
||||||
|
@ -368,7 +372,7 @@ void ConfInviteRow::elementsPaint(
|
||||||
void toggleRowSelected(not_null<PeerListRow*> row, bool video) {
|
void toggleRowSelected(not_null<PeerListRow*> row, bool video) {
|
||||||
delegate()->peerListSetRowChecked(
|
delegate()->peerListSetRowChecked(
|
||||||
row,
|
row,
|
||||||
_toggleGetChecked(row, video));
|
_toggleGetChecked(row, video, anim::type::normal));
|
||||||
}
|
}
|
||||||
|
|
||||||
Main::Session &session() const override {
|
Main::Session &session() const override {
|
||||||
|
@ -382,7 +386,7 @@ void ConfInviteRow::elementsPaint(
|
||||||
private:
|
private:
|
||||||
const ConfInviteStyles &_st;
|
const ConfInviteStyles &_st;
|
||||||
std::vector<not_null<UserData*>> _users;
|
std::vector<not_null<UserData*>> _users;
|
||||||
Fn<bool(not_null<PeerListRow*>, bool)> _toggleGetChecked;
|
Fn<bool(not_null<PeerListRow*>, bool, anim::type)> _toggleGetChecked;
|
||||||
Fn<bool()> _lastSelectWithVideo;
|
Fn<bool()> _lastSelectWithVideo;
|
||||||
Fn<void(bool)> _setLastSelectWithVideo;
|
Fn<void(bool)> _setLastSelectWithVideo;
|
||||||
|
|
||||||
|
@ -448,8 +452,19 @@ void ConfInviteRow::elementsPaint(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const auto init = [=] {
|
||||||
|
for (const auto &user : users) {
|
||||||
|
if (const auto row = delegate->peerListFindRow(user->id.value)) {
|
||||||
|
delegate->peerListSetRowChecked(
|
||||||
|
row,
|
||||||
|
toggleGetChecked(row, false, anim::type::instant));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
.content = std::move(result),
|
.content = std::move(result),
|
||||||
|
.init = init,
|
||||||
.overrideKey = overrideKey,
|
.overrideKey = overrideKey,
|
||||||
.deselect = deselect,
|
.deselect = deselect,
|
||||||
.activate = activate,
|
.activate = activate,
|
||||||
|
@ -608,12 +623,15 @@ void ConfInviteController::prepareViewHook() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfInviteController::addPriorityInvites() {
|
void ConfInviteController::addPriorityInvites() {
|
||||||
const auto toggleGetChecked = [=](not_null<PeerListRow*> row, bool video) {
|
const auto toggleGetChecked = [=](
|
||||||
|
not_null<PeerListRow*> row,
|
||||||
|
bool video,
|
||||||
|
anim::type animated) {
|
||||||
const auto result = toggleRowGetChecked(row, video);
|
const auto result = toggleRowGetChecked(row, video);
|
||||||
delegate()->peerListSetForeignRowChecked(
|
delegate()->peerListSetForeignRowChecked(
|
||||||
row,
|
row,
|
||||||
result,
|
result,
|
||||||
anim::type::normal);
|
animated);
|
||||||
|
|
||||||
_hasSelected = (delegate()->peerListSelectedRowsCount() > 0);
|
_hasSelected = (delegate()->peerListSelectedRowsCount() > 0);
|
||||||
|
|
||||||
|
@ -630,6 +648,13 @@ void ConfInviteController::addPriorityInvites() {
|
||||||
scrollTo
|
scrollTo
|
||||||
) | rpl::start_to_stream(_prioritizeScrollRequests, lifetime());
|
) | rpl::start_to_stream(_prioritizeScrollRequests, lifetime());
|
||||||
}
|
}
|
||||||
|
if (const auto onstack = _prioritizeRows.init) {
|
||||||
|
onstack();
|
||||||
|
|
||||||
|
// Force finishing in instant adding checked rows bunch.
|
||||||
|
delegate()->peerListAddSelectedPeers(
|
||||||
|
std::vector<not_null<PeerData*>>());
|
||||||
|
}
|
||||||
delegate()->peerListSetAboveWidget(std::move(_prioritizeRows.content));
|
delegate()->peerListSetAboveWidget(std::move(_prioritizeRows.content));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -77,23 +77,6 @@ constexpr auto kControlsBackgroundOpacity = 0.8;
|
||||||
constexpr auto kOverrideActiveColorBgAlpha = 172;
|
constexpr auto kOverrideActiveColorBgAlpha = 172;
|
||||||
constexpr auto kHideControlsTimeout = 5 * crl::time(1000);
|
constexpr auto kHideControlsTimeout = 5 * crl::time(1000);
|
||||||
|
|
||||||
[[nodiscard]] QString ComposeTitle(const QByteArray &hash) {
|
|
||||||
auto result = tr::lng_confcall_join_title(tr::now);
|
|
||||||
if (hash.size() >= 32) {
|
|
||||||
const auto fp = bytes::make_span(hash).subspan(0, 32);
|
|
||||||
const auto emoji = Calls::ComputeEmojiFingerprint(fp);
|
|
||||||
result += QString::fromUtf8(" \xc2\xb7 ");
|
|
||||||
const auto base = result.size();
|
|
||||||
for (const auto &single : emoji) {
|
|
||||||
result += single->text();
|
|
||||||
}
|
|
||||||
MTP_LOG(0, ("Got Emoji: %1.").arg(result.mid(base)));
|
|
||||||
} else {
|
|
||||||
MTP_LOG(0, ("Cleared Emoji."));
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
void UnpinMaximized(not_null<QWidget*> widget) {
|
void UnpinMaximized(not_null<QWidget*> widget) {
|
||||||
SetWindowPos(
|
SetWindowPos(
|
||||||
|
@ -2417,7 +2400,7 @@ void Panel::updateMembersGeometry() {
|
||||||
|
|
||||||
rpl::producer<QString> Panel::titleText() {
|
rpl::producer<QString> Panel::titleText() {
|
||||||
if (_call->conference()) {
|
if (_call->conference()) {
|
||||||
return _call->emojiHashValue() | rpl::map(ComposeTitle);
|
return tr::lng_confcall_join_title();
|
||||||
}
|
}
|
||||||
return rpl::combine(
|
return rpl::combine(
|
||||||
Info::Profile::NameValue(_peer),
|
Info::Profile::NameValue(_peer),
|
||||||
|
|
Loading…
Add table
Reference in a new issue