Use Text to display empty group state.

This commit is contained in:
John Preston 2019-01-18 12:11:15 +04:00
parent 8643972f8c
commit 01b7d4ffba
7 changed files with 175 additions and 123 deletions

View file

@ -180,7 +180,7 @@ HistoryInner::HistoryInner(
void HistoryInner::messagesReceived(
PeerData *peer,
const QVector<MTPMessage> &messages) {
if (_history && _history->peer == peer) {
if (_history->peer == peer) {
_history->addOlderSlice(messages);
} else if (_migrated && _migrated->peer == peer) {
const auto newLoaded = _migrated
@ -194,7 +194,7 @@ void HistoryInner::messagesReceived(
}
void HistoryInner::messagesReceivedDown(PeerData *peer, const QVector<MTPMessage> &messages) {
if (_history && _history->peer == peer) {
if (_history->peer == peer) {
const auto oldLoaded = _migrated
&& _history->isEmpty()
&& !_migrated->isEmpty();
@ -523,6 +523,14 @@ TextSelection HistoryInner::itemRenderSelection(
return TextSelection();
}
void HistoryInner::paintEmpty(Painter &p, int width, int height) {
if (!_emptyPainter) {
_emptyPainter = std::make_unique<HistoryView::EmptyPainter>(
_history);
}
_emptyPainter->paint(p, width, height);
}
void HistoryInner::paintEvent(QPaintEvent *e) {
if (Ui::skipPaintEvent(this, e)) {
return;
@ -552,7 +560,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
p.restoreTextPalette();
}
} else if (historyDisplayedEmpty) {
HistoryView::paintEmpty(p, _history, width(), height());
paintEmpty(p, width(), height());
}
if (!noHistoryDisplayed) {
auto readMentions = base::flat_set<not_null<HistoryItem*>>();
@ -1371,8 +1379,6 @@ void HistoryInner::mouseReleaseEvent(QMouseEvent *e) {
}
void HistoryInner::mouseDoubleClickEvent(QMouseEvent *e) {
if (!_history) return;
mouseActionStart(e->globalPos(), e->button());
const auto mouseActionView = _mouseActionItem
@ -2101,8 +2107,6 @@ bool HistoryInner::displayScrollDate() const {
}
void HistoryInner::scrollDateCheck() {
if (!_history) return;
auto newScrollDateItem = _history->scrollTopItem ? _history->scrollTopItem : (_migrated ? _migrated->scrollTopItem : nullptr);
auto newScrollDateItemTop = _history->scrollTopItem ? _history->scrollTopOffset : (_migrated ? _migrated->scrollTopOffset : 0);
//if (newScrollDateItem && !displayScrollDate()) {
@ -2282,7 +2286,6 @@ auto HistoryInner::nextItem(Element *view) -> Element* {
} else if (const auto result = view->nextInBlocks()) {
return result;
} else if (view->data()->history() == _migrated
&& _history
&& _migrated->loadedAtBottom()
&& _history->loadedAtTop()
&& !_history->isEmpty()) {
@ -2380,7 +2383,7 @@ void HistoryInner::onTouchSelect() {
}
void HistoryInner::mouseActionUpdate() {
if (!_history || hasPendingResizedItems()) {
if (hasPendingResizedItems()) {
return;
}
@ -2553,7 +2556,6 @@ void HistoryInner::mouseActionUpdate() {
}
} else if (item) {
if (_mouseAction == MouseAction::Selecting) {
auto canSelectMany = (_history != nullptr);
if (selectingText) {
uint16 second = dragState.symbol;
if (dragState.afterSymbol && _mouseSelectType == TextSelectType::Letters) {
@ -2573,8 +2575,8 @@ void HistoryInner::mouseActionUpdate() {
_wasSelectedText = true;
setFocus();
}
updateDragSelection(0, 0, false);
} else if (canSelectMany) {
updateDragSelection(nullptr, nullptr, false);
} else {
auto selectingDown = (itemTop(_mouseActionItem) < itemTop(item)) || (_mouseActionItem == item && _dragStartPosition.y() < m.y());
auto dragSelFrom = _mouseActionItem->mainView();
auto dragSelTo = view;
@ -2680,7 +2682,7 @@ void HistoryInner::updateDragSelection(Element *dragSelFrom, Element *dragSelTo,
int HistoryInner::historyHeight() const {
int result = 0;
if (!_history || _history->isEmpty()) {
if (_history->isEmpty()) {
result += _migrated ? _migrated->height() : 0;
} else {
result += _history->height() - _historySkipHeight + (_migrated ? _migrated->height() : 0);
@ -2706,7 +2708,11 @@ int HistoryInner::migratedTop() const {
int HistoryInner::historyTop() const {
int mig = migratedTop();
return (_history && !_history->isEmpty()) ? (mig >= 0 ? (mig + _migrated->height() - _historySkipHeight) : _historyPaddingTop) : -1;
return !_history->isEmpty()
? (mig >= 0
? (mig + _migrated->height() - _historySkipHeight)
: _historyPaddingTop)
: -1;
}
int HistoryInner::historyDrawTop() const {
@ -2736,7 +2742,7 @@ int HistoryInner::itemTop(const Element *view) const {
}
void HistoryInner::notifyIsBotChanged() {
const auto newinfo = (_peer && _peer->isUser())
const auto newinfo = _peer->isUser()
? _peer->asUser()->botInfo.get()
: nullptr;
if ((!newinfo && !_botAbout)
@ -2918,7 +2924,7 @@ void HistoryInner::deleteItem(not_null<HistoryItem*> item) {
}
bool HistoryInner::hasPendingResizedItems() const {
return (_history && _history->hasPendingResizedItems())
return _history->hasPendingResizedItems()
|| (_migrated && _migrated->hasPendingResizedItems());
}
@ -3007,12 +3013,12 @@ void HistoryInner::applyDragSelection(
addSelectionRange(toItems, _history, fromblock, fromitem, toblock, toitem);
} else {
auto toRemove = std::vector<not_null<HistoryItem*>>();
for (auto i = toItems->begin(); i != toItems->cend(); ++i) {
auto iy = itemTop(i->first);
for (const auto &item : *toItems) {
auto iy = itemTop(item.first);
if (iy < -1) {
toRemove.push_back(i->first);
toRemove.emplace_back(item.first);
} else if (iy >= 0 && iy >= selfromy && iy < seltoy) {
toRemove.push_back(i->first);
toRemove.emplace_back(item.first);
}
}
for (const auto item : toRemove) {

View file

@ -23,6 +23,7 @@ struct TextState;
struct StateRequest;
enum class CursorState : char;
enum class PointState : char;
class EmptyPainter;
} // namespace HistoryView
namespace Window {
@ -54,6 +55,7 @@ public:
void messagesReceivedDown(PeerData *peer, const QVector<MTPMessage> &messages);
TextWithEntities getSelectedText() const;
void paintEmpty(Painter &p, int width, int height);
void touchScrollUpdated(const QPoint &screenPos);
@ -292,8 +294,8 @@ private:
not_null<Window::Controller*> _controller;
not_null<PeerData*> _peer;
not_null<History*> _history;
const not_null<PeerData*> _peer;
const not_null<History*> _history;
History *_migrated = nullptr;
int _contentWidth = 0;
int _historyPaddingTop = 0;
@ -303,6 +305,7 @@ private:
int _historySkipHeight = 0;
std::unique_ptr<BotAbout> _botAbout;
std::unique_ptr<HistoryView::EmptyPainter> _emptyPainter;
HistoryWidget *_widget = nullptr;
Ui::ScrollArea *_scroll = nullptr;

View file

@ -298,7 +298,7 @@ protected:
void setGroupId(MessageGroupId groupId);
Text _text = { int(st::msgMinWidth) };
Text _text = { st::msgMinWidth };
int _textWidth = -1;
int _textHeight = 0;

View file

@ -6557,7 +6557,10 @@ void HistoryWidget::paintEvent(QPaintEvent *e) {
}
if (_scroll->isHidden() && _history) {
p.setClipRect(_scroll->geometry());
HistoryView::paintEmpty(p, _history, width(), height() - _field->height() - 2 * st::historySendPadding);
_list->paintEmpty(
p,
width(),
height() - _field->height() - 2 * st::historySendPadding);
}
} else {
const auto w = st::msgServiceFont->width(lang(lng_willbe_history))

View file

@ -15,10 +15,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_abstract_structure.h"
#include "data/data_chat.h"
#include "data/data_channel.h"
#include "styles/style_history.h"
#include "ui/text_options.h"
#include "mainwidget.h"
#include "layout.h"
#include "lang/lang_keys.h"
#include "styles/style_history.h"
namespace HistoryView {
namespace {
@ -175,81 +176,13 @@ void paintPreparedDate(Painter &p, const QString &dateText, int dateTextWidth, i
p.drawText(left + st::msgServicePadding.left(), y + st::msgServiceMargin.top() + st::msgServicePadding.top() + st::msgServiceFont->ascent, dateText);
}
void PaintAboutGroup(Painter &p, int width, int height) {
constexpr auto kPhrasesCount = 4;
const auto header = lang(lng_group_about_header);
const auto text = lang(lng_group_about_text);
const auto phrases = std::array<QString, kPhrasesCount>{
lang(lng_group_about1),
lang(lng_group_about2),
lang(lng_group_about3),
lang(lng_group_about4),
};
const auto &font = st::msgServiceFont;
const auto headerWidth = font->width(header);
const auto textWidth = font->width(text);
const auto phraseWidths = ranges::view::all(
phrases
) | ranges::view::transform([&](const QString &text) {
return font->width(text);
}) | ranges::to_vector;
const auto maxPhraseWidth = *ranges::max_element(phraseWidths);
const auto margin = st::msgMargin.left();
const auto maxBubbleWidth = width - 2 * st::historyGroupAboutMargin;
const auto padding = st::historyGroupAboutPadding;
const auto bubbleWidth = std::min(
maxBubbleWidth,
std::max({
maxPhraseWidth + st::historyGroupAboutBulletSkip,
headerWidth,
textWidth }) + padding.left() + padding.right());
const auto bubbleHeight = padding.top()
+ font->height
+ st::historyGroupAboutHeaderSkip
+ font->height
+ st::historyGroupAboutTextSkip
+ font->height * kPhrasesCount
+ st::historyGroupAboutSkip * (kPhrasesCount - 1)
+ padding.bottom();
const auto bubbleLeft = (width - bubbleWidth) / 2;
const auto bubbleTop = (height - bubbleHeight) / 3;
ServiceMessagePainter::paintBubble(
p,
bubbleLeft,
bubbleTop,
bubbleWidth,
bubbleHeight);
p.setFont(font);
p.setPen(st::msgServiceFg);
const auto left = bubbleLeft + padding.left();
auto top = bubbleTop + padding.top();
p.drawTextLeft(bubbleLeft + (bubbleWidth - headerWidth) / 2, top, width, header);
top += font->height + st::historyGroupAboutHeaderSkip;
p.drawTextLeft(left, top, width, text);
top += font->height + st::historyGroupAboutTextSkip;
p.setBrush(st::msgServiceFg);
for (const auto &text : phrases) {
p.setPen(st::msgServiceFg);
p.drawTextLeft(left + st::historyGroupAboutBulletSkip, top, width, text);
{
PainterHighQualityEnabler hq(p);
p.setPen(Qt::NoPen);
p.drawEllipse(
left,
top + (font->height - st::mediaUnreadSize) / 2,
st::mediaUnreadSize,
st::mediaUnreadSize);
}
top += font->height + st::historyGroupAboutSkip;
bool NeedAboutGroup(not_null<History*> history) {
if (const auto chat = history->peer->asChat()) {
return chat->amCreator();
} else if (const auto channel = history->peer->asMegagroup()) {
return channel->amCreator();
}
return false;
}
} // namepsace
@ -360,24 +293,6 @@ QVector<int> ServiceMessagePainter::countLineWidths(const Text &text, const QRec
return lineWidths;
}
void paintEmpty(
Painter &p,
not_null<History*> history,
int width,
int height) {
const auto needAboutGroup = [&] {
if (const auto chat = history->peer->asChat()) {
return chat->amCreator();
} else if (const auto channel = history->peer->asMegagroup()) {
return channel->amCreator();
}
return false;
}();
if (needAboutGroup) {
PaintAboutGroup(p, width, height);
}
}
void serviceColorsUpdated() {
if (serviceMessageStyle) {
for (auto &corner : serviceMessageStyle->corners) {
@ -625,4 +540,119 @@ TextSelection Service::adjustSelection(
return message()->_text.adjustSelection(selection, type);
}
EmptyPainter::EmptyPainter(not_null<History*> history) : _history(history) {
if (NeedAboutGroup(_history)) {
fillAboutGroup();
}
}
void EmptyPainter::fillAboutGroup() {
const auto phrases = {
lang(lng_group_about1),
lang(lng_group_about2),
lang(lng_group_about3),
lang(lng_group_about4),
};
const auto setText = [](Text &text, const QString &content) {
text.setText(
st::serviceTextStyle,
content,
Ui::ItemTextServiceOptions());
};
setText(_header, lang(lng_group_about_header));
setText(_text, lang(lng_group_about_header));
for (const auto &text : phrases) {
_phrases.emplace_back(st::msgMinWidth);
setText(_phrases.back(), text);
}
}
void EmptyPainter::paint(Painter &p, int width, int height) {
if (_phrases.empty()) {
return;
}
constexpr auto kMaxTextLines = 3;
const auto maxPhraseWidth = ranges::max_element(
_phrases,
ranges::less(),
&Text::maxWidth
)->maxWidth();
const auto &font = st::serviceTextStyle.font;
const auto margin = st::msgMargin.left();
const auto maxBubbleWidth = width - 2 * st::historyGroupAboutMargin;
const auto padding = st::historyGroupAboutPadding;
const auto bubbleWidth = std::min(
maxBubbleWidth,
std::max({
maxPhraseWidth + st::historyGroupAboutBulletSkip,
_header.maxWidth(),
_text.maxWidth() }) + padding.left() + padding.right());
const auto innerWidth = bubbleWidth - padding.left() - padding.right();
const auto textHeight = [&](const Text &text) {
return std::min(
text.countHeight(innerWidth),
kMaxTextLines * font->height);
};
const auto bubbleHeight = padding.top()
+ textHeight(_header)
+ st::historyGroupAboutHeaderSkip
+ textHeight(_text)
+ st::historyGroupAboutTextSkip
+ ranges::accumulate(_phrases, 0, ranges::plus(), textHeight)
+ st::historyGroupAboutSkip * int(_phrases.size() - 1)
+ padding.bottom();
const auto bubbleLeft = (width - bubbleWidth) / 2;
const auto bubbleTop = (height - bubbleHeight) / 3;
ServiceMessagePainter::paintBubble(
p,
bubbleLeft,
bubbleTop,
bubbleWidth,
bubbleHeight);
p.setPen(st::msgServiceFg);
p.setBrush(st::msgServiceFg);
const auto left = bubbleLeft + padding.left();
auto top = bubbleTop + padding.top();
_header.drawElided(
p,
left,
top,
innerWidth,
kMaxTextLines,
style::al_top);
top += textHeight(_header) + st::historyGroupAboutHeaderSkip;
_text.drawElided(
p,
left,
top,
innerWidth,
kMaxTextLines);
top += textHeight(_text) + st::historyGroupAboutTextSkip;
for (const auto &text : _phrases) {
p.setPen(st::msgServiceFg);
text.drawElided(
p,
left + st::historyGroupAboutBulletSkip,
top,
innerWidth,
kMaxTextLines);
PainterHighQualityEnabler hq(p);
p.setPen(Qt::NoPen);
p.drawEllipse(
left,
top + (font->height - st::mediaUnreadSize) / 2,
st::mediaUnreadSize,
st::mediaUnreadSize);
top += textHeight(text) + st::historyGroupAboutSkip;
}
}
} // namespace HistoryView

View file

@ -74,11 +74,21 @@ private:
};
void paintEmpty(
Painter &p,
not_null<History*> history,
int width,
int height);
class EmptyPainter {
public:
explicit EmptyPainter(not_null<History*> history);
void paint(Painter &p, int width, int height);
private:
void fillAboutGroup();
not_null<History*> _history;
Text _header = { st::msgMinWidth };
Text _text = { st::msgMinWidth };
std::vector<Text> _phrases;
};
void serviceColorsUpdated();

View file

@ -352,7 +352,7 @@ private:
WebPageData *_page = nullptr;
int _pixw = 0;
int _pixh = 0;
Text _text = { int(st::msgMinWidth) };
Text _text = { st::msgMinWidth };
struct LinkEntry {
LinkEntry() : width(0) {