Update story mention layout, add outline.

Also use uint32 for bool-bitfields, otherwise:

int a : 1 = 0;
...
const auto test = true;
const auto b = test ? 1 : 0;
if (a != b) {
    a = b;
    ...
}
Assert(a == b); // Violation, because a == -1, not 1 (after a = b).
This commit is contained in:
John Preston 2023-07-05 11:55:16 +04:00
parent d7d8847c1d
commit a0ffa15885
15 changed files with 151 additions and 52 deletions

View File

@ -163,7 +163,7 @@ StoriesRow::StoriesRow(
void StoriesRow::applySegments(const Dialogs::Stories::Element &element) {
Expects(element.unreadCount <= element.count);
_count = std::max(element.count, 1);
_count = int(std::max(element.count, 1U));
_unreadCount = element.unreadCount;
refreshSegments();
}

View File

@ -75,9 +75,9 @@ StoriesSourceInfo StoriesSource::info() const {
return {
.id = user->id,
.last = ids.empty() ? 0 : ids.back().date,
.count = std::min(int(ids.size()), kMaxSegmentsCount),
.unreadCount = std::min(unreadCount(), kMaxSegmentsCount),
.premium = user->isPremium() ? 1 : 0,
.count = uint32(std::min(int(ids.size()), kMaxSegmentsCount)),
.unreadCount = uint32(std::min(unreadCount(), kMaxSegmentsCount)),
.premium = user->isPremium() ? 1U : 0U,
};
}
@ -913,9 +913,25 @@ void Stories::markAsRead(FullStoryId id, bool viewed) {
bool Stories::bumpReadTill(PeerId peerId, StoryId maxReadTill) {
auto &till = _readTill[peerId];
auto refreshItems = std::vector<StoryId>();
const auto guard = gsl::finally([&] {
for (const auto id : refreshItems) {
_owner->refreshStoryItemViews({ peerId, id });
}
});
if (till < maxReadTill) {
const auto from = till;
till = maxReadTill;
updateUserStoriesState(_owner->peer(peerId));
const auto i = _stories.find(peerId);
if (i != end(_stories)) {
refreshItems = ranges::make_subrange(
i->second.lower_bound(from + 1),
i->second.lower_bound(till + 1)
) | ranges::views::transform([](const auto &pair) {
return pair.first;
}) | ranges::to_vector;
}
}
const auto i = _all.find(peerId);
if (i == end(_all) || i->second.readTill >= maxReadTill) {
@ -1443,21 +1459,40 @@ std::optional<Stories::PeerSourceState> Stories::peerSourceState(
(i != end(_readTill)) ? i->second : 0),
};
}
if (!_readTillsRequestId) {
const auto api = &_owner->session().api();
_readTillsRequestId = api->request(MTPstories_GetAllReadUserStories(
)).done([=](const MTPUpdates &result) {
_readTillReceived = true;
api->applyUpdates(result);
for (auto &[peer, maxId] : base::take(_pendingUserStateMaxId)) {
updateUserStoriesState(peer);
}
}).send();
}
requestReadTills();
_pendingUserStateMaxId[peer] = storyMaxId;
return std::nullopt;
}
void Stories::requestReadTills() {
if (_readTillReceived || _readTillsRequestId) {
return;
}
const auto api = &_owner->session().api();
_readTillsRequestId = api->request(MTPstories_GetAllReadUserStories(
)).done([=](const MTPUpdates &result) {
_readTillReceived = true;
api->applyUpdates(result);
for (auto &[peer, maxId] : base::take(_pendingUserStateMaxId)) {
updateUserStoriesState(peer);
}
for (const auto &storyId : base::take(_pendingReadTillItems)) {
_owner->refreshStoryItemViews(storyId);
}
}).send();
}
bool Stories::isUnread(not_null<Story*> story) {
const auto till = _readTill.find(story->peer()->id);
if (till == end(_readTill) && !_readTillReceived) {
requestReadTills();
_pendingReadTillItems.emplace(story->fullId());
return false;
}
const auto readTill = (till != end(_readTill)) ? till->second : 0;
return (story->id() > readTill);
}
void Stories::updateUserStoriesState(not_null<PeerData*> peer) {
const auto till = _readTill.find(peer->id);
const auto readTill = (till != end(_readTill)) ? till->second : 0;

View File

@ -41,9 +41,9 @@ struct StoriesIds {
struct StoriesSourceInfo {
PeerId id = 0;
TimeId last = 0;
int count : 15 = 0;
int unreadCount : 15 = 0;
int premium : 1 = 0;
uint32 count : 15 = 0;
uint32 unreadCount : 15 = 0;
uint32 premium : 1 = 0;
friend inline bool operator==(
StoriesSourceInfo,
@ -207,6 +207,7 @@ public:
[[nodiscard]] std::optional<PeerSourceState> peerSourceState(
not_null<PeerData*> peer,
StoryId storyMaxId);
[[nodiscard]] bool isUnread(not_null<Story*> story);
private:
struct Saved {
@ -241,6 +242,7 @@ private:
void savedStateUpdated(not_null<Story*> story);
void sort(StorySourcesList list);
bool bumpReadTill(PeerId peerId, StoryId maxReadTill);
void requestReadTills();
[[nodiscard]] std::shared_ptr<HistoryItem> lookupItem(
not_null<Story*> story);
@ -329,6 +331,7 @@ private:
int _preloadingMainSourcesCounter = 0;
base::flat_map<PeerId, StoryId> _readTill;
base::flat_set<FullStoryId> _pendingReadTillItems;
base::flat_map<not_null<PeerData*>, StoryId> _pendingUserStateMaxId;
mtpRequestId _readTillsRequestId = 0;
bool _readTillReceived = false;

View File

@ -367,6 +367,9 @@ void Row::PaintCornerBadgeFrame(
const auto st = context.st;
const auto storiesUnreadBrush = [&] {
if (context.active) {
return st::dialogsUnreadBgActive->b;
}
const auto left = st->padding.left();
const auto top = st->padding.top();
auto gradient = QLinearGradient(

View File

@ -170,10 +170,10 @@ private:
QImage frame;
QImage cacheTTL;
int frameIndex = -1;
int paletteVersion : 24 = 0;
int storiesShown : 1 = 0;
int storiesUnread : 1 = 0;
int active : 1 = 0;
uint32 paletteVersion : 24 = 0;
uint32 storiesShown : 1 = 0;
uint32 storiesUnread : 1 = 0;
uint32 active : 1 = 0;
};
void setCornerBadgeShown(
@ -192,9 +192,9 @@ private:
mutable std::unique_ptr<CornerBadgeUserpic> _cornerBadgeUserpic;
int _top = 0;
int _height = 0;
int _index : 30 = 0;
int _cornerBadgeShown : 1 = 0;
int _topicJumpRipple : 1 = 0;
uint32 _index : 30 = 0;
uint32 _cornerBadgeShown : 1 = 0;
uint32 _topicJumpRipple : 1 = 0;
};

View File

@ -353,7 +353,7 @@ Content State::next() {
.thumbnail = std::move(userpic),
.count = info.count,
.unreadCount = info.unreadCount,
.skipSmall = user->isSelf() ? 1 : 0,
.skipSmall = user->isSelf() ? 1U : 0U,
});
}
return result;
@ -424,8 +424,8 @@ rpl::producer<Content> LastForPeer(not_null<PeerData*> peer) {
result.elements.push_back({
.id = uint64(id),
.thumbnail = MakeStoryThumbnail(*maybe),
.count = 1,
.unreadCount = (id > readTill) ? 1 : 0,
.count = 1U,
.unreadCount = (id > readTill) ? 1U : 0U,
});
}
} else if (maybe.error() == Data::NoStory::Unknown) {

View File

@ -36,9 +36,9 @@ struct Element {
uint64 id = 0;
QString name;
std::shared_ptr<Thumbnail> thumbnail;
int count : 15 = 0;
int unreadCount : 15 = 0;
int skipSmall : 1 = 0;
uint32 count : 15 = 0;
uint32 unreadCount : 15 = 0;
uint32 skipSmall : 1 = 0;
friend inline bool operator==(
const Element &a,

View File

@ -164,7 +164,7 @@ void Photo::unloadHeavyPart() {
QSize Photo::countOptimalSize() {
if (_serviceWidth > 0) {
return { _serviceWidth, _serviceWidth };
return { int(_serviceWidth), int(_serviceWidth) };
}
if (_parent->media() != this) {
@ -209,7 +209,7 @@ QSize Photo::countOptimalSize() {
QSize Photo::countCurrentSize(int newWidth) {
if (_serviceWidth) {
return { _serviceWidth, _serviceWidth };
return { int(_serviceWidth), int(_serviceWidth) };
}
const auto thumbMaxWidth = qMin(newWidth, st::maxMediaSize);
const auto minWidth = std::clamp(

View File

@ -167,10 +167,10 @@ private:
const std::unique_ptr<MediaSpoiler> _spoiler;
mutable QImage _imageCache;
mutable std::optional<Ui::BubbleRounding> _imageCacheRounding;
int _serviceWidth : 29 = 0;
mutable int _imageCacheForum : 1 = 0;
mutable int _imageCacheBlurred : 1 = 0;
mutable int _story : 1 = 0;
uint32 _serviceWidth : 29 = 0;
mutable uint32 _imageCacheForum : 1 = 0;
mutable uint32 _imageCacheBlurred : 1 = 0;
mutable uint32 _story : 1 = 0;
};

View File

@ -81,8 +81,7 @@ ServiceBox::ServiceBox(
+ _subtitle.countHeight(_maxWidth)
+ (_button.empty()
? 0
: (st::msgServiceGiftBoxButtonMargins.top()
+ _button.size.height()))
: (_content->buttonSkip() + _button.size.height()))
+ st::msgServiceGiftBoxButtonMargins.bottom()))
, _innerSize(_size - QSize(0, st::msgServiceGiftBoxTopSkip)) {
}
@ -165,6 +164,11 @@ TextState ServiceBox::textState(QPoint point, StateRequest request) const {
if (rect.contains(point)) {
result.link = _button.link;
_button.lastPoint = point - rect.topLeft();
} else if (contentRect().contains(point)) {
if (!_contentLink) {
_contentLink = _content->createViewLink();
}
result.link = _contentLink;
}
}
return result;

View File

@ -23,6 +23,9 @@ public:
[[nodiscard]] virtual QSize size() = 0;
[[nodiscard]] virtual QString title() = 0;
[[nodiscard]] virtual TextWithEntities subtitle() = 0;
[[nodiscard]] virtual int buttonSkip() {
return top();
}
[[nodiscard]] virtual QString button() = 0;
virtual void draw(
Painter &p,
@ -84,6 +87,7 @@ private:
const not_null<Element*> _parent;
const std::unique_ptr<ServiceBoxContent> _content;
mutable ClickHandlerPtr _contentLink;
struct Button {
void drawBg(QPainter &p) const;

View File

@ -27,6 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "main/main_session.h"
#include "window/window_session_controller.h"
#include "ui/boxes/confirm_box.h"
#include "ui/chat/chat_style.h"
#include "ui/text/text_utilities.h"
#include "ui/toast/toast.h"
#include "ui/painter.h"
@ -39,13 +40,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace HistoryView {
namespace {
constexpr auto kReadOutlineAlpha = 0.5;
} // namespace
StoryMention::StoryMention(
not_null<Element*> parent,
not_null<Data::Story*> story)
: _parent(parent)
, _story(story) {
, _story(story)
, _unread(story->owner().stories().isUnread(story) ? 1 : 0) {
}
StoryMention::~StoryMention() = default;
@ -62,6 +66,10 @@ QString StoryMention::title() {
return QString();
}
int StoryMention::buttonSkip() {
return st::storyMentionButtonSkip;
}
QString StoryMention::button() {
return tr::lng_action_story_mention_button(tr::now);
}
@ -86,7 +94,7 @@ void StoryMention::draw(
Painter &p,
const PaintContext &context,
const QRect &geometry) {
const auto showStory = !_story->forbidsForward();
const auto showStory = _story->forbidsForward() ? 0 : 1;
if (!_thumbnail || _thumbnailFromStory != showStory) {
using namespace Dialogs::Stories;
const auto item = _parent->data();
@ -97,18 +105,49 @@ void StoryMention::draw(
? history->session().user()
: history->peer);
_thumbnailFromStory = showStory;
_subscribed = false;
_subscribed = 0;
}
if (!_subscribed) {
_thumbnail->subscribeToUpdates([=] {
_parent->data()->history()->owner().requestViewRepaint(_parent);
});
_subscribed = true;
_subscribed = 1;
}
const auto padding = (geometry.width() - st::storyMentionSize) / 2;
const auto size = geometry.width() - 2 * padding;
p.drawImage(
geometry.topLeft(),
_thumbnail->image(st::msgServicePhotoWidth));
geometry.topLeft() + QPoint(padding, padding),
_thumbnail->image(size));
const auto thumbnail = geometry.marginsRemoved(
QMargins(padding, padding, padding, padding));
const auto added = 0.5 * (_unread
? st::storyMentionUnreadSkipTwice
: st::storyMentionReadSkipTwice);
const auto outline = thumbnail.marginsAdded(
QMargins(added, added, added, added));
if (_unread && _paletteVersion != style::PaletteVersion()) {
_paletteVersion = style::PaletteVersion();
auto gradient = QLinearGradient(
outline.topRight(),
outline.bottomLeft());
gradient.setStops({
{ 0., st::groupCallLive1->c },
{ 1., st::groupCallMuted1->c },
});
_unreadBrush = QBrush(gradient);
}
auto readColor = context.st->msgServiceFg()->c;
readColor.setAlphaF(std::min(readColor.alphaF(), kReadOutlineAlpha));
p.setPen(QPen(
_unread ? _unreadBrush : QBrush(readColor),
0.5 * (_unread
? st::storyMentionUnreadStrokeTwice
: st::storyMentionReadStrokeTwice)));
p.setBrush(Qt::NoBrush);
auto hq = PainterHighQualityEnabler(p);
p.drawEllipse(outline);
}
void StoryMention::stickerClearLoopPlayed() {
@ -121,12 +160,12 @@ std::unique_ptr<StickerPlayer> StoryMention::stickerTakePlayer(
}
bool StoryMention::hasHeavyPart() {
return _subscribed;
return _subscribed != 0;
}
void StoryMention::unloadHeavyPart() {
if (_subscribed) {
_subscribed = false;
_subscribed = 0;
_thumbnail->subscribeToUpdates(nullptr);
}
}

View File

@ -32,6 +32,7 @@ public:
QSize size() override;
QString title() override;
TextWithEntities subtitle() override;
int buttonSkip() override;
QString button() override;
void draw(
Painter &p,
@ -57,8 +58,11 @@ private:
const not_null<Element*> _parent;
const not_null<Data::Story*> _story;
std::shared_ptr<Thumbnail> _thumbnail;
bool _thumbnailFromStory = false;
bool _subscribed = false;
QBrush _unreadBrush;
uint32 _paletteVersion : 29 = 0;
uint32 _thumbnailFromStory : 1 = 0;
uint32 _subscribed : 1 = 0;
uint32 _unread : 1 = 0;
};

View File

@ -841,7 +841,7 @@ searchInChatPeerList: PeerList(defaultPeerList) {
}
msgServiceGiftBoxSize: size(206px, 231px); // Plus msgServiceGiftBoxTopSkip.
msgServiceGiftBoxRadius: 12px;
msgServiceGiftBoxRadius: 20px;
msgServiceGiftBoxTopSkip: 4px;
msgServiceGiftBoxButtonHeight: 32px;
msgServiceGiftBoxButtonPadding: margins(2px, 0px, 2px, 0px);
@ -915,3 +915,10 @@ backgroundSwitchToLight: IconButton(backgroundSwitchToDark) {
icon: icon {{ "menu/header_mode_day", boxTitleCloseFg }};
iconOver: icon {{ "menu/header_mode_day", boxTitleCloseFgOver }};
}
storyMentionSize: 80px;
storyMentionUnreadSkipTwice: 8px;
storyMentionUnreadStrokeTwice: 6px;
storyMentionReadSkipTwice: 7px;
storyMentionReadStrokeTwice: 3px;
storyMentionButtonSkip: 5px;

View File

@ -25,8 +25,8 @@ struct PeerUserpicView {
QImage cached;
std::shared_ptr<QImage> cloud;
base::weak_ptr<const EmptyUserpic> empty;
int paletteVersion : 31 = 0;
int forum : 1 = 0;
uint32 paletteVersion : 31 = 0;
uint32 forum : 1 = 0;
};
[[nodiscard]] bool PeerUserpicLoading(const PeerUserpicView &view);