From 34840766b260a6a77cf1d455e66b2a72faa08414 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 14 Aug 2020 16:51:47 +0400 Subject: [PATCH] Move fingerprint / signal bars to a separate widget. --- Telegram/SourceFiles/calls/calls.style | 23 ++- .../calls/calls_emoji_fingerprint.cpp | 147 ++++++++++++++++++ .../calls/calls_emoji_fingerprint.h | 13 +- Telegram/SourceFiles/calls/calls_panel.cpp | 137 ++++------------ Telegram/SourceFiles/calls/calls_panel.h | 18 +-- .../SourceFiles/calls/calls_signal_bars.cpp | 35 ++--- .../SourceFiles/calls/calls_signal_bars.h | 11 +- .../window/themes/window_themes_embedded.cpp | 2 +- Telegram/ThirdParty/tgcalls | 2 +- Telegram/lib_ui | 2 +- 10 files changed, 222 insertions(+), 168 deletions(-) diff --git a/Telegram/SourceFiles/calls/calls.style b/Telegram/SourceFiles/calls/calls.style index 0af42ccbc..d4134c5f2 100644 --- a/Telegram/SourceFiles/calls/calls.style +++ b/Telegram/SourceFiles/calls/calls.style @@ -14,6 +14,8 @@ CallSignalBars { width: pixels; radius: pixels; skip: pixels; + min: pixels; + max: pixels; color: color; inactiveOpacity: double; } @@ -79,9 +81,12 @@ callOutgoingDefaultSize: size(160px, 110px); callInnerPadding: 12px; -callFingerprintPadding: margins(9px, 4px, 9px, 5px); -callFingerprintTop: 11px; -callFingerprintSkip: 3px; +callFingerprintPadding: margins(10px, 4px, 8px, 5px); +callFingerprintSkip: 4px; +callFingerprintSignalBarsSkip: 2px; +callSignalBarsPadding: margins(8px, 9px, 11px, 5px); + +callFingerprintTop: 8px; callFingerprintBottom: -16px; callTooltipMutedIcon: icon{{ "calls_mute_tooltip", videoPlayIconFg }}; @@ -295,17 +300,21 @@ callDebugLabel: FlatLabel(defaultFlatLabel) { callPanelDuration: 150; callPanelSignalBars: CallSignalBars { - width: 3px; + width: 2px; radius: 1px; - skip: 1px; + skip: 2px; + min: 4px; + max: 10px; color: callNameFg; inactiveOpacity: 0.5; } callBarSignalBars: CallSignalBars(callPanelSignalBars) { + width: 3px; + skip: 1px; + min: 3px; + max: 12px; color: callBarFg; } -callSignalMargin: 8px; -callSignalPadding: 4px; callTitleButton: IconButton { width: 34px; diff --git a/Telegram/SourceFiles/calls/calls_emoji_fingerprint.cpp b/Telegram/SourceFiles/calls/calls_emoji_fingerprint.cpp index 5634090aa..923a4b807 100644 --- a/Telegram/SourceFiles/calls/calls_emoji_fingerprint.cpp +++ b/Telegram/SourceFiles/calls/calls_emoji_fingerprint.cpp @@ -8,11 +8,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "calls/calls_emoji_fingerprint.h" #include "calls/calls_call.h" +#include "calls/calls_signal_bars.h" +#include "lang/lang_keys.h" +#include "data/data_user.h" +#include "ui/widgets/tooltip.h" #include "ui/emoji_config.h" +#include "ui/rp_widget.h" +#include "styles/style_calls.h" namespace Calls { namespace { +constexpr auto kTooltipShowTimeoutMs = 1000; + const ushort Data[] = { 0xd83d, 0xde09, 0xd83d, 0xde0d, 0xd83d, 0xde1b, 0xd83d, 0xde2d, 0xd83d, 0xde31, 0xd83d, 0xde21, 0xd83d, 0xde0e, 0xd83d, 0xde34, 0xd83d, 0xde35, 0xd83d, 0xde08, 0xd83d, 0xde2c, 0xd83d, 0xde07, @@ -143,7 +151,146 @@ std::vector ComputeEmojiFingerprint(not_null call) { } } return result; +} +object_ptr CreateFingerprintAndSignalBars( + not_null parent, + not_null call) { + class EmojiTooltipShower final : public Ui::AbstractTooltipShower { + public: + EmojiTooltipShower(not_null window, const QString &text) + : _window(window) + , _text(text) { + } + + QString tooltipText() const override { + return _text; + } + QPoint tooltipPos() const override { + return QCursor::pos(); + } + bool tooltipWindowActive() const override { + return _window->isActiveWindow(); + } + + private: + const not_null _window; + const QString _text; + + }; + + auto result = object_ptr(parent); + const auto raw = result.data(); + + // Emoji tooltip. + const auto shower = raw->lifetime().make_state( + parent->window(), + tr::lng_call_fingerprint_tooltip( + tr::now, + lt_user, + call->user()->name)); + raw->setMouseTracking(true); + raw->events( + ) | rpl::start_with_next([=](not_null e) { + if (e->type() == QEvent::MouseMove) { + Ui::Tooltip::Show(kTooltipShowTimeoutMs, shower); + } else if (e->type() == QEvent::Leave) { + Ui::Tooltip::Hide(); + } + }, raw->lifetime()); + + // Signal bars. + const auto bars = Ui::CreateChild( + raw, + call, + st::callPanelSignalBars); + bars->setAttribute(Qt::WA_TransparentForMouseEvents); + + // Geometry. + const auto print = ComputeEmojiFingerprint(call); + auto realSize = Ui::Emoji::GetSizeNormal(); + auto size = realSize / cIntRetinaFactor(); + auto count = print.size(); + const auto printSize = QSize( + count * size + (count - 1) * st::callFingerprintSkip, + size); + const auto fullPrintSize = QRect( + QPoint(), + printSize + ).marginsAdded(st::callFingerprintPadding).size(); + const auto fullBarsSize = bars->rect().marginsAdded( + st::callSignalBarsPadding + ).size(); + const auto fullSize = QSize( + (fullPrintSize.width() + + st::callFingerprintSignalBarsSkip + + fullBarsSize.width()), + fullPrintSize.height()); + raw->resize(fullSize); + bars->moveToRight( + st::callSignalBarsPadding.right(), + st::callSignalBarsPadding.top()); + + // Paint. + const auto background = raw->lifetime().make_state( + fullSize * cIntRetinaFactor(), + QImage::Format_ARGB32_Premultiplied); + rpl::merge( + rpl::single(rpl::empty_value()), + Ui::Emoji::Updated(), + style::PaletteChanged() + ) | rpl::start_with_next([=] { + background->fill(Qt::transparent); + + // Prepare. + auto p = QPainter(background); + const auto height = fullSize.height(); + const auto fullPrintRect = QRect(QPoint(), fullPrintSize); + const auto fullBarsRect = QRect( + fullSize.width() - fullBarsSize.width(), + 0, + fullBarsSize.width(), + height); + const auto bigRadius = height / 2; + const auto smallRadius = st::buttonRadius; + const auto hq = PainterHighQualityEnabler(p); + p.setPen(Qt::NoPen); + p.setBrush(st::callBgButton); + + // Fingerprint part. + p.setClipRect(0, 0, fullPrintSize.width() / 2, height); + p.drawRoundedRect(fullPrintRect, bigRadius, bigRadius); + p.setClipRect(fullPrintSize.width() / 2, 0, fullSize.width(), height); + p.drawRoundedRect(fullPrintRect, smallRadius, smallRadius); + + // Signal bars part. + const auto middle = fullBarsRect.center().x(); + p.setClipRect(0, 0, middle, height); + p.drawRoundedRect(fullBarsRect, smallRadius, smallRadius); + p.setClipRect(middle, 0, fullBarsRect.width(), height); + p.drawRoundedRect(fullBarsRect, bigRadius, bigRadius); + + // Emoji. + const auto realSize = Ui::Emoji::GetSizeNormal(); + const auto size = realSize / cIntRetinaFactor(); + auto left = st::callFingerprintPadding.left(); + const auto top = st::callFingerprintPadding.top(); + p.setClipping(false); + for (const auto emoji : print) { + Ui::Emoji::Draw(p, emoji, realSize, left, top); + left += st::callFingerprintSkip + size; + } + + raw->update(); + }, raw->lifetime()); + + raw->paintRequest( + ) | rpl::start_with_next([=](QRect clip) { + QPainter(raw).drawImage(raw->rect(), *background); + }, raw->lifetime()); + + raw->show(); + return result; } } // namespace Calls diff --git a/Telegram/SourceFiles/calls/calls_emoji_fingerprint.h b/Telegram/SourceFiles/calls/calls_emoji_fingerprint.h index ef5b84d5f..9ec4e9b68 100644 --- a/Telegram/SourceFiles/calls/calls_emoji_fingerprint.h +++ b/Telegram/SourceFiles/calls/calls_emoji_fingerprint.h @@ -7,10 +7,21 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once +#include "base/object_ptr.h" + +namespace Ui { +class RpWidget; +} // namespace Ui + namespace Calls { class Call; -std::vector ComputeEmojiFingerprint(not_null call); +[[nodiscard]] std::vector ComputeEmojiFingerprint( + not_null call); + +[[nodiscard]] object_ptr CreateFingerprintAndSignalBars( + not_null parent, + not_null call); } // namespace Calls diff --git a/Telegram/SourceFiles/calls/calls_panel.cpp b/Telegram/SourceFiles/calls/calls_panel.cpp index 490040f3c..0a05a0dda 100644 --- a/Telegram/SourceFiles/calls/calls_panel.cpp +++ b/Telegram/SourceFiles/calls/calls_panel.cpp @@ -52,11 +52,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include namespace Calls { -namespace { - -constexpr auto kTooltipShowTimeoutMs = 1000; - -} // namespace class Panel::Button final : public Ui::RippleButton { public: @@ -352,7 +347,8 @@ void Panel::initWindow() { #endif // Q_OS_WIN const auto buttonWidth = st::callCancel.button.width; const auto buttonsWidth = buttonWidth * 4; - const auto inControls = _fingerprintArea.contains(widgetPoint) + const auto inControls = (_fingerprint + && _fingerprint->geometry().contains(widgetPoint)) || QRect( (widget()->width() - buttonsWidth) / 2, _answerHangupRedial->y(), @@ -397,13 +393,9 @@ void Panel::initWidget() { paint(clip); }, widget()->lifetime()); - widget()->events( - ) | rpl::start_with_next([=](not_null e) { - if (e->type() == QEvent::MouseMove) { - handleMouseMove(static_cast(e.get())); - } else if (e->type() == QEvent::Resize) { - updateControlsGeometry(); - } + widget()->sizeValue( + ) | rpl::skip(1) | rpl::start_with_next([=] { + updateControlsGeometry(); }, widget()->lifetime()); } @@ -483,12 +475,6 @@ void Panel::reinitWithCall(Call *call) { _user = _call->user(); - _signalBars.create( - widget(), - _call, - st::callPanelSignalBars, - [=] { widget()->rtlupdate(signalBarsRect()); }); - auto remoteMuted = _call->remoteAudioStateValue( ) | rpl::map([=](Call::RemoteAudioState state) { return (state == Call::RemoteAudioState::Muted); @@ -585,6 +571,7 @@ void Panel::createRemoteAudioMute() { rpl::single(_user->shortName())), st::callRemoteAudioMute), st::callTooltipPadding); + _remoteAudioMute->setAttribute(Qt::WA_TransparentForMouseEvents); _remoteAudioMute->paintRequest( ) | rpl::start_with_next([=] { @@ -763,30 +750,27 @@ QRect Panel::outgoingFrameGeometry() const { return _outgoingVideoBubble->geometry(); } -void Panel::updateFingerprintGeometry() { - auto realSize = Ui::Emoji::GetSizeNormal(); - auto size = realSize / cIntRetinaFactor(); - auto count = _fingerprint.size(); - auto rectWidth = count * size + (count - 1) * st::callFingerprintSkip; - auto rectHeight = size; - auto left = (widget()->width() - rectWidth) / 2; - _fingerprintArea = QRect( - left, - st::callFingerprintTop + st::callFingerprintPadding.top(), - rectWidth, - rectHeight - ).marginsAdded(st::callFingerprintPadding); - _fingerprintHeight = st::callFingerprintTop + _fingerprintArea.height() + st::callFingerprintBottom; -} - void Panel::updateControlsGeometry() { if (widget()->size().isEmpty()) { return; } - updateFingerprintGeometry(); + if (_fingerprint) { +#ifdef Q_OS_WIN + const auto minRight = _controls->geometry().width() + + st::callFingerprintTop; +#else // Q_OS_WIN + const auto minRight = 0; +#endif // _controls + const auto desired = (widget()->width() - _fingerprint->width()) / 2; + _fingerprint->moveToRight( + std::max(desired, minRight), + st::callFingerprintTop); + } const auto innerHeight = std::max(widget()->height(), st::callHeightMin); const auto innerWidth = widget()->width() - 2 * st::callInnerPadding; - const auto availableTop = _fingerprintHeight; + const auto availableTop = st::callFingerprintTop + + (_fingerprint ? _fingerprint->height() : 0) + + st::callFingerprintBottom; const auto available = widget()->height() - st::callBottomControlsHeight - availableTop; @@ -851,10 +835,6 @@ void Panel::updateControlsGeometry() { _cancel->moveToLeft((widget()->width() - bothWidth) / 2, _buttonsTop); updateHangupGeometry(); - - const auto skip = st::callSignalMargin + st::callSignalPadding; - const auto delta = (_signalBars->width() - _signalBars->height()); - _signalBars->moveToLeft(skip, skip + delta / 2); } void Panel::updateOutgoingVideoBubbleGeometry() { @@ -910,44 +890,6 @@ void Panel::paint(QRect clip) { fillTopShadow(p, incoming); } _call->videoIncoming()->markFrameShown(); - - if (_signalBars->isDisplayed()) { - paintSignalBarsBg(p); - } - - if (!_fingerprint.empty() && clip.intersects(_fingerprintArea)) { - const auto radius = _fingerprintArea.height() / 2; - auto hq = PainterHighQualityEnabler(p); - p.setBrush(st::callBgButton); - p.setPen(Qt::NoPen); - p.drawRoundedRect(_fingerprintArea, radius, radius); - - const auto realSize = Ui::Emoji::GetSizeNormal(); - const auto size = realSize / cIntRetinaFactor(); - auto left = _fingerprintArea.left() + st::callFingerprintPadding.left(); - const auto top = _fingerprintArea.top() + st::callFingerprintPadding.top(); - for (const auto emoji : _fingerprint) { - Ui::Emoji::Draw(p, emoji, realSize, left, top); - left += st::callFingerprintSkip + size; - } - } -} - -QRect Panel::signalBarsRect() const { - const auto size = 2 * st::callSignalPadding + _signalBars->width(); - return QRect( - st::callSignalMargin, - st::callSignalMargin, - size, - size); -} - -void Panel::paintSignalBarsBg(Painter &p) { - App::roundRect( - p, - signalBarsRect(), - st::callBgButton, - ImageRoundRadius::Small); } void Panel::handleClose() { @@ -956,30 +898,10 @@ void Panel::handleClose() { } } -void Panel::handleMouseMove(not_null e) { - if (_fingerprintArea.contains(e->pos())) { - Ui::Tooltip::Show(kTooltipShowTimeoutMs, this); - } else { - Ui::Tooltip::Hide(); - } -} - not_null Panel::widget() const { return _window->body(); } -QString Panel::tooltipText() const { - return tr::lng_call_fingerprint_tooltip(tr::now, lt_user, _user->name); -} - -QPoint Panel::tooltipPos() const { - return QCursor::pos(); -} - -bool Panel::tooltipWindowActive() const { - return _window->isActiveWindow(); -} - void Panel::stateChanged(State state) { Expects(_call != nullptr); @@ -990,7 +912,7 @@ void Panel::stateChanged(State state) { && (state != State::EndedByOtherDevice) && (state != State::FailedHangingUp) && (state != State::Failed)) { - auto toggleButton = [this](auto &&button, bool visible) { + auto toggleButton = [&](auto &&button, bool visible) { button->toggle( visible, _window->isHidden() @@ -1018,8 +940,11 @@ void Panel::stateChanged(State state) { _answerHangupRedialState = answerHangupRedialState; refreshAnswerHangupRedialLabel(); } - if (_fingerprint.empty() && _call->isKeyShaForFingerprintReady()) { - fillFingerprint(); + if (!_call->isKeyShaForFingerprintReady()) { + _fingerprint.destroy(); + } else if (!_fingerprint) { + _fingerprint = CreateFingerprintAndSignalBars(widget(), _call); + updateControlsGeometry(); } } } @@ -1037,14 +962,6 @@ void Panel::refreshAnswerHangupRedialLabel() { }()); } -void Panel::fillFingerprint() { - Expects(_call != nullptr); - - _fingerprint = ComputeEmojiFingerprint(_call); - updateControlsGeometry(); - widget()->update(); -} - void Panel::updateStatusText(State state) { auto statusText = [this, state]() -> QString { switch (state) { diff --git a/Telegram/SourceFiles/calls/calls_panel.h b/Telegram/SourceFiles/calls/calls_panel.h index ccdff5791..c27bc6c13 100644 --- a/Telegram/SourceFiles/calls/calls_panel.h +++ b/Telegram/SourceFiles/calls/calls_panel.h @@ -9,8 +9,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/weak_ptr.h" #include "base/timer.h" +#include "base/object_ptr.h" #include "calls/calls_call.h" -#include "ui/widgets/tooltip.h" #include "ui/effects/animations.h" #include "ui/rp_widget.h" @@ -41,12 +41,11 @@ struct CallBodyLayout; namespace Calls { -class Tooltip; class Userpic; class SignalBars; class VideoBubble; -class Panel final : private Ui::AbstractTooltipShower { +class Panel final { public: Panel(not_null call); ~Panel(); @@ -68,11 +67,6 @@ private: [[nodiscard]] not_null widget() const; - // AbstractTooltipShower interface - QString tooltipText() const override; - QPoint tooltipPos() const override; - bool tooltipWindowActive() const override; - void paint(QRect clip); void initWindow(); @@ -84,12 +78,10 @@ private: void initBottomShadow(); void handleClose(); - void handleMouseMove(not_null e); QRect signalBarsRect() const; void paintSignalBarsBg(Painter &p); - void updateFingerprintGeometry(); void updateControlsGeometry(); void updateHangupGeometry(); void updateStatusGeometry(); @@ -98,7 +90,6 @@ private: void showControls(); void updateStatusText(State state); void startDurationUpdateTimer(crl::time currentDuration); - void fillFingerprint(); void setIncomingSize(QSize size); void fillTopShadow(QPainter &p, QRect incoming); void fillBottomShadow(QPainter &p, QRect incoming); @@ -136,16 +127,13 @@ private: object_ptr