From 99926be57a859c2b149656ea943fa052f54dc944 Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 19 Jul 2023 17:31:52 +0400 Subject: [PATCH] Add privacy badge to stories userpic. --- .../icons/mediaview/mini_close_friends.png | Bin 0 -> 325 bytes .../icons/mediaview/mini_close_friends@2x.png | Bin 0 -> 557 bytes .../icons/mediaview/mini_close_friends@3x.png | Bin 0 -> 726 bytes .../icons/mediaview/mini_contacts.png | Bin 0 -> 412 bytes .../icons/mediaview/mini_contacts@2x.png | Bin 0 -> 720 bytes .../icons/mediaview/mini_contacts@3x.png | Bin 0 -> 1106 bytes .../mediaview/mini_selected_contacts.png | Bin 0 -> 395 bytes .../mediaview/mini_selected_contacts@2x.png | Bin 0 -> 666 bytes .../mediaview/mini_selected_contacts@3x.png | Bin 0 -> 996 bytes .../media/stories/media_stories_header.cpp | 216 ++++++++++++++++-- .../media/stories/media_stories_header.h | 12 + .../SourceFiles/media/view/media_view.style | 7 + 12 files changed, 217 insertions(+), 18 deletions(-) create mode 100644 Telegram/Resources/icons/mediaview/mini_close_friends.png create mode 100644 Telegram/Resources/icons/mediaview/mini_close_friends@2x.png create mode 100644 Telegram/Resources/icons/mediaview/mini_close_friends@3x.png create mode 100644 Telegram/Resources/icons/mediaview/mini_contacts.png create mode 100644 Telegram/Resources/icons/mediaview/mini_contacts@2x.png create mode 100644 Telegram/Resources/icons/mediaview/mini_contacts@3x.png create mode 100644 Telegram/Resources/icons/mediaview/mini_selected_contacts.png create mode 100644 Telegram/Resources/icons/mediaview/mini_selected_contacts@2x.png create mode 100644 Telegram/Resources/icons/mediaview/mini_selected_contacts@3x.png diff --git a/Telegram/Resources/icons/mediaview/mini_close_friends.png b/Telegram/Resources/icons/mediaview/mini_close_friends.png new file mode 100644 index 0000000000000000000000000000000000000000..5c3072447e6761a60964c3a7f5a9613034b93cf9 GIT binary patch literal 325 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(-uuz(rC1}QWNE&K$e)_b}*hG>YM zov@qtu!2O3ZcsqLg&={Gx@k;DobvWIh&v>0;W@yT!%#Fa#^F=9i`S|rTqXaH%PQ=8 zXTD3Uw($Gf7nhs1ev4zdT&Aa;)bq7={noN`o|*^S6FC$Eyo%4A-n8cAazUqtYt6S! zm$n>O*l@7=zMJ;JYnHLkGiTl6d+^#sL-ENM0~@vvxsIAw?=kGqm$`LJ;>}Ts<4O`e tqVKF{AD@5j^BK$a9Bj%Drv32#&LH(>rkPa6ts0OQJYD@<);T3K0RRlkYpehO literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/mediaview/mini_close_friends@2x.png b/Telegram/Resources/icons/mediaview/mini_close_friends@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..82dd44af9567a6ae42ec3f9e0abe9d63dfaecb3f GIT binary patch literal 557 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1SJ1Ryj={W7>k44ofy`glX(f`xTHpSruq6Z zXaU(A42G$TlC0TWzSVF5FO4N|zKE$K5**>q1A#}E(R zw^R17YD$newpC*Vx5kCGj;w-WU-A5dYq>Kr-$h=0S)C{f_&nvI2mSGl9(Y+M6etK%;%_(7*OjyH`wc>)iUhT4z z;K^7j!{6?l@U$v$rSyiVwOs4eRvdrDCeh|tJ6YxDuSkxJ*aI1>6~EQ)w|IB>VZ;9* zJ%@%Z8I0KmtFq>wPu~%1@O%`4v+QDQaqc*nJ(oh-*T{dSly d{Npue4Re#^JuB;scafl|@^tlcS?83{1OT%u-kSgb literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/mediaview/mini_close_friends@3x.png b/Telegram/Resources/icons/mediaview/mini_close_friends@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..1f024ce73a918ea36c5efa2249200872f12e4ea8 GIT binary patch literal 726 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1SD@HZ*nce=uX?Cq?K~pc+qNxiO zJjmwfYnf4*VjOvbYqHWE%kT4w{qq*h+!6CCKo|sn{NM~(ef4bG=D78O7WIY`J)RU; z*u=9l>BxS&l4I5>U3Bb{r*q!+YO~odc@sG=82T>_@_gTRghAHWM20WZnPINs6d$$s z->q{w6oM|zv$=G}ZSlj81}_*UPMS7V<=N+-r=L3Yw<~xobnuJ%(tTj-vY?d_I%2(U z`{UR-g*si-CL6vrpXn2}`fA?x+wZ?yOitK+_uTW(#~&wpnTs8H{PAeg!P$+nGMfa< zzZu7t?~b*OJ$lD)`C;P&6`eDiqu<&&E{3?yWF-5zmIJ?&&B!WC)8(scj5 zJP%t@Y^4r|Q`j-yhYyZkdHuEGRal|N-?OvV#jU@eXTJLF*RKnlUFr->Hpl2a_Pp}_ zM=g)ii!IAymjq}S#K$G2FKEcvZr*P%*MIcwOC1dxQ?+w{<&(mG*Vw7)F{o-VZQUMX zA+s-T{TH4i92yGyI61||)TW$G+Z(q&pm!n*tBYF`bELtP(@!mw)HbL-+HagvR2U^y zt^D@)=?@p?oPYlQdgFl!O5Or9tG#_=)I647K9hFiQnqyl%b8sJjs>SnL<_RE&N_IZ zXy=*`EfZGxD;|~6@vd$@?2>@_uCa3@a literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/mediaview/mini_contacts.png b/Telegram/Resources/icons/mediaview/mini_contacts.png new file mode 100644 index 0000000000000000000000000000000000000000..bc716cab41b4aa5c0aea7361c2e85903c9436a81 GIT binary patch literal 412 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(-uuz(rC1}QWNE&K#j#^&kb7@{G% zH^8>fH9_R*%y*(HSE^KmoRkH%H}LEhl-|IlXd5cDO}4Gp7qR*af|zXFL&SP zxz*?X3b&NEmt-q=PI_!nD6@NA_Z5r2-}~;HJioqa&9ck|4-Q;6IU8K)8lZpL>C${< zj`N@Y9BTI5Q5Gn2Uis7}75~`l0x_$(XUsUxad<*epon4T(Tk44ofy`glX(f`xTHpSruq6Z zXaU(A42G$TlC0TWzSVF5FO4N|zKE$K4@1Cy1fi(`lf z@7qx8{*8_zahvi)d6kYVeU^V==U>J@j6&Ppoc}PlMlBRybn(It6_xn*77nTDhfKXX z@1}=`J)0}J=hokExpO|BDV}FsoObTxw{PG7{^IhOH!ooE#UC|x>#t{Tzs<|W{A2ai zECY!Ut*J#jYb<2uY28uP5a~)W+Ij!I{kxjocllU$#?*yBI@lDq{(Xs6`4m=z3o^r0n+wZ@-HocJO@>F*)59HZ#r@7d%{qVsy?g^J)9+BG`RiO1InM?FK%{j&;X8mj;;^*~q0|t8DO5%QlD1b z=ql_tc%!i3YL@zB&O=Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91FrWhf1ONa40RR91FaQ7m0NXcg3;+NFyh%hsR9Fe^SIaB>Q4}{rHbRZp zc>LtOkYYU6N~mcTSRpBiVrNU#`~|`wl8q%bVQhNc31v(2QECMsetB?kKw3rwhlhu}zCe$SjZx-yO3>ckPKm(Z-rhEl znksa3bWrBgp&P@X_4Rpqc~S&PfUs_`io@(dYcL29?#mzS6E z)@#~qwxy*dy!9d#jsy{ADkC3~hsj}xUL!7vej}M`m#db*@>aQRKDi{pLHUb_k6bcFM z*i!zNK^zDK8X6jyo}NGo(H}7Cqvzk;+~5?ar>7Sd7S`6*R#sLTAL{k>bv>X7r?|Kn zU69`vqcK8EO-&gzNHpe|nHi`t6P^HUX=$m6d#QMKc9sVMjsA}hptQ6!slLEO!n|JZ z`}?~9f)P~ER9$9eW!>K1GPvjG=a!ZhRpU=RjBGD2FAU`V{yrxs=Tm6?x4*xiVg2}P zW@Kc9fkZXJYVl7_PHJ!%nNex1ZmOoH=JfPbL%_kZYA(@FOiaY3&=4OVAG^D|B}xRV zx3~A{=}EKQ*w_FRp=gl}AeJYf@9gX#tmNcm3I?K{o}S&^T`d&6k(-!B!@S}=@n`(% z>I#>?v$M0hy82J#0nydfH9b9jeSOVB!Q~$q7+@eVCUEai3#j_Ug3t)4;REC6A3f@^ zqh^kdj-t0v{SKW0W4uA-H1PGqsYOFXgTy3?w>E8IMnDh8Wwh4G4Wf&S;u(l%;5TI8 YKb9#QwuW; zCrEs8@bl}NI(2G9x?aSF2mk;7@0YiqHgziNtHkv5^=sCgIdS5^x_A!{julc&UESS{ zU9)D-R=l%fg~kp!HH+7W+xgeW?QJT^zrT++<-^C1jywMR`BRXp!Nbhld~O35H&%Xp#CnGN|Nnn~Z(qF{8XU|l$XHZdRP?~DS;D&PO~dLob9YjFuLAD<3`A0Hnde|~;`zntx=HEaF=S@-V6b)0(Wz{J31-xJ^dweK}3 OAUs|DT-G@yGywpfbC=Bk literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/mediaview/mini_selected_contacts@2x.png b/Telegram/Resources/icons/mediaview/mini_selected_contacts@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..d12f95496d4a6772f01ef6e1156e63c469728599 GIT binary patch literal 666 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1SJ1Ryj={W7>k44ofy`glX(f`xTHpSruq6Z zXaU(A42G$TlC0TWzSVF5FO4N|zKE$K4@0~3plx=wTzr?-a2%EN*@H3T<-{H$Jvzz3JIjbI+w2XYRfKzW?~+-+%Yre)}lg ztD^r!iBH%63L$h1gyAN9Dd@IcN- zZL-(WrbtWKgV*x5JOBDw6ZHM&gBrW%MV38Nebgf7967NpNV8F+^+TnD{v|KImDgWu zoP7RS#&6rjj452780YD)nbB7_1&t;ucLK6V7G#14G literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/mediaview/mini_selected_contacts@3x.png b/Telegram/Resources/icons/mediaview/mini_selected_contacts@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..90860348b9d729a0a23d59f8e994773bad20966f GIT binary patch literal 996 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1SD@Hos zE|-HGYkXPTB3xP}SodySQ*Hll=ZVZ`XI{#1zl)u-dDhQozipq*{`g&b^{OfUfetV@ zjY;d}n_07FO`Iq=`)pWrbah3A1Xt_4`SX{~{%;^$nX>t2{GRu}e%*Rl@TWkc^IcVS zwYL?5)44WhJw3gGrY*-0 z9z5u@@WA#ix5Zz-el-a;OEQ}IqweL$7cVp(Zdt$n{mYk~X>ErSn>_umnz$$g%yZ~i zc{NLQ!qTNnj~#PkI#QC6v10%J`Nw{qxXx*6V)B9aMPYETFk_dCXyl_gHFGcAyeTQz zKV#m!d7lKgM(JwsAJ5Fpd^lNK^7_@QPd``waw_mx8FK0M*OvQ}Ckuv0~l2w28NG-3s78ZF%?3ou1y_jVEei-1HxxDM+7q{Pu0> z``p=g?%wS+Dl0E7*kd-P~g zPl$u7*$uAy-BUikd#4wk1_T9tkJMI#8WkNXJ9>9x*!S<>ci!8+Z{N8`1r~kh6AU~~ zahi+g6qYNkuG=r4*yHy2^yzL6Uhm11Czm)eofYMc{BYJ)np0plo+y$75l0|K*oW92FKu=(Nd;`%dk9QDQa0cxF!rXB userpic, + PrivacyBadge badge, + Fn clicked); + +private: + bool eventFilter(QObject *o, QEvent *e) override; + void paintEvent(QPaintEvent *e) override; + + void updateGeometry(); + + const not_null _userpic; + const PrivacyBadge _badgeData; + const std::unique_ptr _clickable; + QRect _badge; + QImage _layer; + bool _grabbing = false; + +}; + +[[nodiscard]] PrivacyBadge LookupPrivacyBadge(Data::StoryPrivacy privacy) { + using namespace Data; + static const auto badges = base::flat_map{ + { StoryPrivacy::CloseFriends, PrivacyBadge{ + &st::storiesBadgeCloseFriends, + &st::historyPeer2UserpicBg, + &st::historyPeer2UserpicBg2, + } }, + { StoryPrivacy::Contacts, PrivacyBadge{ + &st::storiesBadgeContacts, + &st::historyPeer5UserpicBg, + &st::historyPeer5UserpicBg2, + } }, + { StoryPrivacy::SelectedContacts, PrivacyBadge{ + &st::storiesBadgeSelectedContacts, + &st::historyPeer8UserpicBg, + &st::historyPeer8UserpicBg2, + } }, + }; + if (const auto i = badges.find(privacy); i != end(badges)) { + return i->second; + } + return {}; +} + +UserpicBadge::UserpicBadge( + not_null userpic, + PrivacyBadge badge, + Fn clicked) +: RpWidget(userpic->parentWidget()) +, _userpic(userpic) +, _badgeData(badge) +, _clickable(std::make_unique(parentWidget())) { + _clickable->setClickedCallback(std::move(clicked)); + userpic->installEventFilter(this); + updateGeometry(); + setAttribute(Qt::WA_TransparentForMouseEvents); + Ui::PostponeCall(this, [=] { + _userpic->raise(); + }); + show(); +} + +bool UserpicBadge::eventFilter(QObject *o, QEvent *e) { + if (o != _userpic) { + return false; + } + const auto type = e->type(); + switch (type) { + case QEvent::Move: + case QEvent::Resize: + updateGeometry(); + return false; + case QEvent::Paint: + return !_grabbing; + } + return false; +} + +void UserpicBadge::paintEvent(QPaintEvent *e) { + const auto ratio = style::DevicePixelRatio(); + const auto layerSize = size() * ratio; + if (_layer.size() != layerSize) { + _layer = QImage(layerSize, QImage::Format_ARGB32_Premultiplied); + _layer.setDevicePixelRatio(ratio); + } + _layer.fill(Qt::transparent); + auto q = QPainter(&_layer); + + _grabbing = true; + Ui::RenderWidget(q, _userpic); + _grabbing = false; + + auto hq = PainterHighQualityEnabler(q); + auto pen = st::transparent->p; + pen.setWidthF(st::storiesBadgeOutline); + const auto half = st::storiesBadgeOutline / 2.; + auto outer = QRectF(_badge).marginsAdded({ half, half, half, half }); + auto gradient = QLinearGradient(outer.topLeft(), outer.bottomLeft()); + gradient.setStops({ + { 0., (*_badgeData.bg1)->c }, + { 1., (*_badgeData.bg2)->c }, + }); + q.setPen(pen); + q.setBrush(gradient); + q.setCompositionMode(QPainter::CompositionMode_Source); + q.drawEllipse(outer); + q.setCompositionMode(QPainter::CompositionMode_SourceOver); + _badgeData.icon->paintInCenter(q, _badge); + q.end(); + + QPainter(this).drawImage(0, 0, _layer); +} + +void UserpicBadge::updateGeometry() { + const auto width = _userpic->width() + st::storiesBadgeShift.x(); + const auto height = _userpic->height() + st::storiesBadgeShift.y(); + setGeometry(QRect(_userpic->pos(), QSize{ width, height })); + const auto inner = QRect(QPoint(), _badgeData.icon->size()); + const auto badge = inner.marginsAdded(st::storiesBadgePadding).size(); + _badge = QRect( + QPoint(width - badge.width(), height - badge.height()), + badge); + _clickable->setGeometry(_badge.translated(pos())); + update(); +} + +[[nodiscard]] std::unique_ptr MakePrivacyBadge( + not_null userpic, + Data::StoryPrivacy privacy, + Fn clicked) { + const auto badge = LookupPrivacyBadge(privacy); + if (!badge.icon) { + return nullptr; + } + return std::make_unique( + userpic, + badge, + std::move(clicked)); +} + [[nodiscard]] Timestamp ComposeTimestamp(TimeId when, TimeId now) { const auto minutes = (now - when) / 60; if (!minutes) { @@ -102,42 +253,41 @@ Header::Header(not_null controller) , _dateUpdateTimer([=] { updateDateText(); }) { } -Header::~Header() { -} +Header::~Header() = default; void Header::show(HeaderData data) { if (_data == data) { return; } - const auto nameDataChanged = !_data - || (_data->user != data.user) + const auto userChanged = !_data + || (_data->user != data.user); + const auto nameDataChanged = userChanged + || !_name || (_data->fullCount != data.fullCount) || (data.fullCount && _data->fullIndex != data.fullIndex); _data = data; - if (nameDataChanged) { + if (userChanged) { _date = nullptr; + _name = nullptr; + _userpic = nullptr; + _info = nullptr; + _privacy = nullptr; const auto parent = _controller->wrap(); - auto widget = std::make_unique(parent); + auto widget = std::make_unique(parent); const auto raw = widget.get(); - raw->setClickedCallback([=] { + _info = std::make_unique(raw); + _info->setClickedCallback([=] { _controller->uiShow()->show(PrepareShortInfoBox(_data->user)); }); - const auto userpic = Ui::CreateChild( + _userpic = std::make_unique( raw, data.user, st::storiesHeaderPhoto); - userpic->setAttribute(Qt::WA_TransparentForMouseEvents); - userpic->show(); - userpic->move( + _userpic->setAttribute(Qt::WA_TransparentForMouseEvents); + _userpic->show(); + _userpic->move( st::storiesHeaderMargin.left(), st::storiesHeaderMargin.top()); - const auto name = Ui::CreateChild( - raw, - rpl::single(ComposeName(data)), - st::storiesHeaderName); - name->setAttribute(Qt::WA_TransparentForMouseEvents); - name->setOpacity(kNameOpacity); - name->move(st::storiesHeaderNamePosition); raw->show(); _widget = std::move(widget); @@ -146,6 +296,26 @@ void Header::show(HeaderData data) { raw->setGeometry(layout.header); }, raw->lifetime()); } + if (nameDataChanged) { + _name = std::make_unique( + _widget.get(), + rpl::single(ComposeName(data)), + st::storiesHeaderName); + _name->setAttribute(Qt::WA_TransparentForMouseEvents); + _name->setOpacity(kNameOpacity); + _name->move(st::storiesHeaderNamePosition); + _name->show(); + + rpl::combine( + _name->widthValue(), + _widget->heightValue() + ) | rpl::start_with_next([=](int width, int height) { + if (_date) { + _info->setGeometry( + { 0, 0, std::max(width, _date->width()), height }); + } + }, _name->lifetime()); + } auto timestamp = ComposeDetails(data, base::unixtime::now()); _date = std::make_unique( _widget.get(), @@ -156,6 +326,16 @@ void Header::show(HeaderData data) { _date->show(); _date->move(st::storiesHeaderDatePosition); + _date->widthValue( + ) | rpl::start_with_next([=](int width) { + _info->setGeometry( + { 0, 0, std::max(width, _name->width()), _widget->height() }); + }, _name->lifetime()); + + _privacy = MakePrivacyBadge(_userpic.get(), data.privacy, [=] { + + }); + if (timestamp.changes > 0) { _dateUpdateTimer.callOnce(timestamp.changes * crl::time(1000)); } diff --git a/Telegram/SourceFiles/media/stories/media_stories_header.h b/Telegram/SourceFiles/media/stories/media_stories_header.h index 2b8f6e61f..ae584ed8c 100644 --- a/Telegram/SourceFiles/media/stories/media_stories_header.h +++ b/Telegram/SourceFiles/media/stories/media_stories_header.h @@ -17,6 +17,11 @@ enum class StoryPrivacy : uchar; namespace Ui { class RpWidget; class FlatLabel; +class IconButton; +class AbstractButton; +class UserpicButton; +template +class FadeWrap; } // namespace Ui namespace Media::Stories { @@ -51,7 +56,14 @@ private: const not_null _controller; std::unique_ptr _widget; + std::unique_ptr _info; + std::unique_ptr _userpic; + std::unique_ptr _name; std::unique_ptr _date; + std::unique_ptr _playPause; + std::unique_ptr _volumeToggle; + std::unique_ptr> _volume; + std::unique_ptr _privacy; std::optional _data; base::Timer _dateUpdateTimer; diff --git a/Telegram/SourceFiles/media/view/media_view.style b/Telegram/SourceFiles/media/view/media_view.style index ac5d9e062..097b498cb 100644 --- a/Telegram/SourceFiles/media/view/media_view.style +++ b/Telegram/SourceFiles/media/view/media_view.style @@ -844,3 +844,10 @@ storiesReportBox: ReportBox(defaultReportBox) { storiesActionToast: Toast(defaultToast) { maxWidth: 320px; } + +storiesBadgeCloseFriends: icon{{ "mediaview/mini_close_friends", historyPeerUserpicFg }}; +storiesBadgeContacts: icon{{ "mediaview/mini_contacts", historyPeerUserpicFg }}; +storiesBadgeSelectedContacts: icon{{ "mediaview/mini_selected_contacts", historyPeerUserpicFg }}; +storiesBadgePadding: margins(1px, 1px, 1px, 1px); +storiesBadgeOutline: 2px; +storiesBadgeShift: point(5px, 4px);