mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Added initial implementation of chats filters tabs slider to td_ui.
This commit is contained in:
parent
5a4449f1a2
commit
f0a2c47613
3 changed files with 245 additions and 0 deletions
188
Telegram/SourceFiles/ui/widgets/chat_filters_tabs_slider.cpp
Normal file
188
Telegram/SourceFiles/ui/widgets/chat_filters_tabs_slider.cpp
Normal 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 §ion) {
|
||||||
|
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
|
55
Telegram/SourceFiles/ui/widgets/chat_filters_tabs_slider.h
Normal file
55
Telegram/SourceFiles/ui/widgets/chat_filters_tabs_slider.h
Normal 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
|
|
@ -421,6 +421,8 @@ PRIVATE
|
||||||
ui/widgets/fields/time_part_input_with_placeholder.cpp
|
ui/widgets/fields/time_part_input_with_placeholder.cpp
|
||||||
ui/widgets/fields/time_part_input_with_placeholder.h
|
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.cpp
|
||||||
ui/widgets/color_editor.h
|
ui/widgets/color_editor.h
|
||||||
ui/widgets/continuous_sliders.cpp
|
ui/widgets/continuous_sliders.cpp
|
||||||
|
|
Loading…
Add table
Reference in a new issue