diff --git a/Telegram/Resources/colors.palette b/Telegram/Resources/colors.palette index d19343a1ab..1467b92fe5 100644 --- a/Telegram/Resources/colors.palette +++ b/Telegram/Resources/colors.palette @@ -194,6 +194,7 @@ dialogsUnreadBg: windowBgActive; // chat list unread badge background for not mu dialogsUnreadBgMuted: #bbbbbb; // chat list unread badge background for muted chat dialogsUnreadFg: windowFgActive; // chat list unread badge text dialogsArchiveFg: #525252 | dialogsNameFg; // chat list archive name text +dialogsOnlineBadgeFg: #4dc920 | dialogsUnreadBg; // chat list online status dialogsBgOver: windowBgOver; // chat list background with mouse over dialogsNameFgOver: windowBoldFgOver; // chat list name text with mouse over @@ -225,6 +226,7 @@ dialogsSentIconFgActive: dialogsTextFgActive; // chat list sent message tick / d dialogsUnreadBgActive: dialogsTextFgActive; // chat list unread badge background for not muted chat for current (active) chat dialogsUnreadBgMutedActive: dialogsDraftFgActive; // chat list unread badge background for muted chat for current (active) chat dialogsUnreadFgActive: dialogsBgActive; // chat list unread badge text for current (active) chat +dialogsOnlineBadgeFgActive: #ffffff; // chat list online status for current (active) chat dialogsRippleBg: windowBgRipple; // chat list background ripple effect dialogsRippleBgActive: activeButtonBgRipple; // chat list background ripple effect for current (active) chat diff --git a/Telegram/Resources/day-blue.tdesktop-theme b/Telegram/Resources/day-blue.tdesktop-theme index 4934875a60..d24019eb17 100644 Binary files a/Telegram/Resources/day-blue.tdesktop-theme and b/Telegram/Resources/day-blue.tdesktop-theme differ diff --git a/Telegram/Resources/night-green.tdesktop-theme b/Telegram/Resources/night-green.tdesktop-theme index 3b25f9d55e..c7caf91ba5 100644 Binary files a/Telegram/Resources/night-green.tdesktop-theme and b/Telegram/Resources/night-green.tdesktop-theme differ diff --git a/Telegram/Resources/night.tdesktop-theme b/Telegram/Resources/night.tdesktop-theme index 87865eccfc..37e401c7c5 100644 Binary files a/Telegram/Resources/night.tdesktop-theme and b/Telegram/Resources/night.tdesktop-theme differ diff --git a/Telegram/SourceFiles/dialogs/dialogs.style b/Telegram/SourceFiles/dialogs/dialogs.style index a8ff3550e3..e679fb1aa9 100644 --- a/Telegram/SourceFiles/dialogs/dialogs.style +++ b/Telegram/SourceFiles/dialogs/dialogs.style @@ -34,6 +34,10 @@ dialogsPhotoSize: 46px; dialogsPhotoPadding: 12px; dialogsPadding: point(10px, 8px); +dialogsOnlineBadgeSizePadding: 4px; +dialogsOnlineBadgeSize: 8px; +dialogsOnlineBadgeRightSkip: 2px; + dialogsImportantBarHeight: 37px; dialogsSkip: 8px; diff --git a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp index 069820ae67..5b09aa80af 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp @@ -177,6 +177,34 @@ InnerWidget::InnerWidget( UpdateRowSection::Default | UpdateRowSection::Filtered); }, lifetime()); + const auto handleUserOnline = [=](const Notify::PeerUpdate &peerUpdate) { + if (peerUpdate.peer->isSelf()) { + return; + } + const auto circleSize = st::dialogsOnlineBadgeSize + + st::dialogsOnlineBadgeSizePadding; + const auto updateRect = QRect( + st::dialogsPadding.x() + + st::dialogsPhotoSize + - st::dialogsOnlineBadgeRightSkip + - circleSize, + st::dialogsPadding.y() + + st::dialogsPhotoSize + - circleSize, + circleSize, + circleSize); + updateDialogRow( + RowDescriptor( + session().data().history(peerUpdate.peer->id), + FullMsgId()), + updateRect, + UpdateRowSection::Default | UpdateRowSection::Filtered); + }; + + subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler( + Notify::PeerUpdate::Flag::UserOnlineChanged, + handleUserOnline)); + session().data().chatsListChanges( ) | rpl::filter([=](Data::Folder *folder) { return (folder == _openedFolder); diff --git a/Telegram/SourceFiles/dialogs/dialogs_layout.cpp b/Telegram/SourceFiles/dialogs/dialogs_layout.cpp index 910190f94c..e637f8c508 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_layout.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_layout.cpp @@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_session.h" #include "dialogs/dialogs_list.h" #include "styles/style_dialogs.h" +#include "styles/style_window.h" #include "storage/localstorage.h" #include "ui/empty_userpic.h" #include "ui/text_options.h" @@ -23,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_channel.h" #include "data/data_user.h" #include "data/data_folder.h" +#include "data/data_peer_values.h" namespace Dialogs { namespace Layout { @@ -202,6 +204,7 @@ enum class Flag { Selected = 0x02, SearchResult = 0x04, SavedMessages = 0x08, + UserOnline = 0x10, //FeedSearchResult = 0x10, // #feed }; inline constexpr bool is_flag_type(Flag) { return true; } @@ -249,12 +252,57 @@ void paintRow( fullWidth, st::dialogsPhotoSize); } else if (from) { - from->paintUserpicLeft( - p, - st::dialogsPadding.x(), - st::dialogsPadding.y(), - fullWidth, - st::dialogsPhotoSize); + if (flags & Flag::UserOnline) { + auto frame = QImage( + st::dialogsPhotoSize * cRetinaFactor(), + st::dialogsPhotoSize * cRetinaFactor(), + QImage::Format_ARGB32_Premultiplied); + frame.setDevicePixelRatio(cRetinaFactor()); + frame.fill(Qt::transparent); + { + const auto size = st::dialogsOnlineBadgeSize; + const auto paddingSize = st::dialogsOnlineBadgeSizePadding; + const auto circleSize = size + paddingSize; + const auto offset = size + paddingSize / 2; + const auto x = st::dialogsPadding.x() + st::dialogsPhotoSize - st::dialogsOnlineBadgeRightSkip - size; + const auto y = st::dialogsPadding.y() + st::dialogsPhotoSize - size; + + Painter q(&frame); + PainterHighQualityEnabler hq(q); + from->paintUserpicLeft( + q, + 0, + 0, + fullWidth, + st::dialogsPhotoSize); + q.setPen(Qt::NoPen); + q.setBrush(Qt::transparent); + q.setCompositionMode(QPainter::CompositionMode_SourceOut); + q.drawEllipse( + x - circleSize, + y - circleSize, + circleSize, + circleSize); + + q.setBrush(active + ? st::dialogsOnlineBadgeFgActive + : st::dialogsOnlineBadgeFg); + q.setCompositionMode(QPainter::CompositionMode_Source); + q.drawEllipse( + x - offset, + y - offset, + size, + size); + } + p.drawImage(st::dialogsPadding, frame); + } else { + from->paintUserpicLeft( + p, + st::dialogsPadding.x(), + st::dialogsPadding.y(), + fullWidth, + st::dialogsPhotoSize); + } } else if (hiddenSenderInfo) { hiddenSenderInfo->userpic.paint( p, @@ -648,8 +696,14 @@ void RowPainter::paint( ? history->peer->migrateTo() : history->peer.get()) : nullptr; + const auto showUserOnline = peer + && peer->isUser() + && Data::OnlineTextActive(peer->asUser(), unixtime()) + && !(fullWidth < st::columnMinimalWidthLeft + && (displayUnreadCounter || displayUnreadMark)); const auto flags = (active ? Flag::Active : Flag(0)) | (selected ? Flag::Selected : Flag(0)) + | (showUserOnline ? Flag::UserOnline : Flag(0)) | (peer && peer->isSelf() ? Flag::SavedMessages : Flag(0)); const auto paintItemCallback = [&](int nameleft, int namewidth) { const auto texttop = st::dialogsPadding.y()