mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-16 14:17:12 +02:00
Add a semi-nice looking box with reactions overview.
This commit is contained in:
parent
3aacae2cb2
commit
c875f367e6
6 changed files with 228 additions and 36 deletions
|
@ -1085,7 +1085,10 @@ void AddWhoReactedAction(
|
|||
strong->hideMenu();
|
||||
}
|
||||
if (const auto item = controller->session().data().message(itemId)) {
|
||||
controller->window().show(ReactionsListBox(controller, item));
|
||||
controller->window().show(ReactionsListBox(
|
||||
controller,
|
||||
item,
|
||||
QString()));
|
||||
}
|
||||
};
|
||||
if (!menu->empty()) {
|
||||
|
|
|
@ -28,6 +28,7 @@ public:
|
|||
Controller(
|
||||
not_null<Window::SessionController*> window,
|
||||
not_null<HistoryItem*> item,
|
||||
const QString &selected,
|
||||
rpl::producer<QString> switches);
|
||||
|
||||
Main::Session &session() const override;
|
||||
|
@ -64,10 +65,12 @@ private:
|
|||
Controller::Controller(
|
||||
not_null<Window::SessionController*> window,
|
||||
not_null<HistoryItem*> item,
|
||||
const QString &selected,
|
||||
rpl::producer<QString> switches)
|
||||
: _window(window)
|
||||
, _item(item)
|
||||
, _api(&window->session().mtp()) {
|
||||
, _api(&window->session().mtp())
|
||||
, _shownReaction(selected) {
|
||||
std::move(
|
||||
switches
|
||||
) | rpl::filter([=](const QString &reaction) {
|
||||
|
@ -189,42 +192,113 @@ bool Controller::appendRow(not_null<UserData*> user, QString reaction) {
|
|||
return true;
|
||||
}
|
||||
|
||||
class Row final : public PeerListRow {
|
||||
public:
|
||||
Row(not_null<PeerData*> peer, const QString &reaction);
|
||||
|
||||
QSize rightActionSize() const override;
|
||||
QMargins rightActionMargins() const override;
|
||||
bool rightActionDisabled() const override;
|
||||
void rightActionPaint(
|
||||
Painter &p,
|
||||
int x,
|
||||
int y,
|
||||
int outerWidth,
|
||||
bool selected,
|
||||
bool actionSelected) override;
|
||||
|
||||
private:
|
||||
EmojiPtr _emoji = nullptr;
|
||||
|
||||
};
|
||||
|
||||
Row::Row(not_null<PeerData*> peer, const QString &reaction)
|
||||
: PeerListRow(peer)
|
||||
, _emoji(Ui::Emoji::Find(reaction)) {
|
||||
}
|
||||
|
||||
QSize Row::rightActionSize() const {
|
||||
const auto size = Ui::Emoji::GetSizeNormal() / style::DevicePixelRatio();
|
||||
return _emoji ? QSize(size, size) : QSize();
|
||||
}
|
||||
|
||||
QMargins Row::rightActionMargins() const {
|
||||
if (!_emoji) {
|
||||
return QMargins();
|
||||
}
|
||||
const auto size = Ui::Emoji::GetSizeNormal() / style::DevicePixelRatio();
|
||||
return QMargins(
|
||||
size / 2,
|
||||
(st::defaultPeerList.item.height - size) / 2,
|
||||
(size * 3) / 2,
|
||||
0);
|
||||
}
|
||||
|
||||
bool Row::rightActionDisabled() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
void Row::rightActionPaint(
|
||||
Painter &p,
|
||||
int x,
|
||||
int y,
|
||||
int outerWidth,
|
||||
bool selected,
|
||||
bool actionSelected) {
|
||||
if (!_emoji) {
|
||||
return;
|
||||
}
|
||||
// #TODO reactions
|
||||
Ui::Emoji::Draw(p, _emoji, Ui::Emoji::GetSizeNormal(), x, y);
|
||||
}
|
||||
|
||||
std::unique_ptr<PeerListRow> Controller::createRow(
|
||||
not_null<UserData*> user,
|
||||
QString reaction) const {
|
||||
auto result = std::make_unique<PeerListRow>(user);
|
||||
if (!reaction.isEmpty()) {
|
||||
result->setCustomStatus(reaction);
|
||||
}
|
||||
return result;
|
||||
return std::make_unique<Row>(user, reaction);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
object_ptr<Ui::BoxContent> ReactionsListBox(
|
||||
not_null<Window::SessionController*> window,
|
||||
not_null<HistoryItem*> item) {
|
||||
not_null<Window::SessionController*> window,
|
||||
not_null<HistoryItem*> item,
|
||||
QString selected) {
|
||||
Expects(IsServerMsgId(item->id));
|
||||
|
||||
if (!item->reactions().contains(selected)) {
|
||||
selected = QString();
|
||||
}
|
||||
const auto tabRequests = std::make_shared<rpl::event_stream<QString>>();
|
||||
const auto initBox = [=](not_null<PeerListBox*> box) {
|
||||
box->setNoContentMargin(true);
|
||||
|
||||
const auto selector = CreateReactionSelector(box, item->reactions());
|
||||
const auto selector = CreateReactionSelector(
|
||||
box,
|
||||
item->reactions(),
|
||||
selected);
|
||||
selector->changes(
|
||||
) | rpl::start_to_stream(*tabRequests, box->lifetime());
|
||||
|
||||
box->widthValue(
|
||||
) | rpl::start_with_next([=](int width) {
|
||||
selector->resizeToWidth(width);
|
||||
selector->move(0, 0);
|
||||
}, box->lifetime());
|
||||
selector->changes(
|
||||
) | rpl::start_to_stream(*tabRequests, box->lifetime());
|
||||
box->setAddedTopScrollSkip(selector->height());
|
||||
selector->heightValue(
|
||||
) | rpl::start_with_next([=](int height) {
|
||||
box->setAddedTopScrollSkip(height);
|
||||
}, box->lifetime());
|
||||
box->addButton(tr::lng_close(), [=] {
|
||||
box->closeBox();
|
||||
});
|
||||
};
|
||||
return Box<PeerListBox>(
|
||||
std::make_unique<Controller>(window, item, tabRequests->events()),
|
||||
std::make_unique<Controller>(
|
||||
window,
|
||||
item,
|
||||
selected,
|
||||
tabRequests->events()),
|
||||
initBox);
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ namespace HistoryView {
|
|||
|
||||
object_ptr<Ui::BoxContent> ReactionsListBox(
|
||||
not_null<Window::SessionController*> window,
|
||||
not_null<HistoryItem*> item);
|
||||
not_null<HistoryItem*> item,
|
||||
QString selected);
|
||||
|
||||
} // namespace HistoryView
|
||||
|
|
|
@ -7,15 +7,95 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
*/
|
||||
#include "history/view/reactions/message_reactions_selector.h"
|
||||
|
||||
#include "ui/widgets/discrete_sliders.h"
|
||||
#include "styles/style_layers.h"
|
||||
#include "ui/rp_widget.h"
|
||||
#include "ui/abstract_button.h"
|
||||
#include "styles/style_widgets.h"
|
||||
#include "styles/style_chat.h"
|
||||
|
||||
namespace HistoryView {
|
||||
namespace {
|
||||
|
||||
not_null<Ui::AbstractButton*> CreateTab(
|
||||
not_null<QWidget*> parent,
|
||||
const style::MultiSelect &st,
|
||||
const QString &reaction,
|
||||
int count,
|
||||
rpl::producer<bool> selected) {
|
||||
struct State {
|
||||
bool selected = false;
|
||||
QImage cache;
|
||||
};
|
||||
const auto stm = &st.item;
|
||||
const auto text = QString("%L1").arg(count);
|
||||
const auto font = st::semiboldFont;
|
||||
const auto textWidth = font->width(text);
|
||||
const auto result = Ui::CreateChild<Ui::AbstractButton>(parent.get());
|
||||
const auto width = stm->height
|
||||
+ stm->padding.left()
|
||||
+ textWidth
|
||||
+ stm->padding.right();
|
||||
result->resize(width, stm->height);
|
||||
const auto state = result->lifetime().make_state<State>();
|
||||
std::move(
|
||||
selected
|
||||
) | rpl::start_with_next([=](bool selected) {
|
||||
state->selected = selected;
|
||||
state->cache = QImage();
|
||||
result->update();
|
||||
}, result->lifetime());
|
||||
|
||||
result->paintRequest(
|
||||
) | rpl::start_with_next([=] {
|
||||
if (state->cache.isNull()) {
|
||||
const auto factor = style::DevicePixelRatio();
|
||||
state->cache = QImage(
|
||||
result->size() * factor,
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
state->cache.setDevicePixelRatio(factor);
|
||||
state->cache.fill(Qt::transparent);
|
||||
auto p = QPainter(&state->cache);
|
||||
|
||||
const auto height = stm->height;
|
||||
const auto radius = height / 2;
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(state->selected ? stm->textActiveBg : stm->textBg);
|
||||
{
|
||||
PainterHighQualityEnabler hq(p);
|
||||
p.drawRoundedRect(result->rect(), radius, radius);
|
||||
}
|
||||
const auto skip = st::reactionsTabIconSkip;
|
||||
const auto icon = QRect(skip, 0, height, height);
|
||||
if (const auto emoji = Ui::Emoji::Find(reaction)) {
|
||||
// #TODO reactions
|
||||
const auto size = Ui::Emoji::GetSizeNormal();
|
||||
const auto shift = (height - (size / factor)) / 2;
|
||||
Ui::Emoji::Draw(p, emoji, size, icon.x() + shift, shift);
|
||||
} else {
|
||||
(state->selected
|
||||
? st::reactionsTabAllSelected
|
||||
: st::reactionsTabAll).paintInCenter(p, icon);
|
||||
}
|
||||
|
||||
const auto textLeft = height + stm->padding.left();
|
||||
p.setPen(state->selected ? stm->textActiveFg : stm->textFg);
|
||||
p.setFont(font);
|
||||
p.drawText(textLeft, stm->padding.top() + font->ascent, text);
|
||||
}
|
||||
QPainter(result).drawImage(0, 0, state->cache);
|
||||
}, result->lifetime());
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
not_null<Selector*> CreateReactionSelector(
|
||||
not_null<QWidget*> parent,
|
||||
const base::flat_map<QString, int> &items) {
|
||||
const auto sectionsCount = int(items.size() + 1);
|
||||
const base::flat_map<QString, int> &items,
|
||||
const QString &selected) {
|
||||
struct State {
|
||||
rpl::variable<QString> selected;
|
||||
std::vector<not_null<Ui::AbstractButton*>> tabs;
|
||||
};
|
||||
const auto result = Ui::CreateChild<Selector>(parent.get());
|
||||
using Entry = std::pair<int, QString>;
|
||||
auto sorted = std::vector<Entry>();
|
||||
|
@ -28,30 +108,57 @@ not_null<Selector*> CreateReactionSelector(
|
|||
0,
|
||||
std::plus<>(),
|
||||
&Entry::first);
|
||||
auto labels = QStringList() << ("ALL (" + QString::number(count) + ")");
|
||||
auto tabs = Ui::CreateChild<Ui::RpWidget>(parent.get());
|
||||
const auto st = &st::reactionsTabs;
|
||||
const auto state = tabs->lifetime().make_state<State>();
|
||||
state->selected = selected;
|
||||
const auto append = [&](const QString &reaction, int count) {
|
||||
using namespace rpl::mappers;
|
||||
const auto tab = CreateTab(
|
||||
tabs,
|
||||
*st,
|
||||
reaction,
|
||||
count,
|
||||
state->selected.value() | rpl::map(_1 == reaction));
|
||||
tab->setClickedCallback([=] {
|
||||
state->selected = reaction;
|
||||
});
|
||||
state->tabs.push_back(tab);
|
||||
};
|
||||
append(QString(), count);
|
||||
for (const auto &[count, reaction] : sorted) {
|
||||
labels.append(reaction + " (" + QString::number(count) + ")");
|
||||
append(reaction, count);
|
||||
}
|
||||
auto tabs = Ui::CreateChild<Ui::SettingsSlider>(
|
||||
parent.get(),
|
||||
st::defaultTabsSlider);
|
||||
tabs->setSections(labels | ranges::to_vector);
|
||||
tabs->setRippleTopRoundRadius(st::boxRadius);
|
||||
result->move = [=](int x, int y) {
|
||||
tabs->moveToLeft(x, y);
|
||||
};
|
||||
result->resizeToWidth = [=](int width) {
|
||||
tabs->resizeToWidth(std::min(
|
||||
width,
|
||||
sectionsCount * st::defaultTabsSlider.height * 2));
|
||||
const auto available = width
|
||||
- st->padding.left()
|
||||
- st->padding.right();
|
||||
if (available <= 0) {
|
||||
return;
|
||||
}
|
||||
auto left = available;
|
||||
auto height = st->padding.top();
|
||||
for (const auto &tab : state->tabs) {
|
||||
if (left > 0 && available - left < tab->width()) {
|
||||
left = 0;
|
||||
height += tab->height() + st->itemSkip;
|
||||
}
|
||||
tab->move(
|
||||
st->padding.left() + left,
|
||||
height - tab->height() - st->itemSkip);
|
||||
left += tab->width() + st->itemSkip;
|
||||
}
|
||||
tabs->resize(width, height - st->itemSkip + st->padding.bottom());
|
||||
};
|
||||
result->height = [=] {
|
||||
return tabs->height() - st::lineWidth;
|
||||
result->heightValue = [=] {
|
||||
using namespace rpl::mappers;
|
||||
return tabs->heightValue() | rpl::map(_1 - st::lineWidth);
|
||||
};
|
||||
result->changes = [=] {
|
||||
return tabs->sectionActivated() | rpl::map([=](int section) {
|
||||
return (section > 0) ? sorted[section - 1].second : QString();
|
||||
});
|
||||
return state->selected.changes();
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -13,11 +13,12 @@ struct Selector {
|
|||
Fn<void(int, int)> move;
|
||||
Fn<void(int)> resizeToWidth;
|
||||
Fn<rpl::producer<QString>()> changes;
|
||||
Fn<int()> height;
|
||||
Fn<rpl::producer<int>()> heightValue;
|
||||
};
|
||||
|
||||
not_null<Selector*> CreateReactionSelector(
|
||||
not_null<QWidget*> parent,
|
||||
const base::flat_map<QString, int> &items);
|
||||
const base::flat_map<QString, int> &items,
|
||||
const QString &selected);
|
||||
|
||||
} // namespace HistoryView
|
||||
|
|
|
@ -921,6 +921,12 @@ whoReadPlayed: icon{{ "menu/read_audio", menuSubmenuArrowFg }};
|
|||
whoReadPlayedOver: icon{{ "menu/read_audio", menuSubmenuArrowFg }};
|
||||
whoReadPlayedDisabled: icon {{ "menu/read_audio", menuFgDisabled }};
|
||||
|
||||
reactionsTabAll: icon {{ "menu/read_reactions", windowFg }};
|
||||
reactionsTabAllSelected: icon {{ "menu/read_reactions", activeButtonFg }};
|
||||
reactionsTabs: MultiSelect(defaultMultiSelect) {
|
||||
padding: margins(12px, 10px, 12px, 10px);
|
||||
}
|
||||
reactionsTabIconSkip: 3px;
|
||||
historyRequestsUserpics: GroupCallUserpics {
|
||||
size: 22px;
|
||||
shift: 8px;
|
||||
|
|
Loading…
Add table
Reference in a new issue