mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2026-08-07 12:15:19 +00:00
324 lines
9.2 KiB
C++
324 lines
9.2 KiB
C++
/*
|
|
This file is part of Telegram Desktop,
|
|
the official desktop application for the Telegram messaging service.
|
|
|
|
For license and copyright information please follow this link:
|
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|
*/
|
|
#include "info/profile/info_profile_section_stack.h"
|
|
|
|
#include "ui/widgets/box_content_divider.h"
|
|
#include "ui/widgets/labels.h"
|
|
#include "ui/wrap/slide_wrap.h"
|
|
#include "ui/wrap/vertical_layout.h"
|
|
#include "ui/vertical_list.h"
|
|
|
|
#include "styles/style_info.h"
|
|
#include "styles/style_layers.h"
|
|
|
|
namespace Info::Profile {
|
|
|
|
SectionSeparator SectionSeparator::None() {
|
|
return SectionSeparator{ .kind = Kind::None };
|
|
}
|
|
|
|
SectionSeparator SectionSeparator::Plain() {
|
|
return SectionSeparator{ .kind = Kind::Plain };
|
|
}
|
|
|
|
SectionSeparator SectionSeparator::Text(
|
|
rpl::producer<TextWithEntities> text,
|
|
Fn<void(not_null<Ui::FlatLabel*>)> setup) {
|
|
return SectionSeparator{
|
|
.kind = Kind::Text,
|
|
.text = std::move(text),
|
|
.textSetup = std::move(setup),
|
|
};
|
|
}
|
|
|
|
SectionStack::SectionStack(not_null<Ui::VerticalLayout*> layout)
|
|
: _layout(layout) {
|
|
}
|
|
|
|
void SectionStack::add(Section section) {
|
|
Expects(!_finalized);
|
|
|
|
_sections.push_back(std::move(section));
|
|
}
|
|
|
|
void SectionStack::addPlainSeparator() {
|
|
Expects(!_finalized);
|
|
|
|
if (_sections.empty()) {
|
|
return;
|
|
}
|
|
const auto position = int(_sections.size() - 1);
|
|
if (!_plainMarkerAfter.empty()
|
|
&& _plainMarkerAfter.back() == position) {
|
|
return;
|
|
}
|
|
_plainMarkerAfter.push_back(position);
|
|
}
|
|
|
|
int SectionStack::count() const {
|
|
return int(_sections.size());
|
|
}
|
|
|
|
not_null<Ui::VerticalLayout*> SectionStack::layout() const {
|
|
return _layout;
|
|
}
|
|
|
|
rpl::producer<bool> SectionStack::anyShownAtOrBefore(int index) const {
|
|
if (index < 0) {
|
|
return rpl::single(false);
|
|
}
|
|
auto producers = std::vector<rpl::producer<bool>>();
|
|
producers.reserve(index + 1);
|
|
for (auto k = 0; k <= index; ++k) {
|
|
producers.push_back(_sections[k].shown
|
|
? rpl::duplicate(_sections[k].shown)
|
|
: rpl::single(true));
|
|
}
|
|
return rpl::combine(
|
|
std::move(producers),
|
|
[](const std::vector<bool> &v) {
|
|
return ranges::find(v, true) != v.end();
|
|
}) | rpl::distinct_until_changed();
|
|
}
|
|
|
|
rpl::producer<bool> SectionStack::anyShownAfter(int index) const {
|
|
if (index + 1 >= int(_sections.size())) {
|
|
return rpl::single(false);
|
|
}
|
|
auto producers = std::vector<rpl::producer<bool>>();
|
|
producers.reserve(_sections.size() - index - 1);
|
|
for (auto k = index + 1; k != int(_sections.size()); ++k) {
|
|
producers.push_back(_sections[k].shown
|
|
? rpl::duplicate(_sections[k].shown)
|
|
: rpl::single(true));
|
|
}
|
|
return rpl::combine(
|
|
std::move(producers),
|
|
[](const std::vector<bool> &v) {
|
|
return ranges::find(v, true) != v.end();
|
|
}) | rpl::distinct_until_changed();
|
|
}
|
|
|
|
rpl::producer<bool> SectionStack::anyShownInRange(
|
|
int from,
|
|
int toInclusive) const {
|
|
const auto sectionCount = int(_sections.size());
|
|
if (from > toInclusive || from >= sectionCount || toInclusive < 0) {
|
|
return rpl::single(false);
|
|
}
|
|
const auto begin = std::max(from, 0);
|
|
const auto end = std::min(toInclusive, sectionCount - 1);
|
|
auto producers = std::vector<rpl::producer<bool>>();
|
|
producers.reserve(end - begin + 1);
|
|
for (auto k = begin; k <= end; ++k) {
|
|
producers.push_back(_sections[k].shown
|
|
? rpl::duplicate(_sections[k].shown)
|
|
: rpl::single(true));
|
|
}
|
|
return rpl::combine(
|
|
std::move(producers),
|
|
[](const std::vector<bool> &v) {
|
|
return ranges::find(v, true) != v.end();
|
|
}) | rpl::distinct_until_changed();
|
|
}
|
|
|
|
rpl::producer<bool> SectionStack::nextVisibleIsNonEmbedding(
|
|
int afterIndex) const {
|
|
if (afterIndex + 1 >= int(_sections.size())) {
|
|
return rpl::single(false);
|
|
}
|
|
auto producers = std::vector<rpl::producer<bool>>();
|
|
auto embeds = std::vector<bool>();
|
|
producers.reserve(_sections.size() - afterIndex - 1);
|
|
embeds.reserve(_sections.size() - afterIndex - 1);
|
|
for (auto j = afterIndex + 1; j != int(_sections.size()); ++j) {
|
|
producers.push_back(_sections[j].shown
|
|
? rpl::duplicate(_sections[j].shown)
|
|
: rpl::single(true));
|
|
embeds.push_back(_sections[j].embedsLeadingSeparator);
|
|
}
|
|
return rpl::combine(
|
|
std::move(producers),
|
|
[embeds = std::move(embeds)](const std::vector<bool> &values) {
|
|
for (auto k = 0; k != int(values.size()); ++k) {
|
|
if (values[k]) {
|
|
return !embeds[k];
|
|
}
|
|
}
|
|
return false;
|
|
}) | rpl::distinct_until_changed();
|
|
}
|
|
|
|
rpl::producer<bool> SectionStack::firstVisibleAttachesToCover() const {
|
|
if (_sections.empty()) {
|
|
return rpl::single(false);
|
|
}
|
|
auto producers = std::vector<rpl::producer<bool>>();
|
|
auto attaches = std::vector<bool>();
|
|
producers.reserve(_sections.size());
|
|
attaches.reserve(_sections.size());
|
|
for (auto j = 0; j != int(_sections.size()); ++j) {
|
|
producers.push_back(_sections[j].shown
|
|
? rpl::duplicate(_sections[j].shown)
|
|
: rpl::single(true));
|
|
attaches.push_back(_sections[j].attachesToCover);
|
|
}
|
|
return rpl::combine(
|
|
std::move(producers),
|
|
[attaches = std::move(attaches)](const std::vector<bool> &values) {
|
|
for (auto k = 0; k != int(values.size()); ++k) {
|
|
if (values[k]) {
|
|
return attaches[k];
|
|
}
|
|
}
|
|
return false;
|
|
}) | rpl::distinct_until_changed();
|
|
}
|
|
|
|
rpl::producer<bool> SectionStack::computePlainMarkerCandidate(
|
|
int position) const {
|
|
auto upper = anyShownAtOrBefore(position);
|
|
auto lower = nextVisibleIsNonEmbedding(position);
|
|
return rpl::combine(
|
|
std::move(upper),
|
|
std::move(lower)
|
|
) | rpl::map([](bool u, bool l) {
|
|
return u && l;
|
|
}) | rpl::distinct_until_changed();
|
|
}
|
|
|
|
void SectionStack::addPlainMarkerSlot(
|
|
int markerIndex,
|
|
not_null<std::vector<rpl::variable<bool>>*> candidates,
|
|
bool suppressedByText) {
|
|
auto inner = object_ptr<Ui::VerticalLayout>(_layout);
|
|
Ui::AddSkip(inner.data(), st::infoProfileSkip);
|
|
inner->add(object_ptr<Ui::BoxContentDivider>(
|
|
inner.data(),
|
|
st::boxDividerHeight,
|
|
st::defaultDividerBar));
|
|
Ui::AddSkip(inner.data(), st::infoProfileSkip);
|
|
const auto wrap = _layout->add(
|
|
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
|
_layout,
|
|
std::move(inner)));
|
|
|
|
auto shown = rpl::producer<bool>();
|
|
if (suppressedByText) {
|
|
shown = rpl::single(false);
|
|
} else if (markerIndex == 0) {
|
|
shown = (*candidates)[0].value();
|
|
} else {
|
|
const auto position = _plainMarkerAfter[markerIndex];
|
|
const auto prevPosition = _plainMarkerAfter[markerIndex - 1];
|
|
auto sameGap = anyShownInRange(prevPosition + 1, position);
|
|
shown = rpl::combine(
|
|
(*candidates)[markerIndex].value(),
|
|
(*candidates)[markerIndex - 1].value(),
|
|
std::move(sameGap)
|
|
) | rpl::map([](bool here, bool prev, bool anyBetween) {
|
|
const auto sameGapAsPrev = !anyBetween;
|
|
return here && !(sameGapAsPrev && prev);
|
|
}) | rpl::distinct_until_changed();
|
|
}
|
|
wrap->setDuration(st::infoSlideDuration)->toggleOn(std::move(shown));
|
|
}
|
|
|
|
void SectionStack::addTextSeparatorSlot(
|
|
int sectionIndex,
|
|
SectionSeparator &trailing) {
|
|
auto inner = object_ptr<Ui::VerticalLayout>(_layout);
|
|
Ui::AddSkip(inner.data(), st::infoProfileSkip);
|
|
auto label = object_ptr<Ui::FlatLabel>(
|
|
inner.data(),
|
|
std::move(trailing.text),
|
|
st::defaultDividerLabel.label);
|
|
const auto rawLabel = label.data();
|
|
inner->add(object_ptr<Ui::DividerLabel>(
|
|
inner.data(),
|
|
std::move(label),
|
|
st::defaultBoxDividerLabelPadding,
|
|
st::defaultDividerLabel.bar,
|
|
RectPart::Top | RectPart::Bottom));
|
|
Ui::AddSkip(inner.data(), st::infoProfileSkip);
|
|
const auto wrap = _layout->add(
|
|
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
|
_layout,
|
|
std::move(inner)));
|
|
if (trailing.textSetup) {
|
|
trailing.textSetup(rawLabel);
|
|
}
|
|
auto upper = anyShownAtOrBefore(sectionIndex);
|
|
auto lower = anyShownAfter(sectionIndex);
|
|
auto shown = rpl::combine(
|
|
std::move(upper),
|
|
std::move(lower)
|
|
) | rpl::map([](bool u, bool l) {
|
|
return u && l;
|
|
}) | rpl::distinct_until_changed();
|
|
wrap->setDuration(st::infoSlideDuration)->toggleOn(std::move(shown));
|
|
}
|
|
|
|
void SectionStack::finalize() {
|
|
Expects(!_finalized);
|
|
|
|
_finalized = true;
|
|
for (auto &s : _sections) {
|
|
if (!s.shown) {
|
|
s.shown = rpl::single(true);
|
|
}
|
|
}
|
|
|
|
auto leadingInner = object_ptr<Ui::VerticalLayout>(_layout);
|
|
Ui::AddSkip(leadingInner.data(), st::infoProfileSkip);
|
|
const auto leadingWrap = _layout->add(
|
|
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
|
_layout,
|
|
std::move(leadingInner)));
|
|
leadingWrap->setDuration(st::infoSlideDuration)->toggleOn(
|
|
firstVisibleAttachesToCover()
|
|
| rpl::map([](bool a) { return !a; }));
|
|
|
|
const auto sectionCount = int(_sections.size());
|
|
const auto markerCandidates = _layout->lifetime().make_state<
|
|
std::vector<rpl::variable<bool>>>();
|
|
markerCandidates->reserve(_plainMarkerAfter.size());
|
|
for (auto m = 0; m != int(_plainMarkerAfter.size()); ++m) {
|
|
const auto position = _plainMarkerAfter[m];
|
|
markerCandidates->emplace_back(
|
|
computePlainMarkerCandidate(position));
|
|
}
|
|
|
|
auto nextMarker = 0;
|
|
for (auto i = 0; i != sectionCount; ++i) {
|
|
auto §ion = _sections[i];
|
|
Expects(section.widget != nullptr);
|
|
|
|
_layout->add(std::move(section.widget));
|
|
|
|
const auto hasText = (section.trailing.kind
|
|
== SectionSeparator::Kind::Text);
|
|
if (hasText && i + 1 < sectionCount) {
|
|
addTextSeparatorSlot(i, section.trailing);
|
|
}
|
|
|
|
while (nextMarker != int(_plainMarkerAfter.size())
|
|
&& _plainMarkerAfter[nextMarker] == i) {
|
|
addPlainMarkerSlot(
|
|
nextMarker,
|
|
markerCandidates,
|
|
hasText);
|
|
++nextMarker;
|
|
}
|
|
}
|
|
|
|
Ui::AddSkip(_layout, st::infoProfileSkip);
|
|
}
|
|
|
|
} // namespace Info::Profile
|