From 1d1fa5f98b66efcf5116d0fbae1fe0a709d098a9 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 30 Nov 2021 16:06:41 +0400 Subject: [PATCH] Improve session details box design. --- Telegram/Resources/qrc/telegram/telegram.qrc | 1 + Telegram/SourceFiles/boxes/boxes.style | 65 ----- Telegram/SourceFiles/boxes/sessions_box.cpp | 256 +++++++++++++++---- Telegram/SourceFiles/settings/settings.style | 90 +++++++ Telegram/lib_lottie | 2 +- 5 files changed, 298 insertions(+), 116 deletions(-) diff --git a/Telegram/Resources/qrc/telegram/telegram.qrc b/Telegram/Resources/qrc/telegram/telegram.qrc index 13e96d5ea6..37013d170b 100644 --- a/Telegram/Resources/qrc/telegram/telegram.qrc +++ b/Telegram/Resources/qrc/telegram/telegram.qrc @@ -74,6 +74,7 @@ ../../icons/settings/devices/device_desktop_mac.lottie ../../icons/settings/devices/device_desktop_win.lottie ../../icons/settings/devices/device_linux.lottie + ../../icons/settings/devices/device_linux_ubuntu.lottie ../../icons/settings/devices/device_phone_android.lottie ../../icons/settings/devices/device_phone_ios.lottie ../../icons/settings/devices/device_tablet_ios.lottie diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index 5dd5c1894b..55c0105230 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -279,71 +279,6 @@ membersAbout: FlatLabel(defaultFlatLabel) { style: boxLabelStyle; } -sessionsScroll: boxScroll; -sessionsHeight: 350px; -sessionsTerminateAll: SettingsButton(defaultSettingsButton) { - textFg: attentionButtonFg; - textFgOver: attentionButtonFgOver; - font: font(boxFontSize semibold); - height: 20px; - padding: margins(77px, 12px, 22px, 10px); -} -sessionsTerminateAllIcon: icon {{ "settings/devices/terminate_all", attentionButtonFg }}; -sessionsTerminateAllIconLeft: 30px; -sessionHeight: 84px; -sessionInfoTop: 21px; -sessionLocationTop: 43px; -sessionCurrentSkip: 8px; -sessionSubtitleSkip: 14px; -sessionPadding: margins(77px, 11px, 22px, 0px); -sessionNameFont: msgNameFont; -sessionNameFg: boxTextFg; -sessionWhenFont: msgDateFont; -sessionWhenFg: windowSubTextFg; -sessionInfoFont: msgFont; -sessionInfoFg: windowSubTextFg; -sessionTerminateTop: 9px; -sessionTerminateSkip: 22px; -sessionUserpicSize: 42px; -sessionUserpicPosition: point(21px, 10px); -sessionNamePadding: margins(0px, 0px, 5px, 0px); -sessionTerminate: IconButton { - width: 20px; - height: 20px; - - icon: smallCloseIcon; - iconOver: smallCloseIconOver; - iconPosition: point(5px, 5px); - - rippleAreaPosition: point(0px, 0px); - rippleAreaSize: 20px; - ripple: RippleAnimation(defaultRippleAnimation) { - color: windowBgOver; - } -} -sessionNameStyle: TextStyle(defaultTextStyle) { - font: sessionNameFont; -} -sessionWhenStyle: TextStyle(defaultTextStyle) { - font: sessionWhenFont; -} -sessionInfoStyle: TextStyle(defaultTextStyle) { - font: sessionInfoFont; -} -sessionIconWindows: icon{{ "settings/devices/device_desktop_win", historyPeerUserpicFg }}; -sessionIconMac: icon{{ "settings/devices/device_desktop_mac", historyPeerUserpicFg }}; -sessionIconUbuntu: icon{{ "settings/devices/device_linux_ubuntu", historyPeerUserpicFg }}; -sessionIconLinux: icon{{ "settings/devices/device_linux", historyPeerUserpicFg }}; -sessionIconiPhone: icon{{ "settings/devices/device_phone_ios", historyPeerUserpicFg }}; -sessionIconiPad: icon{{ "settings/devices/device_tablet_ios", historyPeerUserpicFg }}; -sessionIconAndroid: icon{{ "settings/devices/device_phone_android", historyPeerUserpicFg }}; -sessionIconWeb: icon{{ "settings/devices/device_web_other", historyPeerUserpicFg }}; -sessionIconChrome: icon{{ "settings/devices/device_web_chrome", historyPeerUserpicFg }}; -sessionIconEdge: icon{{ "settings/devices/device_web_edge", historyPeerUserpicFg }}; -sessionIconFirefox: icon{{ "settings/devices/device_web_firefox", historyPeerUserpicFg }}; -sessionIconSafari: icon{{ "settings/devices/device_web_safari", historyPeerUserpicFg }}; -sessionIconOther: icon{{ "settings/devices/device_other", historyPeerUserpicFg }}; - passcodeHeaderFont: font(19px); passcodeHeaderHeight: 80px; passcodeInput: InputField(introPhone) { diff --git a/Telegram/SourceFiles/boxes/sessions_box.cpp b/Telegram/SourceFiles/boxes/sessions_box.cpp index bab50931fc..501fdd4289 100644 --- a/Telegram/SourceFiles/boxes/sessions_box.cpp +++ b/Telegram/SourceFiles/boxes/sessions_box.cpp @@ -22,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/labels.h" #include "ui/widgets/scroll_area.h" #include "ui/wrap/slide_wrap.h" +#include "ui/wrap/padding_wrap.h" #include "ui/wrap/vertical_layout.h" #include "ui/layers/generic_box.h" #include "lottie/lottie_icon.h" @@ -93,56 +94,6 @@ void RenameBox(not_null box) { box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); } -void SessionInfoBox( - not_null box, - const EntryData &data, - Fn terminate) { - box->setTitle(rpl::single(data.name)); - box->setWidth(st::boxWidth); - - const auto skips = style::margins(0, 0, 0, st::settingsSectionSkip); - const auto date = base::unixtime::parse(data.activeTime); - box->addRow( - object_ptr( - box, - rpl::single(langDateTimeFull(date)), - st::boxDividerLabel), - st::boxRowPadding + skips); - - const auto add = [&](rpl::producer label, QString value) { - if (value.isEmpty()) { - return; - } - Settings::AddSubsectionTitle( - box->verticalLayout(), - std::move(label)); - box->addRow( - object_ptr( - box, - rpl::single(value), - st::boxDividerLabel), - st::boxRowPadding + skips); - }; - add(tr::lng_sessions_application(), data.info); - add(tr::lng_sessions_system(), data.system); - add(tr::lng_sessions_ip(), data.ip); - add(tr::lng_sessions_location(), data.location); - if (!data.location.isEmpty()) { - Settings::AddDividerText( - box->verticalLayout(), - tr::lng_sessions_location_about()); - } - - box->addButton(tr::lng_about_done(), [=] { box->closeBox(); }); - box->addLeftButton(tr::lng_sessions_terminate(), [=, hash = data.hash] { - const auto weak = Ui::MakeWeak(box.get()); - terminate(hash); - if (weak) { - box->closeBox(); - } - }, st::attentionBoxButton); -} - [[nodiscard]] QString LocationAndDate(const EntryData &entry) { return (entry.location.isEmpty() ? entry.ip : entry.location) + (entry.hash @@ -265,6 +216,44 @@ void SessionInfoBox( Unexpected("Type in IconForType."); } +[[nodiscard]] const style::icon *IconBigForType(Type type) { + switch (type) { + case Type::Web: return &st::sessionBigIconWeb; + case Type::Other: return &st::sessionBigIconOther; + } + return nullptr; +} + +[[nodiscard]] std::unique_ptr LottieForType(Type type) { + if (IconBigForType(type)) { + return nullptr; + } + const auto path = [&] { + switch (type) { + case Type::Windows: return "device_desktop_win"; + case Type::Mac: return "device_desktop_mac"; + case Type::Ubuntu: return "device_linux_ubuntu"; + case Type::Linux: return "device_linux"; + case Type::iPhone: return "device_phone_ios"; + case Type::iPad: return "device_tablet_ios"; + case Type::Android: return "device_phone_android"; + case Type::Chrome: return "device_web_chrome"; + case Type::Edge: return "device_web_edge"; + case Type::Firefox: return "device_web_firefox"; + case Type::Safari: return "device_web_safari"; + } + Unexpected("Type in LottieForType."); + }(); + const auto size = st::sessionBigLottieSize; + static const auto kWhite = style::owned_color(Qt::white); + return std::make_unique(Lottie::IconDescriptor{ + .path = u":/icons/settings/devices/"_q + path + u".lottie"_q, + .color = kWhite.color(), + .sizeOverride = QSize(size, size), + .frame = 1, + }); +} + [[nodiscard]] QImage GenerateUserpic(Type type) { const auto size = st::sessionUserpicSize; const auto full = size * style::DevicePixelRatio(); @@ -285,6 +274,172 @@ void SessionInfoBox( return result; } +[[nodiscard]] not_null GenerateUserpicBig( + not_null parent, + rpl::producer<> shown, + Type type) { + const auto size = st::sessionBigUserpicSize; + const auto full = size * style::DevicePixelRatio(); + const auto rect = QRect(0, 0, size, size); + + const auto result = Ui::CreateChild(parent.get()); + result->resize(rect.size()); + struct State { + QImage background; + std::unique_ptr lottie; + QImage lottieFrame; + QImage colorizedFrame; + }; + const auto state = result->lifetime().make_state(); + state->background = QImage( + full, + full, + QImage::Format_ARGB32_Premultiplied); + state->background.fill(Qt::transparent); + state->background.setDevicePixelRatio(style::DevicePixelRatio()); + state->colorizedFrame = state->lottieFrame = state->background; + + auto p = QPainter(&state->background); + auto hq = PainterHighQualityEnabler(p); + p.setBrush(ColorForType(type)); + p.setPen(Qt::NoPen); + p.drawEllipse(rect); + if (const auto icon = IconBigForType(type)) { + icon->paintInCenter(p, rect); + } + p.end(); + + if ((state->lottie = LottieForType(type))) { + std::move( + shown + ) | rpl::start_with_next([=] { + state->lottie->animate( + [=] { result->update(); }, + 0, + state->lottie->framesCount()); + }, result->lifetime()); + } + + result->paintRequest( + ) | rpl::start_with_next([=] { + auto p = QPainter(result); + p.drawImage(QPoint(0, 0), state->background); + if (state->lottie) { + state->lottieFrame.fill(Qt::black); + auto q = QPainter(&state->lottieFrame); + state->lottie->paintInCenter(q, result->rect()); + q.end(); + style::colorizeImage( + state->lottieFrame, + st::historyPeerUserpicFg->c, + &state->colorizedFrame); + p.drawImage(QPoint(0, 0), state->colorizedFrame); + + } + }, result->lifetime()); + + return result; +} + +void SessionInfoBox( + not_null box, + const EntryData &data, + Fn terminate) { + box->setWidth(st::boxWideWidth); + + const auto shown = box->lifetime().make_state>(); + box->setShowFinishedCallback([=] { + shown->fire({}); + }); + + const auto userpicWrap = box->addRow( + object_ptr(box, st::sessionBigUserpicSize), + st::sessionBigCoverPadding); + const auto big = GenerateUserpicBig( + userpicWrap, + shown->events(), + TypeFromEntry(data)); + userpicWrap->sizeValue( + ) | rpl::start_with_next([=](QSize size) { + big->move((size.width() - big->width()) / 2, 0); + }, userpicWrap->lifetime()); + + const auto nameWrap = box->addRow( + object_ptr( + box, + st::sessionBigName.maxHeight)); + const auto name = Ui::CreateChild( + nameWrap, + rpl::single(data.name), + st::sessionBigName); + nameWrap->widthValue( + ) | rpl::start_with_next([=](int width) { + name->resizeToWidth(width); + name->move((width - name->width()) / 2, 0); + }, name->lifetime()); + + const auto dateWrap = box->addRow( + object_ptr( + box, + st::sessionDateLabel.style.font->height), + style::margins(0, 0, 0, st::sessionDateSkip)); + const auto date = Ui::CreateChild( + dateWrap, + rpl::single( + langDateTimeFull(base::unixtime::parse(data.activeTime))), + st::sessionDateLabel); + rpl::combine( + dateWrap->widthValue(), + date->widthValue() + ) | rpl::start_with_next([=](int outer, int inner) { + date->move((outer - inner) / 2, 0); + }, date->lifetime()); + + using namespace Settings; + const auto container = box->verticalLayout(); + AddDivider(container); + AddSkip(container, st::sessionSubtitleSkip); + AddSubsectionTitle(container, tr::lng_sessions_info()); + + const auto add = [&](rpl::producer label, QString value) { + if (value.isEmpty()) { + return; + } + container->add( + object_ptr( + container, + rpl::single(value), + st::boxLabel), + st::boxRowPadding + st::sessionValuePadding); + container->add( + object_ptr( + container, + std::move(label), + st::sessionValueLabel), + (st::boxRowPadding + + style::margins{ 0, 0, 0, st::sessionValueSkip })); + }; + add(tr::lng_sessions_application(), data.info); + add(tr::lng_sessions_system(), data.system); + add(tr::lng_sessions_ip(), data.ip); + add(tr::lng_sessions_location(), data.location); + AddSkip(container, st::sessionValueSkip); + if (!data.location.isEmpty()) { + AddDividerText(container, tr::lng_sessions_location_about()); + } + + box->addButton(tr::lng_about_done(), [=] { box->closeBox(); }); + if (const auto hash = data.hash) { + box->addLeftButton(tr::lng_sessions_terminate(), [=] { + const auto weak = Ui::MakeWeak(box.get()); + terminate(hash); + if (weak) { + box->closeBox(); + } + }, st::attentionBoxButton); + } +} + } // namespace class SessionsContent : public Ui::RpWidget { @@ -737,6 +892,7 @@ rpl::producer SessionsContent::Inner::terminateOne() const { rpl::producer SessionsContent::Inner::showRequests() const { return rpl::merge( + _current->showRequests(), _incomplete->showRequests(), _list->showRequests()); } diff --git a/Telegram/SourceFiles/settings/settings.style b/Telegram/SourceFiles/settings/settings.style index e06edc15c9..d56433435e 100644 --- a/Telegram/SourceFiles/settings/settings.style +++ b/Telegram/SourceFiles/settings/settings.style @@ -237,3 +237,93 @@ settingsDeviceName: InputField(defaultInputField) { dictionariesSectionButton: SettingsButton(settingsUpdateToggle) { font: font(14px semibold); } + +sessionsScroll: boxScroll; +sessionsHeight: 350px; +sessionsTerminateAll: SettingsButton(defaultSettingsButton) { + textFg: attentionButtonFg; + textFgOver: attentionButtonFgOver; + font: font(boxFontSize semibold); + height: 20px; + padding: margins(77px, 12px, 22px, 10px); +} +sessionsTerminateAllIcon: icon {{ "settings/devices/terminate_all", attentionButtonFg }}; +sessionsTerminateAllIconLeft: 30px; +sessionHeight: 84px; +sessionInfoTop: 21px; +sessionLocationTop: 43px; +sessionCurrentSkip: 8px; +sessionSubtitleSkip: 14px; +sessionPadding: margins(77px, 11px, 22px, 0px); +sessionNameFont: msgNameFont; +sessionNameFg: boxTextFg; +sessionWhenFont: msgDateFont; +sessionWhenFg: windowSubTextFg; +sessionInfoFont: msgFont; +sessionInfoFg: windowSubTextFg; +sessionTerminateTop: 9px; +sessionTerminateSkip: 22px; +sessionUserpicSize: 42px; +sessionUserpicPosition: point(21px, 10px); +sessionNamePadding: margins(0px, 0px, 5px, 0px); +sessionTerminate: IconButton { + width: 20px; + height: 20px; + + icon: smallCloseIcon; + iconOver: smallCloseIconOver; + iconPosition: point(5px, 5px); + + rippleAreaPosition: point(0px, 0px); + rippleAreaSize: 20px; + ripple: RippleAnimation(defaultRippleAnimation) { + color: windowBgOver; + } +} +sessionNameStyle: TextStyle(defaultTextStyle) { + font: sessionNameFont; +} +sessionWhenStyle: TextStyle(defaultTextStyle) { + font: sessionWhenFont; +} +sessionInfoStyle: TextStyle(defaultTextStyle) { + font: sessionInfoFont; +} +sessionIconWindows: icon{{ "settings/devices/device_desktop_win", historyPeerUserpicFg }}; +sessionIconMac: icon{{ "settings/devices/device_desktop_mac", historyPeerUserpicFg }}; +sessionIconUbuntu: icon{{ "settings/devices/device_linux_ubuntu", historyPeerUserpicFg }}; +sessionIconLinux: icon{{ "settings/devices/device_linux", historyPeerUserpicFg }}; +sessionIconiPhone: icon{{ "settings/devices/device_phone_ios", historyPeerUserpicFg }}; +sessionIconiPad: icon{{ "settings/devices/device_tablet_ios", historyPeerUserpicFg }}; +sessionIconAndroid: icon{{ "settings/devices/device_phone_android", historyPeerUserpicFg }}; +sessionIconWeb: icon{{ "settings/devices/device_web_other", historyPeerUserpicFg }}; +sessionIconChrome: icon{{ "settings/devices/device_web_chrome", historyPeerUserpicFg }}; +sessionIconEdge: icon{{ "settings/devices/device_web_edge", historyPeerUserpicFg }}; +sessionIconFirefox: icon{{ "settings/devices/device_web_firefox", historyPeerUserpicFg }}; +sessionIconSafari: icon{{ "settings/devices/device_web_safari", historyPeerUserpicFg }}; +sessionIconOther: icon{{ "settings/devices/device_other", historyPeerUserpicFg }}; +sessionBigUserpicSize: 70px; +sessionBigLottieSize: 52px; +sessionBigIconOther: icon{{ "settings/devices/device_other_large", historyPeerUserpicFg }}; +sessionBigIconWeb: icon{{ "settings/devices/device_web_other_large", historyPeerUserpicFg }}; +sessionBigCoverPadding: margins(0px, 18px, 0px, 7px); +sessionBigName: FlatLabel(defaultFlatLabel) { + textFg: boxTitleFg; + maxHeight: 29px; + style: TextStyle(defaultTextStyle) { + font: font(20px semibold); + linkFont: font(20px semibold); + linkFontOver: font(20px semibold underline); + } + align: align(top); +} +sessionDateLabel: FlatLabel(defaultFlatLabel) { + textFg: windowSubTextFg; + align: align(top); +} +sessionDateSkip: 19px; +sessionValuePadding: margins(0px, 5px, 0px, 2px); +sessionValueLabel: FlatLabel(defaultFlatLabel) { + textFg: windowSubTextFg; +} +sessionValueSkip: 8px; diff --git a/Telegram/lib_lottie b/Telegram/lib_lottie index fd4d8576ad..ad7fce76f3 160000 --- a/Telegram/lib_lottie +++ b/Telegram/lib_lottie @@ -1 +1 @@ -Subproject commit fd4d8576adb611780d1dc7dcce4fe0115eabc9c0 +Subproject commit ad7fce76f3b403471a296c928bae67cd36b8b2cf