mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-07-26 23:43:06 +02:00
Support vertical tabs somehow.
This commit is contained in:
parent
72b57924b7
commit
e0e69ce740
2 changed files with 469 additions and 13 deletions
|
@ -16,14 +16,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_saved_sublist.h"
|
#include "data/data_saved_sublist.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
#include "data/data_thread.h"
|
#include "data/data_thread.h"
|
||||||
|
#include "data/data_user.h"
|
||||||
#include "dialogs/dialogs_main_list.h"
|
#include "dialogs/dialogs_main_list.h"
|
||||||
#include "history/history.h"
|
#include "history/history.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
|
#include "main/main_session.h"
|
||||||
|
#include "ui/effects/ripple_animation.h"
|
||||||
#include "ui/text/text_utilities.h"
|
#include "ui/text/text_utilities.h"
|
||||||
#include "ui/widgets/buttons.h"
|
#include "ui/widgets/buttons.h"
|
||||||
#include "ui/widgets/discrete_sliders.h"
|
#include "ui/widgets/discrete_sliders.h"
|
||||||
#include "ui/widgets/scroll_area.h"
|
#include "ui/widgets/scroll_area.h"
|
||||||
#include "ui/widgets/shadow.h"
|
#include "ui/widgets/shadow.h"
|
||||||
|
#include "ui/dynamic_image.h"
|
||||||
|
#include "ui/dynamic_thumbnails.h"
|
||||||
#include "window/window_session_controller.h"
|
#include "window/window_session_controller.h"
|
||||||
#include "styles/style_chat.h"
|
#include "styles/style_chat.h"
|
||||||
|
|
||||||
|
@ -31,6 +36,284 @@ namespace HistoryView {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr auto kDefaultLimit = 5; AssertIsDebug()// 10;
|
constexpr auto kDefaultLimit = 5; AssertIsDebug()// 10;
|
||||||
|
constexpr auto kMaxNameLines = 3;
|
||||||
|
|
||||||
|
class VerticalSlider final : public Ui::RpWidget {
|
||||||
|
public:
|
||||||
|
explicit VerticalSlider(not_null<QWidget*> parent);
|
||||||
|
|
||||||
|
struct Section {
|
||||||
|
std::shared_ptr<Ui::DynamicImage> userpic;
|
||||||
|
QString text;
|
||||||
|
};
|
||||||
|
|
||||||
|
void setSections(std::vector<Section> sections, Fn<bool()> paused);
|
||||||
|
void setActiveSectionFast(int active);
|
||||||
|
|
||||||
|
void fitHeightToSections();
|
||||||
|
|
||||||
|
[[nodiscard]] rpl::producer<int> sectionActivated() const {
|
||||||
|
return _sectionActivated.events();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] int sectionsCount() const;
|
||||||
|
[[nodiscard]] int lookupSectionTop(int index) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Tab {
|
||||||
|
std::shared_ptr<Ui::DynamicImage> userpic;
|
||||||
|
Ui::Text::String text;
|
||||||
|
std::unique_ptr<Ui::RippleAnimation> ripple;
|
||||||
|
int top = 0;
|
||||||
|
int height = 0;
|
||||||
|
bool subscribed = false;
|
||||||
|
};
|
||||||
|
struct Range {
|
||||||
|
int top = 0;
|
||||||
|
int height = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
void paintEvent(QPaintEvent *e) override;
|
||||||
|
void timerEvent(QTimerEvent *e) override;
|
||||||
|
void mousePressEvent(QMouseEvent *e) override;
|
||||||
|
void mouseReleaseEvent(QMouseEvent *e) override;
|
||||||
|
|
||||||
|
void startRipple(int index);
|
||||||
|
[[nodiscard]] int getIndexFromPosition(QPoint position) const;
|
||||||
|
[[nodiscard]] QImage prepareRippleMask(int index, const Tab &tab);
|
||||||
|
|
||||||
|
void activateCallback();
|
||||||
|
[[nodiscard]] Range getFinalActiveRange() const;
|
||||||
|
|
||||||
|
const style::ChatTabsVertical &_st;
|
||||||
|
Ui::RoundRect _bar;
|
||||||
|
std::vector<Tab> _tabs;
|
||||||
|
int _active = -1;
|
||||||
|
int _pressed = -1;
|
||||||
|
Ui::Animations::Simple _activeTop;
|
||||||
|
Ui::Animations::Simple _activeHeight;
|
||||||
|
|
||||||
|
int _timerId = -1;
|
||||||
|
crl::time _callbackAfterMs = 0;
|
||||||
|
|
||||||
|
rpl::event_stream<int> _sectionActivated;
|
||||||
|
Fn<bool()> _paused;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
VerticalSlider::VerticalSlider(not_null<QWidget*> parent)
|
||||||
|
: RpWidget(parent)
|
||||||
|
, _st(st::chatTabsVertical)
|
||||||
|
, _bar(_st.barRadius, _st.barFg) {
|
||||||
|
setCursor(style::cur_pointer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VerticalSlider::setSections(
|
||||||
|
std::vector<Section> sections,
|
||||||
|
Fn<bool()> paused) {
|
||||||
|
auto old = base::take(_tabs);
|
||||||
|
_tabs.reserve(sections.size());
|
||||||
|
|
||||||
|
for (auto §ion : sections) {
|
||||||
|
const auto i = ranges::find(old, section.userpic, &Tab::userpic);
|
||||||
|
if (i != end(old)) {
|
||||||
|
_tabs.push_back(std::move(*i));
|
||||||
|
old.erase(i);
|
||||||
|
} else {
|
||||||
|
_tabs.push_back({ .userpic = std::move(section.userpic), });
|
||||||
|
}
|
||||||
|
_tabs.back().text = Ui::Text::String(
|
||||||
|
_st.nameStyle,
|
||||||
|
section.text,
|
||||||
|
kDefaultTextOptions,
|
||||||
|
_st.nameWidth);
|
||||||
|
}
|
||||||
|
for (const auto &was : old) {
|
||||||
|
if (was.subscribed) {
|
||||||
|
was.userpic->subscribeToUpdates(nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VerticalSlider::setActiveSectionFast(int active) {
|
||||||
|
_active = active;
|
||||||
|
_activeTop.stop();
|
||||||
|
_activeHeight.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VerticalSlider::fitHeightToSections() {
|
||||||
|
auto top = 0;
|
||||||
|
for (auto &tab : _tabs) {
|
||||||
|
tab.top = top;
|
||||||
|
tab.height = _st.baseHeight + std::min(
|
||||||
|
_st.nameStyle.font->height * kMaxNameLines,
|
||||||
|
tab.text.countHeight(_st.nameWidth, true));
|
||||||
|
top += tab.height;
|
||||||
|
}
|
||||||
|
resize(_st.width, top);
|
||||||
|
}
|
||||||
|
|
||||||
|
int VerticalSlider::sectionsCount() const {
|
||||||
|
return int(_tabs.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
int VerticalSlider::lookupSectionTop(int index) const {
|
||||||
|
Expects(index >= 0 && index < _tabs.size());
|
||||||
|
|
||||||
|
return _tabs[index].top;
|
||||||
|
}
|
||||||
|
|
||||||
|
VerticalSlider::Range VerticalSlider::getFinalActiveRange() const {
|
||||||
|
return (_active >= 0)
|
||||||
|
? Range{ _tabs[_active].top, _tabs[_active].height }
|
||||||
|
: Range();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VerticalSlider::paintEvent(QPaintEvent *e) {
|
||||||
|
const auto finalRange = getFinalActiveRange();
|
||||||
|
const auto range = Range{
|
||||||
|
int(base::SafeRound(_activeTop.value(finalRange.top))),
|
||||||
|
int(base::SafeRound(_activeHeight.value(finalRange.height))),
|
||||||
|
};
|
||||||
|
|
||||||
|
auto p = QPainter(this);
|
||||||
|
auto clip = e->rect();
|
||||||
|
const auto drawRect = [&](QRect rect) {
|
||||||
|
_bar.paint(p, rect);
|
||||||
|
};
|
||||||
|
const auto nameLeft = (_st.width - _st.nameWidth) / 2;
|
||||||
|
for (auto &tab : _tabs) {
|
||||||
|
if (!clip.intersects(QRect(0, tab.top, width(), tab.height))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const auto divider = std::max(std::min(tab.height, range.height), 1);
|
||||||
|
const auto active = 1.
|
||||||
|
- std::clamp(
|
||||||
|
std::abs(range.top - tab.top) / float64(divider),
|
||||||
|
0.,
|
||||||
|
1.);
|
||||||
|
if (tab.ripple) {
|
||||||
|
const auto color = anim::color(
|
||||||
|
_st.rippleBg,
|
||||||
|
_st.rippleBgActive,
|
||||||
|
active);
|
||||||
|
tab.ripple->paint(p, 0, tab.top, width(), &color);
|
||||||
|
if (tab.ripple->empty()) {
|
||||||
|
tab.ripple.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tab.subscribed) {
|
||||||
|
tab.subscribed = true;
|
||||||
|
tab.userpic->subscribeToUpdates([=] { update(); });
|
||||||
|
}
|
||||||
|
const auto &image = tab.userpic->image(_st.userpicSize);
|
||||||
|
const auto userpicLeft = (width() - _st.userpicSize) / 2;
|
||||||
|
p.drawImage(userpicLeft, tab.top + _st.userpicTop, image);
|
||||||
|
p.setPen(anim::pen(_st.nameFg, _st.nameFgActive, active));
|
||||||
|
tab.text.draw(p, {
|
||||||
|
.position = QPoint(nameLeft, tab.top + _st.nameTop),
|
||||||
|
.outerWidth = width(),
|
||||||
|
.availableWidth = _st.nameWidth,
|
||||||
|
.align = style::al_top,
|
||||||
|
.paused = _paused && _paused(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (range.height > 0) {
|
||||||
|
const auto add = _st.barStroke / 2;
|
||||||
|
drawRect(myrtlrect(-add, range.top, _st.barStroke, range.height));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VerticalSlider::timerEvent(QTimerEvent *e) {
|
||||||
|
activateCallback();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VerticalSlider::startRipple(int index) {
|
||||||
|
if (!_st.ripple.showDuration) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto &tab = _tabs[index];
|
||||||
|
if (!tab.ripple) {
|
||||||
|
auto mask = prepareRippleMask(index, tab);
|
||||||
|
tab.ripple = std::make_unique<Ui::RippleAnimation>(
|
||||||
|
_st.ripple,
|
||||||
|
std::move(mask),
|
||||||
|
[this] { update(); });
|
||||||
|
}
|
||||||
|
const auto point = mapFromGlobal(QCursor::pos());
|
||||||
|
tab.ripple->add(point - QPoint(0, tab.top));
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage VerticalSlider::prepareRippleMask(int index, const Tab &tab) {
|
||||||
|
return Ui::RippleAnimation::RectMask(QSize(width(), tab.height));
|
||||||
|
}
|
||||||
|
|
||||||
|
int VerticalSlider::getIndexFromPosition(QPoint position) const {
|
||||||
|
const auto count = int(_tabs.size());
|
||||||
|
for (auto i = 0; i != count; ++i) {
|
||||||
|
const auto &tab = _tabs[i];
|
||||||
|
if (position.y() < tab.top + tab.height) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VerticalSlider::mousePressEvent(QMouseEvent *e) {
|
||||||
|
for (auto i = 0, count = int(_tabs.size()); i != count; ++i) {
|
||||||
|
auto &tab = _tabs[i];
|
||||||
|
if (tab.top <= e->y() && e->y() < tab.top + tab.height) {
|
||||||
|
startRipple(i);
|
||||||
|
_pressed = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VerticalSlider::mouseReleaseEvent(QMouseEvent *e) {
|
||||||
|
const auto pressed = std::exchange(_pressed, -1);
|
||||||
|
if (pressed < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto index = getIndexFromPosition(e->pos());
|
||||||
|
if (pressed < _tabs.size()) {
|
||||||
|
if (_tabs[pressed].ripple) {
|
||||||
|
_tabs[pressed].ripple->lastStop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (index == pressed) {
|
||||||
|
if (_active != index) {
|
||||||
|
_callbackAfterMs = crl::now() + _st.duration;
|
||||||
|
activateCallback();
|
||||||
|
|
||||||
|
const auto from = getFinalActiveRange();
|
||||||
|
_active = index;
|
||||||
|
const auto to = getFinalActiveRange();
|
||||||
|
const auto updater = [this] { update(); };
|
||||||
|
_activeTop.start(updater, from.top, to.top, _st.duration);
|
||||||
|
_activeHeight.start(
|
||||||
|
updater,
|
||||||
|
from.height,
|
||||||
|
to.height,
|
||||||
|
_st.duration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VerticalSlider::activateCallback() {
|
||||||
|
if (_timerId >= 0) {
|
||||||
|
killTimer(_timerId);
|
||||||
|
_timerId = -1;
|
||||||
|
}
|
||||||
|
auto ms = crl::now();
|
||||||
|
if (ms >= _callbackAfterMs) {
|
||||||
|
_sectionActivated.fire_copy(_active);
|
||||||
|
} else {
|
||||||
|
_timerId = startTimer(_callbackAfterMs - ms, Qt::PreciseTimer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
@ -47,6 +330,13 @@ SubsectionTabs::SubsectionTabs(
|
||||||
track();
|
track();
|
||||||
refreshSlice();
|
refreshSlice();
|
||||||
setupHorizontal(parent);
|
setupHorizontal(parent);
|
||||||
|
|
||||||
|
dataChanged() | rpl::start_with_next([=] {
|
||||||
|
if (_loading) {
|
||||||
|
_loading = false;
|
||||||
|
refreshSlice();
|
||||||
|
}
|
||||||
|
}, _lifetime);
|
||||||
}
|
}
|
||||||
|
|
||||||
SubsectionTabs::~SubsectionTabs() {
|
SubsectionTabs::~SubsectionTabs() {
|
||||||
|
@ -63,6 +353,8 @@ void SubsectionTabs::setupHorizontal(not_null<QWidget*> parent) {
|
||||||
if (!_shadow) {
|
if (!_shadow) {
|
||||||
_shadow = Ui::CreateChild<Ui::PlainShadow>(parent);
|
_shadow = Ui::CreateChild<Ui::PlainShadow>(parent);
|
||||||
_shadow->show();
|
_shadow->show();
|
||||||
|
} else {
|
||||||
|
_shadow->raise();
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto toggle = Ui::CreateChild<Ui::IconButton>(
|
const auto toggle = Ui::CreateChild<Ui::IconButton>(
|
||||||
|
@ -132,13 +424,6 @@ void SubsectionTabs::setupHorizontal(not_null<QWidget*> parent) {
|
||||||
}
|
}
|
||||||
}, _horizontal->lifetime());
|
}, _horizontal->lifetime());
|
||||||
|
|
||||||
dataChanged() | rpl::start_with_next([=] {
|
|
||||||
if (_loading) {
|
|
||||||
_loading = false;
|
|
||||||
refreshSlice();
|
|
||||||
}
|
|
||||||
}, _horizontal->lifetime());
|
|
||||||
|
|
||||||
_horizontal->sizeValue(
|
_horizontal->sizeValue(
|
||||||
) | rpl::start_with_next([=](QSize size) {
|
) | rpl::start_with_next([=](QSize size) {
|
||||||
const auto togglew = toggle->width();
|
const auto togglew = toggle->width();
|
||||||
|
@ -147,7 +432,11 @@ void SubsectionTabs::setupHorizontal(not_null<QWidget*> parent) {
|
||||||
}, scroll->lifetime());
|
}, scroll->lifetime());
|
||||||
|
|
||||||
_horizontal->paintRequest() | rpl::start_with_next([=](QRect clip) {
|
_horizontal->paintRequest() | rpl::start_with_next([=](QRect clip) {
|
||||||
QPainter(_horizontal).fillRect(clip, st::windowBg);
|
QPainter(_horizontal).fillRect(
|
||||||
|
clip.intersected(
|
||||||
|
_horizontal->rect().marginsRemoved(
|
||||||
|
{ 0, 0, 0, st::lineWidth })),
|
||||||
|
st::windowBg);
|
||||||
}, _horizontal->lifetime());
|
}, _horizontal->lifetime());
|
||||||
|
|
||||||
_refreshed.events_starting_with_copy(
|
_refreshed.events_starting_with_copy(
|
||||||
|
@ -254,8 +543,52 @@ void SubsectionTabs::setupVertical(not_null<QWidget*> parent) {
|
||||||
toggleModes();
|
toggleModes();
|
||||||
});
|
});
|
||||||
toggle->move(0, 0);
|
toggle->move(0, 0);
|
||||||
const auto scroll = Ui::CreateChild<Ui::ScrollArea>(_vertical);
|
const auto scroll = Ui::CreateChild<Ui::ScrollArea>(
|
||||||
|
_vertical,
|
||||||
|
st::chatTabsScroll);
|
||||||
scroll->show();
|
scroll->show();
|
||||||
|
const auto tabs = scroll->setOwnedWidget(
|
||||||
|
object_ptr<VerticalSlider>(scroll));
|
||||||
|
tabs->sectionActivated() | rpl::start_with_next([=](int active) {
|
||||||
|
if (active >= 0
|
||||||
|
&& active < _slice.size()
|
||||||
|
&& _active != _slice[active]) {
|
||||||
|
auto params = Window::SectionShow();
|
||||||
|
params.way = Window::SectionShow::Way::ClearStack;
|
||||||
|
params.animated = anim::type::instant;
|
||||||
|
_controller->showThread(_slice[active], {}, params);
|
||||||
|
}
|
||||||
|
}, tabs->lifetime());
|
||||||
|
|
||||||
|
rpl::merge(
|
||||||
|
scroll->scrolls(),
|
||||||
|
_scrollCheckRequests.events(),
|
||||||
|
scroll->heightValue() | rpl::skip(1) | rpl::map_to(rpl::empty)
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
const auto height = scroll->height();
|
||||||
|
const auto top = scroll->scrollTop();
|
||||||
|
const auto max = scroll->scrollTopMax();
|
||||||
|
const auto availableTop = top;
|
||||||
|
const auto availableBottom = (max - top);
|
||||||
|
if (max <= 2 * height && _afterAvailable > 0) {
|
||||||
|
_beforeLimit *= 2;
|
||||||
|
_afterLimit *= 2;
|
||||||
|
}
|
||||||
|
if (availableTop < height
|
||||||
|
&& _beforeSkipped.value_or(0) > 0
|
||||||
|
&& !_slice.empty()) {
|
||||||
|
_around = _slice.front();
|
||||||
|
refreshSlice();
|
||||||
|
} else if (availableBottom < height) {
|
||||||
|
if (_afterAvailable > 0) {
|
||||||
|
_around = _slice.back();
|
||||||
|
refreshSlice();
|
||||||
|
} else if (!_afterSkipped.has_value()) {
|
||||||
|
_loading = true;
|
||||||
|
loadMore();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, _vertical->lifetime());
|
||||||
|
|
||||||
_vertical->sizeValue(
|
_vertical->sizeValue(
|
||||||
) | rpl::start_with_next([=](QSize size) {
|
) | rpl::start_with_next([=](QSize size) {
|
||||||
|
@ -271,7 +604,90 @@ void SubsectionTabs::setupVertical(not_null<QWidget*> parent) {
|
||||||
_refreshed.events_starting_with_copy(
|
_refreshed.events_starting_with_copy(
|
||||||
rpl::empty
|
rpl::empty
|
||||||
) | rpl::start_with_next([=] {
|
) | rpl::start_with_next([=] {
|
||||||
_vertical->resize(std::max(toggle->width(), 0), 0);
|
auto sections = std::vector<VerticalSlider::Section>();
|
||||||
|
auto activeIndex = -1;
|
||||||
|
for (const auto &thread : _slice) {
|
||||||
|
if (thread == _active) {
|
||||||
|
activeIndex = int(sections.size());
|
||||||
|
}
|
||||||
|
if (const auto topic = thread->asTopic()) {
|
||||||
|
sections.push_back({
|
||||||
|
.userpic = (topic->iconId()
|
||||||
|
? Ui::MakeEmojiThumbnail(
|
||||||
|
&topic->owner(),
|
||||||
|
Data::SerializeCustomEmojiId(topic->iconId()))
|
||||||
|
: Ui::MakeUserpicThumbnail(
|
||||||
|
_controller->session().user())),
|
||||||
|
.text = topic->title(),
|
||||||
|
});
|
||||||
|
} else if (const auto sublist = thread->asSublist()) {
|
||||||
|
const auto peer = sublist->sublistPeer();
|
||||||
|
sections.push_back({
|
||||||
|
.userpic = Ui::MakeUserpicThumbnail(peer),
|
||||||
|
.text = peer->shortName(),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
sections.push_back({
|
||||||
|
.userpic = Ui::MakeUserpicThumbnail(
|
||||||
|
_controller->session().user()),
|
||||||
|
.text = tr::lng_filters_all_short(tr::now),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const auto paused = [=] {
|
||||||
|
return _controller->isGifPausedAtLeastFor(
|
||||||
|
Window::GifPauseReason::Any);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto scrollSavingThread = (Data::Thread*)nullptr;
|
||||||
|
auto scrollSavingShift = 0;
|
||||||
|
auto scrollSavingIndex = -1;
|
||||||
|
if (const auto count = tabs->sectionsCount()) {
|
||||||
|
const auto scrollTop = scroll->scrollTop();
|
||||||
|
auto indexTop = tabs->lookupSectionTop(0);
|
||||||
|
for (auto index = 0; index != count; ++index) {
|
||||||
|
const auto nextTop = (index + 1 != count)
|
||||||
|
? tabs->lookupSectionTop(index + 1)
|
||||||
|
: (indexTop + scrollTop + 1);
|
||||||
|
if (indexTop <= scrollTop && nextTop > scrollTop) {
|
||||||
|
scrollSavingThread = _sectionsSlice[index];
|
||||||
|
scrollSavingShift = scrollTop - indexTop;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
indexTop = nextTop;
|
||||||
|
}
|
||||||
|
scrollSavingIndex = scrollSavingThread
|
||||||
|
? int(ranges::find(_slice, not_null(scrollSavingThread))
|
||||||
|
- begin(_slice))
|
||||||
|
: -1;
|
||||||
|
if (scrollSavingIndex == _slice.size()) {
|
||||||
|
scrollSavingIndex = -1;
|
||||||
|
for (auto index = 0; index != count; ++index) {
|
||||||
|
const auto thread = _sectionsSlice[index];
|
||||||
|
if (ranges::contains(_slice, thread)) {
|
||||||
|
scrollSavingThread = thread;
|
||||||
|
scrollSavingShift = scrollTop
|
||||||
|
- tabs->lookupSectionTop(index);
|
||||||
|
scrollSavingIndex = index;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tabs->setSections(sections, paused);
|
||||||
|
tabs->fitHeightToSections();
|
||||||
|
tabs->setActiveSectionFast(activeIndex);
|
||||||
|
_sectionsSlice = _slice;
|
||||||
|
_vertical->resize(
|
||||||
|
std::max(toggle->width(), tabs->width()),
|
||||||
|
_vertical->height());
|
||||||
|
if (scrollSavingIndex >= 0) {
|
||||||
|
scroll->scrollToY(tabs->lookupSectionTop(scrollSavingIndex)
|
||||||
|
+ scrollSavingShift);
|
||||||
|
}
|
||||||
|
|
||||||
|
_scrollCheckRequests.fire({});
|
||||||
}, _vertical->lifetime());
|
}, _vertical->lifetime());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -341,7 +757,7 @@ void SubsectionTabs::setBoundingRect(QRect boundingRect) {
|
||||||
_horizontal->height());
|
_horizontal->height());
|
||||||
_shadow->setGeometry(
|
_shadow->setGeometry(
|
||||||
boundingRect.x(),
|
boundingRect.x(),
|
||||||
_horizontal->y() + _horizontal->height(),
|
_horizontal->y() + _horizontal->height() - st::lineWidth,
|
||||||
boundingRect.width(),
|
boundingRect.width(),
|
||||||
st::lineWidth);
|
st::lineWidth);
|
||||||
} else {
|
} else {
|
||||||
|
@ -367,7 +783,7 @@ int SubsectionTabs::leftSkip() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
int SubsectionTabs::topSkip() const {
|
int SubsectionTabs::topSkip() const {
|
||||||
return _horizontal ? _horizontal->height() : 0;
|
return _horizontal ? (_horizontal->height() - st::lineWidth) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SubsectionTabs::raise() {
|
void SubsectionTabs::raise() {
|
||||||
|
|
|
@ -1284,3 +1284,43 @@ chatTabsSlider: SettingsSlider(defaultSettingsSlider) {
|
||||||
rippleBgActive: lightButtonBgOver;
|
rippleBgActive: lightButtonBgOver;
|
||||||
ripple: defaultRippleAnimation;
|
ripple: defaultRippleAnimation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ChatTabsVertical {
|
||||||
|
barStroke: pixels;
|
||||||
|
barRadius: pixels;
|
||||||
|
barFg: color;
|
||||||
|
nameStyle: TextStyle;
|
||||||
|
nameWidth: pixels;
|
||||||
|
nameTop: pixels;
|
||||||
|
nameFg: color;
|
||||||
|
nameFgActive: color;
|
||||||
|
userpicTop: pixels;
|
||||||
|
userpicSize: pixels;
|
||||||
|
baseHeight: pixels;
|
||||||
|
width: pixels;
|
||||||
|
ripple: RippleAnimation;
|
||||||
|
rippleBg: color;
|
||||||
|
rippleBgActive: color;
|
||||||
|
duration: int;
|
||||||
|
}
|
||||||
|
|
||||||
|
chatTabsVertical: ChatTabsVertical {
|
||||||
|
barStroke: 8px;
|
||||||
|
barRadius: 4px;
|
||||||
|
barFg: sliderBgActive;
|
||||||
|
nameStyle: TextStyle(defaultTextStyle) {
|
||||||
|
font: font(10px);
|
||||||
|
}
|
||||||
|
nameWidth: 46px;
|
||||||
|
nameTop: 46px;
|
||||||
|
nameFg: windowSubTextFg;
|
||||||
|
nameFgActive: lightButtonFg;
|
||||||
|
userpicTop: 8px;
|
||||||
|
userpicSize: 36px;
|
||||||
|
baseHeight: 56px;
|
||||||
|
width: 56px;
|
||||||
|
ripple: defaultRippleAnimation;
|
||||||
|
rippleBg: windowBgOver;
|
||||||
|
rippleBgActive: lightButtonBgOver;
|
||||||
|
duration: 150;
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue