mirror of
https://github.com/telegramdesktop/tdesktop
synced 2025-03-25 04:38:23 +00:00
Move some classes to separate modules.
MessageField, BotKeyboard, HistoryInner from historywidget.
This commit is contained in:
parent
570cd9bdfa
commit
330fc35800
@ -32,6 +32,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
#include "boxes/confirm_box.h"
|
||||
#include "window/themes/window_theme.h"
|
||||
#include "window/notifications_manager.h"
|
||||
#include "chat_helpers/message_field.h"
|
||||
|
||||
namespace {
|
||||
|
||||
@ -1128,7 +1129,7 @@ void ApiWrap::saveDraftsToCloud() {
|
||||
if (!textWithTags.tags.isEmpty()) {
|
||||
flags |= MTPmessages_SaveDraft::Flag::f_entities;
|
||||
}
|
||||
auto entities = linksToMTP(entitiesFromTextTags(textWithTags.tags), true);
|
||||
auto entities = linksToMTP(ConvertTextTagsToEntities(textWithTags.tags), true);
|
||||
|
||||
cloudDraft->saveRequestId = request(MTPmessages_SaveDraft(MTP_flags(flags), MTP_int(cloudDraft->msgId), history->peer->input, MTP_string(textWithTags.text), entities)).done([this, history](const MTPBool &result, mtpRequestId requestId) {
|
||||
if (auto cloudDraft = history->cloudDraft()) {
|
||||
|
248
Telegram/SourceFiles/chat_helpers/bot_keyboard.cpp
Normal file
248
Telegram/SourceFiles/chat_helpers/bot_keyboard.cpp
Normal file
@ -0,0 +1,248 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
It is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
In addition, as a special exception, the copyright holders give permission
|
||||
to link the code of portions of this program with the OpenSSL library.
|
||||
|
||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#include "chat_helpers/bot_keyboard.h"
|
||||
|
||||
#include "styles/style_widgets.h"
|
||||
#include "styles/style_history.h"
|
||||
|
||||
BotKeyboard::BotKeyboard(QWidget *parent) : TWidget(parent)
|
||||
, _st(&st::botKbButton) {
|
||||
setGeometry(0, 0, _st->margin, st::botKbScroll.deltat);
|
||||
_height = st::botKbScroll.deltat;
|
||||
setMouseTracking(true);
|
||||
}
|
||||
|
||||
void BotKeyboard::paintEvent(QPaintEvent *e) {
|
||||
Painter p(this);
|
||||
|
||||
auto clip = e->rect();
|
||||
p.fillRect(clip, st::historyComposeAreaBg);
|
||||
|
||||
if (_impl) {
|
||||
int x = rtl() ? st::botKbScroll.width : _st->margin;
|
||||
p.translate(x, st::botKbScroll.deltat);
|
||||
_impl->paint(p, width(), clip.translated(-x, -st::botKbScroll.deltat), getms());
|
||||
}
|
||||
}
|
||||
|
||||
void BotKeyboard::Style::startPaint(Painter &p) const {
|
||||
p.setPen(st::botKbColor);
|
||||
p.setFont(st::botKbStyle.font);
|
||||
}
|
||||
|
||||
const style::TextStyle &BotKeyboard::Style::textStyle() const {
|
||||
return st::botKbStyle;
|
||||
}
|
||||
|
||||
void BotKeyboard::Style::repaint(const HistoryItem *item) const {
|
||||
_parent->update();
|
||||
}
|
||||
|
||||
int BotKeyboard::Style::buttonRadius() const {
|
||||
return st::buttonRadius;
|
||||
}
|
||||
|
||||
void BotKeyboard::Style::paintButtonBg(Painter &p, const QRect &rect, float64 howMuchOver) const {
|
||||
App::roundRect(p, rect, st::botKbBg, BotKeyboardCorners);
|
||||
}
|
||||
|
||||
void BotKeyboard::Style::paintButtonIcon(Painter &p, const QRect &rect, int outerWidth, HistoryMessageReplyMarkup::Button::Type type) const {
|
||||
// Buttons with icons should not appear here.
|
||||
}
|
||||
|
||||
void BotKeyboard::Style::paintButtonLoading(Painter &p, const QRect &rect) const {
|
||||
// Buttons with loading progress should not appear here.
|
||||
}
|
||||
|
||||
int BotKeyboard::Style::minButtonWidth(HistoryMessageReplyMarkup::Button::Type type) const {
|
||||
int result = 2 * buttonPadding();
|
||||
return result;
|
||||
}
|
||||
|
||||
void BotKeyboard::mousePressEvent(QMouseEvent *e) {
|
||||
_lastMousePos = e->globalPos();
|
||||
updateSelected();
|
||||
|
||||
ClickHandler::pressed();
|
||||
}
|
||||
|
||||
void BotKeyboard::mouseMoveEvent(QMouseEvent *e) {
|
||||
_lastMousePos = e->globalPos();
|
||||
updateSelected();
|
||||
}
|
||||
|
||||
void BotKeyboard::mouseReleaseEvent(QMouseEvent *e) {
|
||||
_lastMousePos = e->globalPos();
|
||||
updateSelected();
|
||||
|
||||
if (ClickHandlerPtr activated = ClickHandler::unpressed()) {
|
||||
App::activateClickHandler(activated, e->button());
|
||||
}
|
||||
}
|
||||
|
||||
void BotKeyboard::enterEventHook(QEvent *e) {
|
||||
_lastMousePos = QCursor::pos();
|
||||
updateSelected();
|
||||
}
|
||||
|
||||
void BotKeyboard::leaveEventHook(QEvent *e) {
|
||||
clearSelection();
|
||||
}
|
||||
|
||||
bool BotKeyboard::moderateKeyActivate(int key) {
|
||||
if (auto item = App::histItemById(_wasForMsgId)) {
|
||||
if (auto markup = item->Get<HistoryMessageReplyMarkup>()) {
|
||||
if (key >= Qt::Key_1 && key <= Qt::Key_9) {
|
||||
int index = (key - Qt::Key_1);
|
||||
if (!markup->rows.isEmpty() && index >= 0 && index < markup->rows.front().size()) {
|
||||
App::activateBotCommand(item, 0, index);
|
||||
return true;
|
||||
}
|
||||
} else if (key == Qt::Key_Q) {
|
||||
if (auto user = item->history()->peer->asUser()) {
|
||||
if (user->botInfo && item->from() == user) {
|
||||
App::sendBotCommand(user, user, qsl("/translate"));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void BotKeyboard::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) {
|
||||
if (!_impl) return;
|
||||
_impl->clickHandlerActiveChanged(p, active);
|
||||
}
|
||||
|
||||
void BotKeyboard::clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) {
|
||||
if (!_impl) return;
|
||||
_impl->clickHandlerPressedChanged(p, pressed);
|
||||
}
|
||||
|
||||
bool BotKeyboard::updateMarkup(HistoryItem *to, bool force) {
|
||||
if (!to || !to->definesReplyKeyboard()) {
|
||||
if (_wasForMsgId.msg) {
|
||||
_maximizeSize = _singleUse = _forceReply = false;
|
||||
_wasForMsgId = FullMsgId();
|
||||
_impl = nullptr;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_wasForMsgId == FullMsgId(to->channelId(), to->id) && !force) {
|
||||
return false;
|
||||
}
|
||||
|
||||
_wasForMsgId = FullMsgId(to->channelId(), to->id);
|
||||
|
||||
auto markupFlags = to->replyKeyboardFlags();
|
||||
_forceReply = markupFlags & MTPDreplyKeyboardMarkup_ClientFlag::f_force_reply;
|
||||
_maximizeSize = !(markupFlags & MTPDreplyKeyboardMarkup::Flag::f_resize);
|
||||
_singleUse = _forceReply || (markupFlags & MTPDreplyKeyboardMarkup::Flag::f_single_use);
|
||||
|
||||
_impl = nullptr;
|
||||
if (auto markup = to->Get<HistoryMessageReplyMarkup>()) {
|
||||
if (!markup->rows.isEmpty()) {
|
||||
_impl.reset(new ReplyKeyboard(to, std::make_unique<Style>(this, *_st)));
|
||||
}
|
||||
}
|
||||
|
||||
resizeToWidth(width(), _maxOuterHeight);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BotKeyboard::hasMarkup() const {
|
||||
return _impl != nullptr;
|
||||
}
|
||||
|
||||
bool BotKeyboard::forceReply() const {
|
||||
return _forceReply;
|
||||
}
|
||||
|
||||
int BotKeyboard::resizeGetHeight(int newWidth) {
|
||||
updateStyle(newWidth);
|
||||
_height = st::botKbScroll.deltat + st::botKbScroll.deltab + (_impl ? _impl->naturalHeight() : 0);
|
||||
if (_maximizeSize) {
|
||||
accumulate_max(_height, _maxOuterHeight);
|
||||
}
|
||||
if (_impl) {
|
||||
int implWidth = newWidth - _st->margin - st::botKbScroll.width;
|
||||
int implHeight = _height - (st::botKbScroll.deltat + st::botKbScroll.deltab);
|
||||
_impl->resize(implWidth, implHeight);
|
||||
}
|
||||
return _height;
|
||||
}
|
||||
|
||||
bool BotKeyboard::maximizeSize() const {
|
||||
return _maximizeSize;
|
||||
}
|
||||
|
||||
bool BotKeyboard::singleUse() const {
|
||||
return _singleUse;
|
||||
}
|
||||
|
||||
void BotKeyboard::updateStyle(int newWidth) {
|
||||
if (!_impl) return;
|
||||
|
||||
int implWidth = newWidth - st::botKbButton.margin - st::botKbScroll.width;
|
||||
_st = _impl->isEnoughSpace(implWidth, st::botKbButton) ? &st::botKbButton : &st::botKbTinyButton;
|
||||
|
||||
_impl->setStyle(std::make_unique<Style>(this, *_st));
|
||||
}
|
||||
|
||||
void BotKeyboard::clearSelection() {
|
||||
if (_impl) {
|
||||
if (ClickHandler::setActive(ClickHandlerPtr(), this)) {
|
||||
Ui::Tooltip::Hide();
|
||||
setCursor(style::cur_default);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QPoint BotKeyboard::tooltipPos() const {
|
||||
return _lastMousePos;
|
||||
}
|
||||
|
||||
QString BotKeyboard::tooltipText() const {
|
||||
if (ClickHandlerPtr lnk = ClickHandler::getActive()) {
|
||||
return lnk->tooltip();
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
void BotKeyboard::updateSelected() {
|
||||
Ui::Tooltip::Show(1000, this);
|
||||
|
||||
if (!_impl) return;
|
||||
|
||||
QPoint p(mapFromGlobal(_lastMousePos));
|
||||
int x = rtl() ? st::botKbScroll.width : _st->margin;
|
||||
|
||||
auto link = _impl->getState(p.x() - x, p.y() - _st->margin);
|
||||
if (ClickHandler::setActive(link, this)) {
|
||||
Ui::Tooltip::Hide();
|
||||
setCursor(link ? style::cur_pointer : style::cur_default);
|
||||
}
|
||||
}
|
109
Telegram/SourceFiles/chat_helpers/bot_keyboard.h
Normal file
109
Telegram/SourceFiles/chat_helpers/bot_keyboard.h
Normal file
@ -0,0 +1,109 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
It is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
In addition, as a special exception, the copyright holders give permission
|
||||
to link the code of portions of this program with the OpenSSL library.
|
||||
|
||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "ui/widgets/tooltip.h"
|
||||
|
||||
class BotKeyboard : public TWidget, public Ui::AbstractTooltipShower, public ClickHandlerHost {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
BotKeyboard(QWidget *parent);
|
||||
|
||||
bool moderateKeyActivate(int index);
|
||||
|
||||
// With force=true the markup is updated even if it is
|
||||
// already shown for the passed history item.
|
||||
bool updateMarkup(HistoryItem *last, bool force = false);
|
||||
bool hasMarkup() const;
|
||||
bool forceReply() const;
|
||||
|
||||
void step_selected(TimeMs ms, bool timer);
|
||||
void resizeToWidth(int newWidth, int maxOuterHeight) {
|
||||
_maxOuterHeight = maxOuterHeight;
|
||||
return TWidget::resizeToWidth(newWidth);
|
||||
}
|
||||
|
||||
bool maximizeSize() const;
|
||||
bool singleUse() const;
|
||||
|
||||
FullMsgId forMsgId() const {
|
||||
return _wasForMsgId;
|
||||
}
|
||||
|
||||
// AbstractTooltipShower interface
|
||||
QString tooltipText() const override;
|
||||
QPoint tooltipPos() const override;
|
||||
|
||||
// ClickHandlerHost interface
|
||||
void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override;
|
||||
void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) override;
|
||||
|
||||
protected:
|
||||
int resizeGetHeight(int newWidth) override;
|
||||
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
void mousePressEvent(QMouseEvent *e) override;
|
||||
void mouseMoveEvent(QMouseEvent *e) override;
|
||||
void mouseReleaseEvent(QMouseEvent *e) override;
|
||||
void enterEventHook(QEvent *e) override;
|
||||
void leaveEventHook(QEvent *e) override;
|
||||
|
||||
private:
|
||||
void updateSelected();
|
||||
|
||||
void updateStyle(int newWidth);
|
||||
void clearSelection();
|
||||
|
||||
FullMsgId _wasForMsgId;
|
||||
int _height = 0;
|
||||
int _maxOuterHeight = 0;
|
||||
bool _maximizeSize = false;
|
||||
bool _singleUse = false;
|
||||
bool _forceReply = false;
|
||||
|
||||
QPoint _lastMousePos;
|
||||
std::unique_ptr<ReplyKeyboard> _impl;
|
||||
|
||||
class Style : public ReplyKeyboard::Style {
|
||||
public:
|
||||
Style(BotKeyboard *parent, const style::BotKeyboardButton &st) : ReplyKeyboard::Style(st), _parent(parent) {
|
||||
}
|
||||
|
||||
int buttonRadius() const override;
|
||||
|
||||
void startPaint(Painter &p) const override;
|
||||
const style::TextStyle &textStyle() const override;
|
||||
void repaint(const HistoryItem *item) const override;
|
||||
|
||||
protected:
|
||||
void paintButtonBg(Painter &p, const QRect &rect, float64 howMuchOver) const override;
|
||||
void paintButtonIcon(Painter &p, const QRect &rect, int outerWidth, HistoryMessageReplyMarkup::Button::Type type) const override;
|
||||
void paintButtonLoading(Painter &p, const QRect &rect) const override;
|
||||
int minButtonWidth(HistoryMessageReplyMarkup::Button::Type type) const override;
|
||||
|
||||
private:
|
||||
BotKeyboard *_parent;
|
||||
|
||||
};
|
||||
const style::BotKeyboardButton *_st = nullptr;
|
||||
|
||||
};
|
152
Telegram/SourceFiles/chat_helpers/message_field.cpp
Normal file
152
Telegram/SourceFiles/chat_helpers/message_field.cpp
Normal file
@ -0,0 +1,152 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
It is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
In addition, as a special exception, the copyright holders give permission
|
||||
to link the code of portions of this program with the OpenSSL library.
|
||||
|
||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#include "chat_helpers/message_field.h"
|
||||
|
||||
#include "historywidget.h"
|
||||
#include "base/qthelp_regex.h"
|
||||
#include "styles/style_history.h"
|
||||
#include "window/window_controller.h"
|
||||
#include "mainwindow.h"
|
||||
#include "auth_session.h"
|
||||
|
||||
namespace {
|
||||
|
||||
// For mention tags save and validate userId, ignore tags for different userId.
|
||||
class FieldTagMimeProcessor : public Ui::FlatTextarea::TagMimeProcessor {
|
||||
public:
|
||||
QString mimeTagFromTag(const QString &tagId) override {
|
||||
return ConvertTagToMimeTag(tagId);
|
||||
}
|
||||
|
||||
QString tagFromMimeTag(const QString &mimeTag) override {
|
||||
if (mimeTag.startsWith(qstr("mention://"))) {
|
||||
auto match = QRegularExpression(":(\\d+)$").match(mimeTag);
|
||||
if (!match.hasMatch() || match.capturedRef(1).toInt() != AuthSession::CurrentUserId()) {
|
||||
return QString();
|
||||
}
|
||||
return mimeTag.mid(0, mimeTag.size() - match.capturedLength());
|
||||
}
|
||||
return mimeTag;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
QString ConvertTagToMimeTag(const QString &tagId) {
|
||||
if (tagId.startsWith(qstr("mention://"))) {
|
||||
return tagId + ':' + QString::number(AuthSession::CurrentUserId());
|
||||
}
|
||||
return tagId;
|
||||
}
|
||||
|
||||
EntitiesInText ConvertTextTagsToEntities(const TextWithTags::Tags &tags) {
|
||||
EntitiesInText result;
|
||||
if (tags.isEmpty()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
result.reserve(tags.size());
|
||||
auto mentionStart = qstr("mention://user.");
|
||||
for_const (auto &tag, tags) {
|
||||
if (tag.id.startsWith(mentionStart)) {
|
||||
if (auto match = qthelp::regex_match("^(\\d+\\.\\d+)(/|$)", tag.id.midRef(mentionStart.size()))) {
|
||||
result.push_back(EntityInText(EntityInTextMentionName, tag.offset, tag.length, match->captured(1)));
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
TextWithTags::Tags ConvertEntitiesToTextTags(const EntitiesInText &entities) {
|
||||
TextWithTags::Tags result;
|
||||
if (entities.isEmpty()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
result.reserve(entities.size());
|
||||
for_const (auto &entity, entities) {
|
||||
if (entity.type() == EntityInTextMentionName) {
|
||||
auto match = QRegularExpression("^(\\d+\\.\\d+)$").match(entity.data());
|
||||
if (match.hasMatch()) {
|
||||
result.push_back({ entity.offset(), entity.length(), qstr("mention://user.") + entity.data() });
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
MessageField::MessageField(QWidget *parent, gsl::not_null<Window::Controller*> controller, const style::FlatTextarea &st, const QString &ph, const QString &val) : Ui::FlatTextarea(parent, st, ph, val)
|
||||
, _controller(controller) {
|
||||
setMinHeight(st::historySendSize.height() - 2 * st::historySendPadding);
|
||||
setMaxHeight(st::historyComposeFieldMaxHeight);
|
||||
|
||||
setTagMimeProcessor(std::make_unique<FieldTagMimeProcessor>());
|
||||
}
|
||||
|
||||
bool MessageField::hasSendText() const {
|
||||
auto &text = getTextWithTags().text;
|
||||
for (auto *ch = text.constData(), *e = ch + text.size(); ch != e; ++ch) {
|
||||
auto code = ch->unicode();
|
||||
if (code != ' ' && code != '\n' && code != '\r' && !chReplacedBySpace(code)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void MessageField::onEmojiInsert(EmojiPtr emoji) {
|
||||
if (isHidden()) return;
|
||||
insertEmoji(emoji, textCursor());
|
||||
}
|
||||
|
||||
void MessageField::dropEvent(QDropEvent *e) {
|
||||
FlatTextarea::dropEvent(e);
|
||||
if (e->isAccepted()) {
|
||||
_controller->window()->activateWindow();
|
||||
}
|
||||
}
|
||||
|
||||
bool MessageField::canInsertFromMimeData(const QMimeData *source) const {
|
||||
if (source->hasUrls()) {
|
||||
int32 files = 0;
|
||||
for (int32 i = 0; i < source->urls().size(); ++i) {
|
||||
if (source->urls().at(i).isLocalFile()) {
|
||||
++files;
|
||||
}
|
||||
}
|
||||
if (files > 1) return false; // multiple confirm with "compressed" checkbox
|
||||
}
|
||||
if (source->hasImage()) return true;
|
||||
return FlatTextarea::canInsertFromMimeData(source);
|
||||
}
|
||||
|
||||
void MessageField::insertFromMimeData(const QMimeData *source) {
|
||||
if (_insertFromMimeDataHook && _insertFromMimeDataHook(source)) {
|
||||
return;
|
||||
}
|
||||
FlatTextarea::insertFromMimeData(source);
|
||||
}
|
||||
|
||||
void MessageField::focusInEvent(QFocusEvent *e) {
|
||||
FlatTextarea::focusInEvent(e);
|
||||
emit focused();
|
||||
}
|
63
Telegram/SourceFiles/chat_helpers/message_field.h
Normal file
63
Telegram/SourceFiles/chat_helpers/message_field.h
Normal file
@ -0,0 +1,63 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
It is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
In addition, as a special exception, the copyright holders give permission
|
||||
to link the code of portions of this program with the OpenSSL library.
|
||||
|
||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "ui/widgets/input_fields.h"
|
||||
|
||||
class HistoryWidget;
|
||||
namespace Window {
|
||||
class Controller;
|
||||
} // namespace Window
|
||||
|
||||
QString ConvertTagToMimeTag(const QString &tagId);
|
||||
|
||||
EntitiesInText ConvertTextTagsToEntities(const TextWithTags::Tags &tags);
|
||||
TextWithTags::Tags ConvertEntitiesToTextTags(const EntitiesInText &entities);
|
||||
|
||||
class MessageField final : public Ui::FlatTextarea {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MessageField(QWidget *parent, gsl::not_null<Window::Controller*> controller, const style::FlatTextarea &st, const QString &ph = QString(), const QString &val = QString());
|
||||
|
||||
bool hasSendText() const;
|
||||
|
||||
void setInsertFromMimeDataHook(base::lambda<bool(const QMimeData *data)> hook) {
|
||||
_insertFromMimeDataHook = std::move(hook);
|
||||
}
|
||||
|
||||
public slots:
|
||||
void onEmojiInsert(EmojiPtr emoji);
|
||||
|
||||
signals:
|
||||
void focused();
|
||||
|
||||
protected:
|
||||
void focusInEvent(QFocusEvent *e) override;
|
||||
void dropEvent(QDropEvent *e) override;
|
||||
bool canInsertFromMimeData(const QMimeData *source) const override;
|
||||
void insertFromMimeData(const QMimeData *source) override;
|
||||
|
||||
private:
|
||||
gsl::not_null<Window::Controller*> _controller;
|
||||
base::lambda<bool(const QMimeData *data)> _insertFromMimeDataHook;
|
||||
|
||||
};
|
@ -21,6 +21,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
#include "data/data_drafts.h"
|
||||
|
||||
#include "ui/widgets/input_fields.h"
|
||||
#include "chat_helpers/message_field.h"
|
||||
#include "historywidget.h"
|
||||
#include "mainwidget.h"
|
||||
#include "storage/localstorage.h"
|
||||
@ -41,7 +42,7 @@ void applyPeerCloudDraft(PeerId peerId, const MTPDdraftMessage &draft) {
|
||||
auto history = App::history(peerId);
|
||||
auto text = qs(draft.vmessage);
|
||||
auto entities = draft.has_entities() ? entitiesFromMTP(draft.ventities.v) : EntitiesInText();
|
||||
TextWithTags textWithTags = { textApplyEntities(text, entities), textTagsFromEntities(entities) };
|
||||
TextWithTags textWithTags = { textApplyEntities(text, entities), ConvertEntitiesToTextTags(entities) };
|
||||
MsgId replyTo = draft.has_reply_to_msg_id() ? draft.vreply_to_msg_id.v : 0;
|
||||
auto cloudDraft = std::make_unique<Draft>(textWithTags, replyTo, MessageCursor(QFIXED_MAX, QFIXED_MAX, QFIXED_MAX), draft.is_no_webpage());
|
||||
cloudDraft->date = ::date(draft.vdate);
|
||||
|
2465
Telegram/SourceFiles/history/history_inner_widget.cpp
Normal file
2465
Telegram/SourceFiles/history/history_inner_widget.cpp
Normal file
File diff suppressed because it is too large
Load Diff
303
Telegram/SourceFiles/history/history_inner_widget.h
Normal file
303
Telegram/SourceFiles/history/history_inner_widget.h
Normal file
@ -0,0 +1,303 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
It is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
In addition, as a special exception, the copyright holders give permission
|
||||
to link the code of portions of this program with the OpenSSL library.
|
||||
|
||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "ui/widgets/tooltip.h"
|
||||
#include "ui/widgets/scroll_area.h"
|
||||
|
||||
namespace Window {
|
||||
class Controller;
|
||||
} // namespace Window
|
||||
|
||||
namespace Ui {
|
||||
class PopupMenu;
|
||||
} // namespace Ui
|
||||
|
||||
class HistoryWidget;
|
||||
class HistoryInner : public TWidget, public Ui::AbstractTooltipShower, private base::Subscriber {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
HistoryInner(HistoryWidget *historyWidget, gsl::not_null<Window::Controller*> controller, Ui::ScrollArea *scroll, History *history);
|
||||
|
||||
void messagesReceived(PeerData *peer, const QVector<MTPMessage> &messages);
|
||||
void messagesReceivedDown(PeerData *peer, const QVector<MTPMessage> &messages);
|
||||
|
||||
void showContextMenu(QContextMenuEvent *e, bool showFromTouch = false);
|
||||
|
||||
TextWithEntities getSelectedText() const;
|
||||
|
||||
void dragActionStart(const QPoint &screenPos, Qt::MouseButton button = Qt::LeftButton);
|
||||
void dragActionUpdate(const QPoint &screenPos);
|
||||
void dragActionFinish(const QPoint &screenPos, Qt::MouseButton button = Qt::LeftButton);
|
||||
void dragActionCancel();
|
||||
|
||||
void touchScrollUpdated(const QPoint &screenPos);
|
||||
QPoint mapMouseToItem(QPoint p, HistoryItem *item);
|
||||
|
||||
void recountHeight();
|
||||
void updateSize();
|
||||
|
||||
void repaintItem(const HistoryItem *item);
|
||||
|
||||
bool canCopySelected() const;
|
||||
bool canDeleteSelected() const;
|
||||
|
||||
void getSelectionState(int32 &selectedForForward, int32 &selectedForDelete) const;
|
||||
void clearSelectedItems(bool onlyTextSelection = false);
|
||||
void fillSelectedItems(SelectedItemSet &sel, bool forDelete = true);
|
||||
void selectItem(HistoryItem *item);
|
||||
|
||||
void updateBotInfo(bool recount = true);
|
||||
|
||||
bool wasSelectedText() const;
|
||||
void setFirstLoading(bool loading);
|
||||
|
||||
// updates history->scrollTopItem/scrollTopOffset
|
||||
void visibleAreaUpdated(int top, int bottom);
|
||||
|
||||
int historyHeight() const;
|
||||
int historyScrollTop() const;
|
||||
int migratedTop() const;
|
||||
int historyTop() const;
|
||||
int historyDrawTop() const;
|
||||
int itemTop(const HistoryItem *item) const; // -1 if should not be visible, -2 if bad history()
|
||||
|
||||
void notifyIsBotChanged();
|
||||
void notifyMigrateUpdated();
|
||||
|
||||
// When inline keyboard has moved because of the edition of its item we want
|
||||
// to move scroll position so that mouse points to the same button row.
|
||||
int moveScrollFollowingInlineKeyboard(const HistoryItem *item, int oldKeyboardTop, int newKeyboardTop);
|
||||
|
||||
// AbstractTooltipShower interface
|
||||
QString tooltipText() const override;
|
||||
QPoint tooltipPos() const override;
|
||||
|
||||
~HistoryInner();
|
||||
|
||||
protected:
|
||||
bool focusNextPrevChild(bool next) override;
|
||||
|
||||
bool event(QEvent *e) override; // calls touchEvent when necessary
|
||||
void touchEvent(QTouchEvent *e);
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
void mouseMoveEvent(QMouseEvent *e) override;
|
||||
void mousePressEvent(QMouseEvent *e) override;
|
||||
void mouseReleaseEvent(QMouseEvent *e) override;
|
||||
void mouseDoubleClickEvent(QMouseEvent *e) override;
|
||||
void enterEventHook(QEvent *e) override;
|
||||
void leaveEventHook(QEvent *e) override;
|
||||
void resizeEvent(QResizeEvent *e) override;
|
||||
void keyPressEvent(QKeyEvent *e) override;
|
||||
|
||||
public slots:
|
||||
void onUpdateSelected();
|
||||
void onParentGeometryChanged();
|
||||
|
||||
void copyContextUrl();
|
||||
void copyContextImage();
|
||||
void cancelContextDownload();
|
||||
void showContextInFolder();
|
||||
void saveContextGif();
|
||||
void copyContextText();
|
||||
void copySelectedText();
|
||||
|
||||
void onMenuDestroy(QObject *obj);
|
||||
void onTouchSelect();
|
||||
void onTouchScrollTimer();
|
||||
void onDragExec();
|
||||
|
||||
private slots:
|
||||
void onScrollDateCheck();
|
||||
void onScrollDateHideByTimer();
|
||||
|
||||
private:
|
||||
void itemRemoved(HistoryItem *item);
|
||||
void savePhotoToFile(PhotoData *photo);
|
||||
void saveDocumentToFile(DocumentData *document);
|
||||
|
||||
void touchResetSpeed();
|
||||
void touchUpdateSpeed();
|
||||
void touchDeaccelerate(int32 elapsed);
|
||||
|
||||
void adjustCurrent(int32 y) const;
|
||||
void adjustCurrent(int32 y, History *history) const;
|
||||
HistoryItem *prevItem(HistoryItem *item);
|
||||
HistoryItem *nextItem(HistoryItem *item);
|
||||
void updateDragSelection(HistoryItem *dragSelFrom, HistoryItem *dragSelTo, bool dragSelecting, bool force = false);
|
||||
|
||||
void setToClipboard(const TextWithEntities &forClipboard, QClipboard::Mode mode = QClipboard::Clipboard);
|
||||
|
||||
void toggleScrollDateShown();
|
||||
void repaintScrollDateCallback();
|
||||
bool displayScrollDate() const;
|
||||
void scrollDateHide();
|
||||
void keepScrollDateForNow();
|
||||
|
||||
gsl::not_null<Window::Controller*> _controller;
|
||||
|
||||
PeerData *_peer = nullptr;
|
||||
History *_migrated = nullptr;
|
||||
History *_history = nullptr;
|
||||
int _historyPaddingTop = 0;
|
||||
|
||||
// with migrated history we perhaps do not need to display first _history message
|
||||
// (if last _migrated message and first _history message are both isGroupMigrate)
|
||||
// or at least we don't need to display first _history date (just skip it by height)
|
||||
int _historySkipHeight = 0;
|
||||
|
||||
class BotAbout : public ClickHandlerHost {
|
||||
public:
|
||||
BotAbout(HistoryInner *parent, BotInfo *info) : info(info), _parent(parent) {
|
||||
}
|
||||
BotInfo *info = nullptr;
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
QRect rect;
|
||||
|
||||
// ClickHandlerHost interface
|
||||
void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override;
|
||||
void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) override;
|
||||
|
||||
private:
|
||||
HistoryInner *_parent;
|
||||
|
||||
};
|
||||
std::unique_ptr<BotAbout> _botAbout;
|
||||
|
||||
HistoryWidget *_widget = nullptr;
|
||||
Ui::ScrollArea *_scroll = nullptr;
|
||||
mutable History *_curHistory = nullptr;
|
||||
mutable int _curBlock = 0;
|
||||
mutable int _curItem = 0;
|
||||
|
||||
bool _firstLoading = false;
|
||||
|
||||
style::cursor _cursor = style::cur_default;
|
||||
using SelectedItems = QMap<HistoryItem*, TextSelection>;
|
||||
SelectedItems _selected;
|
||||
void applyDragSelection();
|
||||
void applyDragSelection(SelectedItems *toItems) const;
|
||||
void addSelectionRange(SelectedItems *toItems, int32 fromblock, int32 fromitem, int32 toblock, int32 toitem, History *h) const;
|
||||
|
||||
// Does any of the shown histories has this flag set.
|
||||
bool hasPendingResizedItems() const {
|
||||
return (_history && _history->hasPendingResizedItems()) || (_migrated && _migrated->hasPendingResizedItems());
|
||||
}
|
||||
|
||||
enum DragAction {
|
||||
NoDrag = 0x00,
|
||||
PrepareDrag = 0x01,
|
||||
Dragging = 0x02,
|
||||
PrepareSelect = 0x03,
|
||||
Selecting = 0x04,
|
||||
};
|
||||
DragAction _dragAction = NoDrag;
|
||||
TextSelectType _dragSelType = TextSelectType::Letters;
|
||||
QPoint _dragStartPos, _dragPos;
|
||||
HistoryItem *_dragItem = nullptr;
|
||||
HistoryCursorState _dragCursorState = HistoryDefaultCursorState;
|
||||
uint16 _dragSymbol = 0;
|
||||
bool _dragWasInactive = false;
|
||||
|
||||
QPoint _trippleClickPoint;
|
||||
QTimer _trippleClickTimer;
|
||||
|
||||
ClickHandlerPtr _contextMenuLnk;
|
||||
|
||||
HistoryItem *_dragSelFrom = nullptr;
|
||||
HistoryItem *_dragSelTo = nullptr;
|
||||
bool _dragSelecting = false;
|
||||
bool _wasSelectedText = false; // was some text selected in current drag action
|
||||
|
||||
// scroll by touch support (at least Windows Surface tablets)
|
||||
bool _touchScroll = false;
|
||||
bool _touchSelect = false;
|
||||
bool _touchInProgress = false;
|
||||
QPoint _touchStart, _touchPrevPos, _touchPos;
|
||||
QTimer _touchSelectTimer;
|
||||
|
||||
Ui::TouchScrollState _touchScrollState = Ui::TouchScrollState::Manual;
|
||||
bool _touchPrevPosValid = false;
|
||||
bool _touchWaitingAcceleration = false;
|
||||
QPoint _touchSpeed;
|
||||
TimeMs _touchSpeedTime = 0;
|
||||
TimeMs _touchAccelerationTime = 0;
|
||||
TimeMs _touchTime = 0;
|
||||
QTimer _touchScrollTimer;
|
||||
|
||||
// context menu
|
||||
Ui::PopupMenu *_menu = nullptr;
|
||||
|
||||
// save visible area coords for painting / pressing userpics
|
||||
int _visibleAreaTop = 0;
|
||||
int _visibleAreaBottom = 0;
|
||||
|
||||
bool _scrollDateShown = false;
|
||||
Animation _scrollDateOpacity;
|
||||
SingleQueuedInvokation _scrollDateCheck;
|
||||
SingleTimer _scrollDateHideTimer;
|
||||
HistoryItem *_scrollDateLastItem = nullptr;
|
||||
int _scrollDateLastItemTop = 0;
|
||||
ClickHandlerPtr _scrollDateLink;
|
||||
|
||||
enum class EnumItemsDirection {
|
||||
TopToBottom,
|
||||
BottomToTop,
|
||||
};
|
||||
// this function finds all history items that are displayed and calls template method
|
||||
// for each found message (in given direction) in the passed history with passed top offset
|
||||
//
|
||||
// method has "bool (*Method)(HistoryItem *item, int itemtop, int itembottom)" signature
|
||||
// if it returns false the enumeration stops immidiately
|
||||
template <bool TopToBottom, typename Method>
|
||||
void enumerateItemsInHistory(History *history, int historytop, Method method);
|
||||
|
||||
template <EnumItemsDirection direction, typename Method>
|
||||
void enumerateItems(Method method) {
|
||||
constexpr auto TopToBottom = (direction == EnumItemsDirection::TopToBottom);
|
||||
if (TopToBottom && _migrated) {
|
||||
enumerateItemsInHistory<TopToBottom>(_migrated, migratedTop(), method);
|
||||
}
|
||||
enumerateItemsInHistory<TopToBottom>(_history, historyTop(), method);
|
||||
if (!TopToBottom && _migrated) {
|
||||
enumerateItemsInHistory<TopToBottom>(_migrated, migratedTop(), method);
|
||||
}
|
||||
}
|
||||
|
||||
// this function finds all userpics on the left that are displayed and calls template method
|
||||
// for each found userpic (from the top to the bottom) using enumerateItems() method
|
||||
//
|
||||
// method has "bool (*Method)(HistoryMessage *message, int userpicTop)" signature
|
||||
// if it returns false the enumeration stops immidiately
|
||||
template <typename Method>
|
||||
void enumerateUserpics(Method method);
|
||||
|
||||
// this function finds all date elements that are displayed and calls template method
|
||||
// for each found date element (from the bottom to the top) using enumerateItems() method
|
||||
//
|
||||
// method has "bool (*Method)(HistoryItem *item, int itemtop, int dateTop)" signature
|
||||
// if it returns false the enumeration stops immidiately
|
||||
template <typename Method>
|
||||
void enumerateDates(Method method);
|
||||
|
||||
};
|
File diff suppressed because it is too large
Load Diff
@ -22,12 +22,11 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
|
||||
#include "storage/localimageloader.h"
|
||||
#include "ui/widgets/tooltip.h"
|
||||
#include "ui/widgets/input_fields.h"
|
||||
#include "ui/widgets/scroll_area.h"
|
||||
#include "history/history_common.h"
|
||||
#include "chat_helpers/field_autocomplete.h"
|
||||
#include "window/section_widget.h"
|
||||
#include "core/single_timer.h"
|
||||
#include "ui/widgets/input_fields.h"
|
||||
|
||||
namespace InlineBots {
|
||||
namespace Layout {
|
||||
@ -64,301 +63,9 @@ class EmojiPanel;
|
||||
class DragArea;
|
||||
class SilentToggle;
|
||||
class SendFilesBox;
|
||||
|
||||
class HistoryWidget;
|
||||
class HistoryInner : public TWidget, public Ui::AbstractTooltipShower, private base::Subscriber {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
HistoryInner(HistoryWidget *historyWidget, gsl::not_null<Window::Controller*> controller, Ui::ScrollArea *scroll, History *history);
|
||||
|
||||
void messagesReceived(PeerData *peer, const QVector<MTPMessage> &messages);
|
||||
void messagesReceivedDown(PeerData *peer, const QVector<MTPMessage> &messages);
|
||||
|
||||
void showContextMenu(QContextMenuEvent *e, bool showFromTouch = false);
|
||||
|
||||
TextWithEntities getSelectedText() const;
|
||||
|
||||
void dragActionStart(const QPoint &screenPos, Qt::MouseButton button = Qt::LeftButton);
|
||||
void dragActionUpdate(const QPoint &screenPos);
|
||||
void dragActionFinish(const QPoint &screenPos, Qt::MouseButton button = Qt::LeftButton);
|
||||
void dragActionCancel();
|
||||
|
||||
void touchScrollUpdated(const QPoint &screenPos);
|
||||
QPoint mapMouseToItem(QPoint p, HistoryItem *item);
|
||||
|
||||
void recountHeight();
|
||||
void updateSize();
|
||||
|
||||
void repaintItem(const HistoryItem *item);
|
||||
|
||||
bool canCopySelected() const;
|
||||
bool canDeleteSelected() const;
|
||||
|
||||
void getSelectionState(int32 &selectedForForward, int32 &selectedForDelete) const;
|
||||
void clearSelectedItems(bool onlyTextSelection = false);
|
||||
void fillSelectedItems(SelectedItemSet &sel, bool forDelete = true);
|
||||
void selectItem(HistoryItem *item);
|
||||
|
||||
void updateBotInfo(bool recount = true);
|
||||
|
||||
bool wasSelectedText() const;
|
||||
void setFirstLoading(bool loading);
|
||||
|
||||
// updates history->scrollTopItem/scrollTopOffset
|
||||
void visibleAreaUpdated(int top, int bottom);
|
||||
|
||||
int historyHeight() const;
|
||||
int historyScrollTop() const;
|
||||
int migratedTop() const;
|
||||
int historyTop() const;
|
||||
int historyDrawTop() const;
|
||||
int itemTop(const HistoryItem *item) const; // -1 if should not be visible, -2 if bad history()
|
||||
|
||||
void notifyIsBotChanged();
|
||||
void notifyMigrateUpdated();
|
||||
|
||||
// When inline keyboard has moved because of the edition of its item we want
|
||||
// to move scroll position so that mouse points to the same button row.
|
||||
int moveScrollFollowingInlineKeyboard(const HistoryItem *item, int oldKeyboardTop, int newKeyboardTop);
|
||||
|
||||
// AbstractTooltipShower interface
|
||||
QString tooltipText() const override;
|
||||
QPoint tooltipPos() const override;
|
||||
|
||||
~HistoryInner();
|
||||
|
||||
protected:
|
||||
bool focusNextPrevChild(bool next) override;
|
||||
|
||||
bool event(QEvent *e) override; // calls touchEvent when necessary
|
||||
void touchEvent(QTouchEvent *e);
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
void mouseMoveEvent(QMouseEvent *e) override;
|
||||
void mousePressEvent(QMouseEvent *e) override;
|
||||
void mouseReleaseEvent(QMouseEvent *e) override;
|
||||
void mouseDoubleClickEvent(QMouseEvent *e) override;
|
||||
void enterEventHook(QEvent *e) override;
|
||||
void leaveEventHook(QEvent *e) override;
|
||||
void resizeEvent(QResizeEvent *e) override;
|
||||
void keyPressEvent(QKeyEvent *e) override;
|
||||
|
||||
public slots:
|
||||
void onUpdateSelected();
|
||||
void onParentGeometryChanged();
|
||||
|
||||
void copyContextUrl();
|
||||
void copyContextImage();
|
||||
void cancelContextDownload();
|
||||
void showContextInFolder();
|
||||
void saveContextGif();
|
||||
void copyContextText();
|
||||
void copySelectedText();
|
||||
|
||||
void onMenuDestroy(QObject *obj);
|
||||
void onTouchSelect();
|
||||
void onTouchScrollTimer();
|
||||
void onDragExec();
|
||||
|
||||
private slots:
|
||||
void onScrollDateCheck();
|
||||
void onScrollDateHideByTimer();
|
||||
|
||||
private:
|
||||
void itemRemoved(HistoryItem *item);
|
||||
void savePhotoToFile(PhotoData *photo);
|
||||
void saveDocumentToFile(DocumentData *document);
|
||||
|
||||
void touchResetSpeed();
|
||||
void touchUpdateSpeed();
|
||||
void touchDeaccelerate(int32 elapsed);
|
||||
|
||||
void adjustCurrent(int32 y) const;
|
||||
void adjustCurrent(int32 y, History *history) const;
|
||||
HistoryItem *prevItem(HistoryItem *item);
|
||||
HistoryItem *nextItem(HistoryItem *item);
|
||||
void updateDragSelection(HistoryItem *dragSelFrom, HistoryItem *dragSelTo, bool dragSelecting, bool force = false);
|
||||
|
||||
void setToClipboard(const TextWithEntities &forClipboard, QClipboard::Mode mode = QClipboard::Clipboard);
|
||||
|
||||
void toggleScrollDateShown();
|
||||
void repaintScrollDateCallback();
|
||||
bool displayScrollDate() const;
|
||||
void scrollDateHide();
|
||||
void keepScrollDateForNow();
|
||||
|
||||
gsl::not_null<Window::Controller*> _controller;
|
||||
|
||||
PeerData *_peer = nullptr;
|
||||
History *_migrated = nullptr;
|
||||
History *_history = nullptr;
|
||||
int _historyPaddingTop = 0;
|
||||
|
||||
// with migrated history we perhaps do not need to display first _history message
|
||||
// (if last _migrated message and first _history message are both isGroupMigrate)
|
||||
// or at least we don't need to display first _history date (just skip it by height)
|
||||
int _historySkipHeight = 0;
|
||||
|
||||
class BotAbout : public ClickHandlerHost {
|
||||
public:
|
||||
BotAbout(HistoryInner *parent, BotInfo *info) : info(info), _parent(parent) {
|
||||
}
|
||||
BotInfo *info = nullptr;
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
QRect rect;
|
||||
|
||||
// ClickHandlerHost interface
|
||||
void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override;
|
||||
void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) override;
|
||||
|
||||
private:
|
||||
HistoryInner *_parent;
|
||||
|
||||
};
|
||||
std::unique_ptr<BotAbout> _botAbout;
|
||||
|
||||
HistoryWidget *_widget = nullptr;
|
||||
Ui::ScrollArea *_scroll = nullptr;
|
||||
mutable History *_curHistory = nullptr;
|
||||
mutable int _curBlock = 0;
|
||||
mutable int _curItem = 0;
|
||||
|
||||
bool _firstLoading = false;
|
||||
|
||||
style::cursor _cursor = style::cur_default;
|
||||
using SelectedItems = QMap<HistoryItem*, TextSelection>;
|
||||
SelectedItems _selected;
|
||||
void applyDragSelection();
|
||||
void applyDragSelection(SelectedItems *toItems) const;
|
||||
void addSelectionRange(SelectedItems *toItems, int32 fromblock, int32 fromitem, int32 toblock, int32 toitem, History *h) const;
|
||||
|
||||
// Does any of the shown histories has this flag set.
|
||||
bool hasPendingResizedItems() const {
|
||||
return (_history && _history->hasPendingResizedItems()) || (_migrated && _migrated->hasPendingResizedItems());
|
||||
}
|
||||
|
||||
enum DragAction {
|
||||
NoDrag = 0x00,
|
||||
PrepareDrag = 0x01,
|
||||
Dragging = 0x02,
|
||||
PrepareSelect = 0x03,
|
||||
Selecting = 0x04,
|
||||
};
|
||||
DragAction _dragAction = NoDrag;
|
||||
TextSelectType _dragSelType = TextSelectType::Letters;
|
||||
QPoint _dragStartPos, _dragPos;
|
||||
HistoryItem *_dragItem = nullptr;
|
||||
HistoryCursorState _dragCursorState = HistoryDefaultCursorState;
|
||||
uint16 _dragSymbol = 0;
|
||||
bool _dragWasInactive = false;
|
||||
|
||||
QPoint _trippleClickPoint;
|
||||
QTimer _trippleClickTimer;
|
||||
|
||||
ClickHandlerPtr _contextMenuLnk;
|
||||
|
||||
HistoryItem *_dragSelFrom = nullptr;
|
||||
HistoryItem *_dragSelTo = nullptr;
|
||||
bool _dragSelecting = false;
|
||||
bool _wasSelectedText = false; // was some text selected in current drag action
|
||||
|
||||
// scroll by touch support (at least Windows Surface tablets)
|
||||
bool _touchScroll = false;
|
||||
bool _touchSelect = false;
|
||||
bool _touchInProgress = false;
|
||||
QPoint _touchStart, _touchPrevPos, _touchPos;
|
||||
QTimer _touchSelectTimer;
|
||||
|
||||
Ui::TouchScrollState _touchScrollState = Ui::TouchScrollState::Manual;
|
||||
bool _touchPrevPosValid = false;
|
||||
bool _touchWaitingAcceleration = false;
|
||||
QPoint _touchSpeed;
|
||||
TimeMs _touchSpeedTime = 0;
|
||||
TimeMs _touchAccelerationTime = 0;
|
||||
TimeMs _touchTime = 0;
|
||||
QTimer _touchScrollTimer;
|
||||
|
||||
// context menu
|
||||
Ui::PopupMenu *_menu = nullptr;
|
||||
|
||||
// save visible area coords for painting / pressing userpics
|
||||
int _visibleAreaTop = 0;
|
||||
int _visibleAreaBottom = 0;
|
||||
|
||||
bool _scrollDateShown = false;
|
||||
Animation _scrollDateOpacity;
|
||||
SingleQueuedInvokation _scrollDateCheck;
|
||||
SingleTimer _scrollDateHideTimer;
|
||||
HistoryItem *_scrollDateLastItem = nullptr;
|
||||
int _scrollDateLastItemTop = 0;
|
||||
ClickHandlerPtr _scrollDateLink;
|
||||
|
||||
enum class EnumItemsDirection {
|
||||
TopToBottom,
|
||||
BottomToTop,
|
||||
};
|
||||
// this function finds all history items that are displayed and calls template method
|
||||
// for each found message (in given direction) in the passed history with passed top offset
|
||||
//
|
||||
// method has "bool (*Method)(HistoryItem *item, int itemtop, int itembottom)" signature
|
||||
// if it returns false the enumeration stops immidiately
|
||||
template <bool TopToBottom, typename Method>
|
||||
void enumerateItemsInHistory(History *history, int historytop, Method method);
|
||||
|
||||
template <EnumItemsDirection direction, typename Method>
|
||||
void enumerateItems(Method method) {
|
||||
constexpr auto TopToBottom = (direction == EnumItemsDirection::TopToBottom);
|
||||
if (TopToBottom && _migrated) {
|
||||
enumerateItemsInHistory<TopToBottom>(_migrated, migratedTop(), method);
|
||||
}
|
||||
enumerateItemsInHistory<TopToBottom>(_history, historyTop(), method);
|
||||
if (!TopToBottom && _migrated) {
|
||||
enumerateItemsInHistory<TopToBottom>(_migrated, migratedTop(), method);
|
||||
}
|
||||
}
|
||||
|
||||
// this function finds all userpics on the left that are displayed and calls template method
|
||||
// for each found userpic (from the top to the bottom) using enumerateItems() method
|
||||
//
|
||||
// method has "bool (*Method)(HistoryMessage *message, int userpicTop)" signature
|
||||
// if it returns false the enumeration stops immidiately
|
||||
template <typename Method>
|
||||
void enumerateUserpics(Method method);
|
||||
|
||||
// this function finds all date elements that are displayed and calls template method
|
||||
// for each found date element (from the bottom to the top) using enumerateItems() method
|
||||
//
|
||||
// method has "bool (*Method)(HistoryItem *item, int itemtop, int dateTop)" signature
|
||||
// if it returns false the enumeration stops immidiately
|
||||
template <typename Method>
|
||||
void enumerateDates(Method method);
|
||||
|
||||
};
|
||||
|
||||
class MessageField : public Ui::FlatTextarea {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MessageField(HistoryWidget *history, const style::FlatTextarea &st, const QString &ph = QString(), const QString &val = QString());
|
||||
void dropEvent(QDropEvent *e);
|
||||
bool canInsertFromMimeData(const QMimeData *source) const;
|
||||
void insertFromMimeData(const QMimeData *source);
|
||||
|
||||
void focusInEvent(QFocusEvent *e);
|
||||
|
||||
bool hasSendText() const;
|
||||
|
||||
public slots:
|
||||
void onEmojiInsert(EmojiPtr emoji);
|
||||
|
||||
signals:
|
||||
void focused();
|
||||
|
||||
private:
|
||||
HistoryWidget *history;
|
||||
|
||||
};
|
||||
class BotKeyboard;
|
||||
class MessageField;
|
||||
class HistoryInner;
|
||||
|
||||
class ReportSpamPanel : public TWidget {
|
||||
Q_OBJECT
|
||||
@ -384,92 +91,6 @@ private:
|
||||
|
||||
};
|
||||
|
||||
class BotKeyboard : public TWidget, public Ui::AbstractTooltipShower, public ClickHandlerHost {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
BotKeyboard(QWidget *parent);
|
||||
|
||||
bool moderateKeyActivate(int index);
|
||||
|
||||
// With force=true the markup is updated even if it is
|
||||
// already shown for the passed history item.
|
||||
bool updateMarkup(HistoryItem *last, bool force = false);
|
||||
bool hasMarkup() const;
|
||||
bool forceReply() const;
|
||||
|
||||
void step_selected(TimeMs ms, bool timer);
|
||||
void resizeToWidth(int newWidth, int maxOuterHeight) {
|
||||
_maxOuterHeight = maxOuterHeight;
|
||||
return TWidget::resizeToWidth(newWidth);
|
||||
}
|
||||
|
||||
bool maximizeSize() const;
|
||||
bool singleUse() const;
|
||||
|
||||
FullMsgId forMsgId() const {
|
||||
return _wasForMsgId;
|
||||
}
|
||||
|
||||
// AbstractTooltipShower interface
|
||||
QString tooltipText() const override;
|
||||
QPoint tooltipPos() const override;
|
||||
|
||||
// ClickHandlerHost interface
|
||||
void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override;
|
||||
void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) override;
|
||||
|
||||
protected:
|
||||
int resizeGetHeight(int newWidth) override;
|
||||
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
void mousePressEvent(QMouseEvent *e) override;
|
||||
void mouseMoveEvent(QMouseEvent *e) override;
|
||||
void mouseReleaseEvent(QMouseEvent *e) override;
|
||||
void enterEventHook(QEvent *e) override;
|
||||
void leaveEventHook(QEvent *e) override;
|
||||
|
||||
private:
|
||||
void updateSelected();
|
||||
|
||||
void updateStyle(int newWidth);
|
||||
void clearSelection();
|
||||
|
||||
FullMsgId _wasForMsgId;
|
||||
int _height = 0;
|
||||
int _maxOuterHeight = 0;
|
||||
bool _maximizeSize = false;
|
||||
bool _singleUse = false;
|
||||
bool _forceReply = false;
|
||||
|
||||
QPoint _lastMousePos;
|
||||
std::unique_ptr<ReplyKeyboard> _impl;
|
||||
|
||||
class Style : public ReplyKeyboard::Style {
|
||||
public:
|
||||
Style(BotKeyboard *parent, const style::BotKeyboardButton &st) : ReplyKeyboard::Style(st), _parent(parent) {
|
||||
}
|
||||
|
||||
int buttonRadius() const override;
|
||||
|
||||
void startPaint(Painter &p) const override;
|
||||
const style::TextStyle &textStyle() const override;
|
||||
void repaint(const HistoryItem *item) const override;
|
||||
|
||||
protected:
|
||||
void paintButtonBg(Painter &p, const QRect &rect, float64 howMuchOver) const override;
|
||||
void paintButtonIcon(Painter &p, const QRect &rect, int outerWidth, HistoryMessageReplyMarkup::Button::Type type) const override;
|
||||
void paintButtonLoading(Painter &p, const QRect &rect) const override;
|
||||
int minButtonWidth(HistoryMessageReplyMarkup::Button::Type type) const override;
|
||||
|
||||
private:
|
||||
BotKeyboard *_parent;
|
||||
|
||||
};
|
||||
const style::BotKeyboardButton *_st = nullptr;
|
||||
|
||||
};
|
||||
|
||||
class HistoryHider : public TWidget, private base::Subscriber {
|
||||
Q_OBJECT
|
||||
|
||||
@ -538,9 +159,6 @@ private:
|
||||
|
||||
};
|
||||
|
||||
EntitiesInText entitiesFromTextTags(const TextWithTags::Tags &tags);
|
||||
TextWithTags::Tags textTagsFromEntities(const EntitiesInText &entities);
|
||||
|
||||
class HistoryWidget : public TWidget, public RPCSender, private base::Subscriber {
|
||||
Q_OBJECT
|
||||
|
||||
|
@ -28,7 +28,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
#include "inline_bots/inline_bot_result.h"
|
||||
#include "inline_bots/inline_bot_layout_item.h"
|
||||
#include "dialogs/dialogs_layout.h"
|
||||
#include "historywidget.h"
|
||||
#include "storage/localstorage.h"
|
||||
#include "lang.h"
|
||||
#include "mainwindow.h"
|
||||
@ -36,6 +35,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
#include "mainwidget.h"
|
||||
#include "auth_session.h"
|
||||
#include "window/window_controller.h"
|
||||
#include "ui/widgets/scroll_area.h"
|
||||
|
||||
namespace InlineBots {
|
||||
namespace Layout {
|
||||
|
@ -30,6 +30,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
#include "window/top_bar_widget.h"
|
||||
#include "data/data_drafts.h"
|
||||
#include "ui/widgets/dropdown_menu.h"
|
||||
#include "chat_helpers/message_field.h"
|
||||
#include "observer_peer.h"
|
||||
#include "apiwrap.h"
|
||||
#include "dialogswidget.h"
|
||||
@ -1167,7 +1168,7 @@ void MainWidget::sendMessage(const MessageToSend &message) {
|
||||
|
||||
saveRecentHashtags(textWithTags.text);
|
||||
|
||||
EntitiesInText sendingEntities, leftEntities = entitiesFromTextTags(textWithTags.tags);
|
||||
EntitiesInText sendingEntities, leftEntities = ConvertTextTagsToEntities(textWithTags.tags);
|
||||
auto prepareFlags = itemTextOptions(history, App::self()).flags;
|
||||
QString sendingText, leftText = prepareTextWithEntities(textWithTags.text, prepareFlags, &leftEntities);
|
||||
|
||||
|
@ -79,6 +79,8 @@
|
||||
<(src_loc)/boxes/stickers_box.h
|
||||
<(src_loc)/boxes/username_box.cpp
|
||||
<(src_loc)/boxes/username_box.h
|
||||
<(src_loc)/chat_helpers/bot_keyboard.cpp
|
||||
<(src_loc)/chat_helpers/bot_keyboard.h
|
||||
<(src_loc)/chat_helpers/emoji_panel.cpp
|
||||
<(src_loc)/chat_helpers/emoji_panel.h
|
||||
<(src_loc)/chat_helpers/emoji_list_widget.cpp
|
||||
@ -89,6 +91,8 @@
|
||||
<(src_loc)/chat_helpers/field_autocomplete.h
|
||||
<(src_loc)/chat_helpers/gifs_list_widget.cpp
|
||||
<(src_loc)/chat_helpers/gifs_list_widget.h
|
||||
<(src_loc)/chat_helpers/message_field.cpp
|
||||
<(src_loc)/chat_helpers/message_field.h
|
||||
<(src_loc)/chat_helpers/stickers.cpp
|
||||
<(src_loc)/chat_helpers/stickers.h
|
||||
<(src_loc)/chat_helpers/stickers_list_widget.cpp
|
||||
@ -123,6 +127,8 @@
|
||||
<(src_loc)/history/history_drag_area.h
|
||||
<(src_loc)/history/history_item.cpp
|
||||
<(src_loc)/history/history_item.h
|
||||
<(src_loc)/history/history_inner_widget.cpp
|
||||
<(src_loc)/history/history_inner_widget.h
|
||||
<(src_loc)/history/history_location_manager.cpp
|
||||
<(src_loc)/history/history_location_manager.h
|
||||
<(src_loc)/history/history_media.h
|
||||
|
Loading…
Reference in New Issue
Block a user