Added initial implementation of chats filters tabs slider to td_ui.

This commit is contained in:
23rd 2024-11-02 16:48:00 +03:00
parent 5a4449f1a2
commit f0a2c47613
3 changed files with 245 additions and 0 deletions

View file

@ -0,0 +1,188 @@
/*
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 "ui/widgets/chat_filters_tabs_slider.h"
#include "ui/effects/ripple_animation.h"
#include "styles/style_widgets.h"
#include <QScrollBar>
namespace Ui {
ChatsFiltersTabs::ChatsFiltersTabs(
not_null<Ui::RpWidget*> parent,
const style::SettingsSlider &st)
: Ui::SettingsSlider(parent, st)
, _st(st)
, _unreadSt([&] {
auto st = Ui::UnreadBadgeStyle();
st.align = style::al_left;
return st;
}())
, _unreadMaxString(u"99+"_q)
, _unreadSkip(st::lineWidth * 5) {
Expects(_st.barSnapToLabel && _st.strictSkip);
if (_st.barRadius > 0) {
_bar.emplace(_st.barRadius, _st.barFg);
_barActive.emplace(_st.barRadius, _st.barFgActive);
}
{
const auto one = Ui::CountUnreadBadgeSize(u"9"_q, _unreadSt, 1);
_cachedBadgeWidths = {
one.width(),
Ui::CountUnreadBadgeSize(u"99"_q, _unreadSt, 2).width(),
Ui::CountUnreadBadgeSize(u"999"_q, _unreadSt, 2).width(),
};
_cachedBadgeHeight = one.height();
}
}
int ChatsFiltersTabs::centerOfSection(int section) const {
const auto widths = countSectionsWidths(0);
auto result = 0;
if (section >= 0 && section < widths.size()) {
for (auto i = 0; i < section; i++) {
result += widths[i];
}
result += widths[section] / 2;
}
return result;
}
void ChatsFiltersTabs::fitWidthToSections() {
const auto widths = countSectionsWidths(0);
resizeToWidth(ranges::accumulate(widths, .0));
}
void ChatsFiltersTabs::setUnreadCount(int index, int unreadCount) {
const auto it = _unreadCounts.find(index);
if (it == _unreadCounts.end()) {
if (unreadCount) {
_unreadCounts.emplace(index, Unread{
.cache = cacheUnreadCount(unreadCount),
.count = unreadCount,
});
}
} else {
if (unreadCount) {
it->second.count = unreadCount;
it->second.cache = cacheUnreadCount(unreadCount);
} else {
_unreadCounts.erase(it);
}
}
if (unreadCount) {
const auto widthIndex = (unreadCount < 10)
? 0
: (unreadCount < 100)
? 1
: 2;
setAdditionalContentWidthToSection(
index,
_cachedBadgeWidths[widthIndex] + _unreadSkip);
} else {
setAdditionalContentWidthToSection(index, 0);
}
}
QImage ChatsFiltersTabs::cacheUnreadCount(int count) const {
const auto widthIndex = (count < 10) ? 0 : (count < 100) ? 1 : 2;
auto image = QImage(
QSize(_cachedBadgeWidths[widthIndex], _cachedBadgeHeight)
* style::DevicePixelRatio(),
QImage::Format_ARGB32_Premultiplied);
image.setDevicePixelRatio(style::DevicePixelRatio());
image.fill(Qt::transparent);
const auto string = (count > 99)
? _unreadMaxString
: QString::number(count);
{
auto p = QPainter(&image);
Ui::PaintUnreadBadge(p, string, 0, 0, _unreadSt, 0);
}
return image;
}
void ChatsFiltersTabs::paintEvent(QPaintEvent *e) {
auto p = QPainter(this);
const auto clip = e->rect();
const auto range = getCurrentActiveRange();
auto index = 0;
enumerateSections([&](Section &section) {
const auto activeWidth = _st.barSnapToLabel
? section.contentWidth
: section.width;
const auto activeLeft = section.left
+ (section.width - activeWidth) / 2;
const auto active = 1.
- std::clamp(
std::abs(range.left - activeLeft) / float64(range.width),
0.,
1.);
if (section.ripple) {
const auto color = anim::color(
_st.rippleBg,
_st.rippleBgActive,
active);
section.ripple->paint(p, section.left, 0, width(), &color);
if (section.ripple->empty()) {
section.ripple.reset();
}
}
const auto labelLeft = section.left
+ (section.width - section.contentWidth) / 2;
const auto rect = myrtlrect(
labelLeft,
_st.labelTop,
section.contentWidth,
_st.labelStyle.font->height);
if (rect.intersects(clip)) {
p.setPen(anim::pen(_st.labelFg, _st.labelFgActive, active));
section.label.draw(p, {
.position = QPoint(labelLeft, _st.labelTop),
.outerWidth = width(),
.availableWidth = section.label.maxWidth(),
});
{
const auto it = _unreadCounts.find(index);
if (it != _unreadCounts.end()) {
p.drawImage(
labelLeft
+ _unreadSkip
+ section.label.maxWidth(),
_st.labelTop,
it->second.cache);
}
}
}
index++;
return true;
});
if (_st.barSnapToLabel) {
const auto drawRect = [&](QRect rect, bool active) {
const auto &bar = active ? _barActive : _bar;
if (bar) {
bar->paint(p, rect);
} else {
p.fillRect(rect, active ? _st.barFgActive : _st.barFg);
}
};
const auto add = _st.barStroke / 2;
const auto from = std::max(range.left - add, 0);
const auto till = std::min(range.left + range.width + add, width());
if (from < till) {
drawRect(
myrtlrect(from, _st.barTop, till - from, _st.barStroke),
true);
}
}
}
} // namespace Ui

View file

@ -0,0 +1,55 @@
/*
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
*/
#pragma once
#include "ui/unread_badge_paint.h"
#include "ui/widgets/discrete_sliders.h"
namespace style {
struct SettingsSlider;
} // namespace style
namespace Ui {
class RpWidget;
class SettingsSlider;
class ChatsFiltersTabs final : public Ui::SettingsSlider {
public:
ChatsFiltersTabs(
not_null<Ui::RpWidget*> parent,
const style::SettingsSlider &st);
[[nodiscard]] int centerOfSection(int section) const;
void fitWidthToSections();
void setUnreadCount(int index, int unreadCount);
protected:
void paintEvent(QPaintEvent *e) override;
private:
[[nodiscard]] QImage cacheUnreadCount(int count) const;
using Index = int;
struct Unread final {
QImage cache;
int count = 0;
};
base::flat_map<Index, Unread> _unreadCounts;
const style::SettingsSlider &_st;
const UnreadBadgeStyle _unreadSt;
const QString _unreadMaxString;
const int _unreadSkip;
std::vector<int> _cachedBadgeWidths;
int _cachedBadgeHeight = 0;
std::optional<Ui::RoundRect> _bar;
std::optional<Ui::RoundRect> _barActive;
};
} // namespace Ui

View file

@ -421,6 +421,8 @@ PRIVATE
ui/widgets/fields/time_part_input_with_placeholder.cpp
ui/widgets/fields/time_part_input_with_placeholder.h
ui/widgets/chat_filters_tabs_slider.cpp
ui/widgets/chat_filters_tabs_slider.h
ui/widgets/color_editor.cpp
ui/widgets/color_editor.h
ui/widgets/continuous_sliders.cpp