mirror of https://github.com/procxx/kepka.git
Unite InputField and InputArea.
Also support and use instant replaces in InputField-s.
This commit is contained in:
parent
8e442563f2
commit
30dd8fe070
|
@ -322,7 +322,11 @@ void GroupInfoBox::prepare() {
|
|||
_title->setMaxLength(kMaxGroupChannelTitle);
|
||||
|
||||
if (_creating == CreatingGroupChannel) {
|
||||
_description.create(this, st::newGroupDescription, langFactory(lng_create_group_description));
|
||||
_description.create(
|
||||
this,
|
||||
st::newGroupDescription,
|
||||
Ui::InputField::Mode::MultiLine,
|
||||
langFactory(lng_create_group_description));
|
||||
_description->show();
|
||||
_description->setMaxLength(kMaxChannelDescription);
|
||||
|
||||
|
@ -1052,7 +1056,12 @@ bool EditNameBox::saveSelfFail(const RPCError &error) {
|
|||
EditBioBox::EditBioBox(QWidget*, not_null<UserData*> self) : BoxContent()
|
||||
, _dynamicFieldStyle(CreateBioFieldStyle())
|
||||
, _self(self)
|
||||
, _bio(this, _dynamicFieldStyle, langFactory(lng_bio_placeholder), _self->about())
|
||||
, _bio(
|
||||
this,
|
||||
_dynamicFieldStyle,
|
||||
Ui::InputField::Mode::MultiLine,
|
||||
langFactory(lng_bio_placeholder),
|
||||
_self->about())
|
||||
, _countdown(this, QString(), Ui::FlatLabel::InitType::Simple, st::editBioCountdownLabel)
|
||||
, _about(this, lang(lng_bio_about), Ui::FlatLabel::InitType::Simple, st::aboutRevokePublicLabel) {
|
||||
}
|
||||
|
@ -1067,9 +1076,10 @@ void EditBioBox::prepare() {
|
|||
auto cursor = _bio->textCursor();
|
||||
cursor.setPosition(_bio->getLastText().size());
|
||||
_bio->setTextCursor(cursor);
|
||||
connect(_bio, &Ui::InputArea::submitted, this, [this](bool ctrlShiftEnter) { save(); });
|
||||
connect(_bio, &Ui::InputArea::resized, this, [this] { updateMaxHeight(); });
|
||||
connect(_bio, &Ui::InputArea::changed, this, [this] { handleBioUpdated(); });
|
||||
connect(_bio, &Ui::InputField::submitted, this, [this](bool ctrlShiftEnter) { save(); });
|
||||
connect(_bio, &Ui::InputField::resized, this, [this] { updateMaxHeight(); });
|
||||
connect(_bio, &Ui::InputField::changed, this, [this] { handleBioUpdated(); });
|
||||
_bio->setInstantReplaces(Ui::InstantReplaces::Default());
|
||||
handleBioUpdated();
|
||||
updateMaxHeight();
|
||||
}
|
||||
|
@ -1122,7 +1132,12 @@ void EditBioBox::save() {
|
|||
EditChannelBox::EditChannelBox(QWidget*, not_null<ChannelData*> channel)
|
||||
: _channel(channel)
|
||||
, _title(this, st::defaultInputField, langFactory(_channel->isMegagroup() ? lng_dlg_new_group_name : lng_dlg_new_channel_name), _channel->name)
|
||||
, _description(this, st::newGroupDescription, langFactory(lng_create_group_description), _channel->about())
|
||||
, _description(
|
||||
this,
|
||||
st::newGroupDescription,
|
||||
Ui::InputField::Mode::MultiLine,
|
||||
langFactory(lng_create_group_description),
|
||||
_channel->about())
|
||||
, _sign(this, lang(lng_edit_sign_messages), channel->addsSignature(), st::defaultBoxCheckbox)
|
||||
, _inviteGroup(std::make_shared<Ui::RadioenumGroup<Invites>>(channel->anyoneCanAddMembers() ? Invites::Everybody : Invites::OnlyAdmins))
|
||||
, _inviteEverybody(this, _inviteGroup, Invites::Everybody, lang(lng_edit_group_invites_everybody))
|
||||
|
|
|
@ -18,7 +18,6 @@ namespace Ui {
|
|||
class FlatLabel;
|
||||
class InputField;
|
||||
class PhoneInput;
|
||||
class InputArea;
|
||||
class UsernameInput;
|
||||
class Checkbox;
|
||||
template <typename Enum>
|
||||
|
@ -111,7 +110,7 @@ private:
|
|||
|
||||
object_ptr<Ui::UserpicButton> _photo = { nullptr };
|
||||
object_ptr<Ui::InputField> _title = { nullptr };
|
||||
object_ptr<Ui::InputArea> _description = { nullptr };
|
||||
object_ptr<Ui::InputField> _description = { nullptr };
|
||||
|
||||
// group / channel creation
|
||||
mtpRequestId _creationRequestId = 0;
|
||||
|
@ -231,7 +230,7 @@ private:
|
|||
style::InputField _dynamicFieldStyle;
|
||||
not_null<UserData*> _self;
|
||||
|
||||
object_ptr<Ui::InputArea> _bio;
|
||||
object_ptr<Ui::InputField> _bio;
|
||||
object_ptr<Ui::FlatLabel> _countdown;
|
||||
object_ptr<Ui::FlatLabel> _about;
|
||||
mtpRequestId _requestId = 0;
|
||||
|
@ -280,7 +279,7 @@ private:
|
|||
not_null<ChannelData*> _channel;
|
||||
|
||||
object_ptr<Ui::InputField> _title;
|
||||
object_ptr<Ui::InputArea> _description;
|
||||
object_ptr<Ui::InputField> _description;
|
||||
object_ptr<Ui::Checkbox> _sign;
|
||||
|
||||
enum class Invites {
|
||||
|
|
|
@ -130,9 +130,15 @@ EditCaptionBox::EditCaptionBox(
|
|||
}
|
||||
Assert(_animated || _photo || _doc);
|
||||
|
||||
_field.create(this, st::confirmCaptionArea, langFactory(lng_photo_caption), caption);
|
||||
_field.create(
|
||||
this,
|
||||
st::confirmCaptionArea,
|
||||
Ui::InputField::Mode::MultiLine,
|
||||
langFactory(lng_photo_caption),
|
||||
caption);
|
||||
_field->setMaxLength(MaxPhotoCaption);
|
||||
_field->setCtrlEnterSubmit(Ui::CtrlEnterSubmit::Both);
|
||||
_field->setInstantReplaces(Ui::InstantReplaces::Default());
|
||||
}
|
||||
|
||||
void EditCaptionBox::prepareGifPreview(DocumentData *document) {
|
||||
|
@ -177,11 +183,11 @@ void EditCaptionBox::prepare() {
|
|||
addButton(langFactory(lng_cancel), [this] { closeBox(); });
|
||||
|
||||
updateBoxSize();
|
||||
connect(_field, &Ui::InputArea::submitted, this, [this] { save(); });
|
||||
connect(_field, &Ui::InputArea::cancelled, this, [this] {
|
||||
connect(_field, &Ui::InputField::submitted, this, [this] { save(); });
|
||||
connect(_field, &Ui::InputField::cancelled, this, [this] {
|
||||
closeBox();
|
||||
});
|
||||
connect(_field, &Ui::InputArea::resized, this, [this] {
|
||||
connect(_field, &Ui::InputField::resized, this, [this] {
|
||||
captionResized();
|
||||
});
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ class Media;
|
|||
} // namespace Data
|
||||
|
||||
namespace Ui {
|
||||
class InputArea;
|
||||
class InputField;
|
||||
} // namespace Ui
|
||||
|
||||
class EditCaptionBox : public BoxContent, public RPCSender {
|
||||
|
@ -49,7 +49,7 @@ private:
|
|||
QPixmap _thumb;
|
||||
Media::Clip::ReaderPointer _gifPreview;
|
||||
|
||||
object_ptr<Ui::InputArea> _field = { nullptr };
|
||||
object_ptr<Ui::InputField> _field = { nullptr };
|
||||
|
||||
int _thumbx = 0;
|
||||
int _thumbw = 0;
|
||||
|
|
|
@ -71,7 +71,7 @@ private:
|
|||
};
|
||||
struct Controls {
|
||||
Ui::InputField *title = nullptr;
|
||||
Ui::InputArea *description = nullptr;
|
||||
Ui::InputField *description = nullptr;
|
||||
Ui::UserpicButton *photo = nullptr;
|
||||
rpl::lifetime initialPhotoImageWaiting;
|
||||
|
||||
|
@ -317,19 +317,21 @@ object_ptr<Ui::RpWidget> Controller::createDescriptionEdit() {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
auto result = object_ptr<Ui::PaddingWrap<Ui::InputArea>>(
|
||||
auto result = object_ptr<Ui::PaddingWrap<Ui::InputField>>(
|
||||
_wrap,
|
||||
object_ptr<Ui::InputArea>(
|
||||
object_ptr<Ui::InputField>(
|
||||
_wrap,
|
||||
st::editPeerDescription,
|
||||
Ui::InputField::Mode::MultiLine,
|
||||
langFactory(lng_create_group_description),
|
||||
channel->about()),
|
||||
st::editPeerDescriptionMargins);
|
||||
result->entity()->setMaxLength(kMaxChannelDescription);
|
||||
result->entity()->setInstantReplaces(Ui::InstantReplaces::Default());
|
||||
|
||||
QObject::connect(
|
||||
result->entity(),
|
||||
&Ui::InputArea::submitted,
|
||||
&Ui::InputField::submitted,
|
||||
[this] { submitDescription(); });
|
||||
|
||||
_controls.description = result->entity();
|
||||
|
|
|
@ -71,7 +71,11 @@ void RateCallBox::ratingChanged(int value) {
|
|||
}
|
||||
if (value < kMaxRating) {
|
||||
if (!_comment) {
|
||||
_comment.create(this, st::callRatingComment, langFactory(lng_call_rate_comment));
|
||||
_comment.create(
|
||||
this,
|
||||
st::callRatingComment,
|
||||
Ui::InputField::Mode::MultiLine,
|
||||
langFactory(lng_call_rate_comment));
|
||||
_comment->show();
|
||||
_comment->setCtrlEnterSubmit(Ui::CtrlEnterSubmit::Both);
|
||||
_comment->setMaxLength(MaxPhotoCaption);
|
||||
|
|
|
@ -11,7 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "mtproto/sender.h"
|
||||
|
||||
namespace Ui {
|
||||
class InputArea;
|
||||
class InputField;
|
||||
class FlatLabel;
|
||||
class IconButton;
|
||||
} // namespace Ui
|
||||
|
@ -44,7 +44,7 @@ private:
|
|||
int _rating = 0;
|
||||
|
||||
std::vector<object_ptr<Ui::IconButton>> _stars;
|
||||
object_ptr<Ui::InputArea> _comment = { nullptr };
|
||||
object_ptr<Ui::InputField> _comment = { nullptr };
|
||||
|
||||
mtpRequestId _requestId = 0;
|
||||
|
||||
|
|
|
@ -82,7 +82,11 @@ void ReportBox::resizeEvent(QResizeEvent *e) {
|
|||
void ReportBox::reasonChanged(Reason reason) {
|
||||
if (reason == Reason::Other) {
|
||||
if (!_reasonOtherText) {
|
||||
_reasonOtherText.create(this, st::profileReportReasonOther, langFactory(lng_report_reason_description));
|
||||
_reasonOtherText.create(
|
||||
this,
|
||||
st::profileReportReasonOther,
|
||||
Ui::InputField::Mode::MultiLine,
|
||||
langFactory(lng_report_reason_description));
|
||||
_reasonOtherText->show();
|
||||
_reasonOtherText->setCtrlEnterSubmit(Ui::CtrlEnterSubmit::Both);
|
||||
_reasonOtherText->setMaxLength(MaxPhotoCaption);
|
||||
|
|
|
@ -14,7 +14,7 @@ template <typename Enum>
|
|||
class RadioenumGroup;
|
||||
template <typename Enum>
|
||||
class Radioenum;
|
||||
class InputArea;
|
||||
class InputField;
|
||||
} // namespace Ui
|
||||
|
||||
class ReportBox : public BoxContent, public RPCSender {
|
||||
|
@ -58,7 +58,7 @@ private:
|
|||
object_ptr<Ui::Radioenum<Reason>> _reasonViolence = { nullptr };
|
||||
object_ptr<Ui::Radioenum<Reason>> _reasonPornography = { nullptr };
|
||||
object_ptr<Ui::Radioenum<Reason>> _reasonOther = { nullptr };
|
||||
object_ptr<Ui::InputArea> _reasonOtherText = { nullptr };
|
||||
object_ptr<Ui::InputField> _reasonOtherText = { nullptr };
|
||||
|
||||
mtpRequestId _requestId = 0;
|
||||
|
||||
|
|
|
@ -1550,29 +1550,34 @@ void SendFilesBox::setupCaption() {
|
|||
return;
|
||||
}
|
||||
|
||||
_caption.create(this, st::confirmCaptionArea, FieldPlaceholder(_list));
|
||||
_caption.create(
|
||||
this,
|
||||
st::confirmCaptionArea,
|
||||
Ui::InputField::Mode::MultiLine,
|
||||
FieldPlaceholder(_list));
|
||||
_caption->setMaxLength(MaxPhotoCaption);
|
||||
_caption->setCtrlEnterSubmit(Ui::CtrlEnterSubmit::Both);
|
||||
connect(_caption, &Ui::InputArea::resized, this, [this] {
|
||||
connect(_caption, &Ui::InputField::resized, this, [this] {
|
||||
captionResized();
|
||||
});
|
||||
connect(_caption, &Ui::InputArea::submitted, this, [this](
|
||||
connect(_caption, &Ui::InputField::submitted, this, [this](
|
||||
bool ctrlShiftEnter) {
|
||||
send(ctrlShiftEnter);
|
||||
});
|
||||
connect(_caption, &Ui::InputArea::cancelled, this, [this] {
|
||||
connect(_caption, &Ui::InputField::cancelled, this, [this] {
|
||||
closeBox();
|
||||
});
|
||||
_caption->setMimeDataHook([this](
|
||||
not_null<const QMimeData*> data,
|
||||
Ui::InputArea::MimeAction action) {
|
||||
if (action == Ui::InputArea::MimeAction::Check) {
|
||||
Ui::InputField::MimeAction action) {
|
||||
if (action == Ui::InputField::MimeAction::Check) {
|
||||
return canAddFiles(data);
|
||||
} else if (action == Ui::InputArea::MimeAction::Insert) {
|
||||
} else if (action == Ui::InputField::MimeAction::Insert) {
|
||||
return addFiles(data);
|
||||
}
|
||||
Unexpected("action in MimeData hook.");
|
||||
});
|
||||
_caption->setInstantReplaces(Ui::InstantReplaces::Default());
|
||||
}
|
||||
|
||||
void SendFilesBox::captionResized() {
|
||||
|
|
|
@ -18,7 +18,7 @@ class Radioenum;
|
|||
template <typename Enum>
|
||||
class RadioenumGroup;
|
||||
class RoundButton;
|
||||
class InputArea;
|
||||
class InputField;
|
||||
struct GroupMediaLayout;
|
||||
} // namespace Ui
|
||||
|
||||
|
@ -103,7 +103,7 @@ private:
|
|||
base::lambda<void()> _cancelledCallback;
|
||||
bool _confirmed = false;
|
||||
|
||||
object_ptr<Ui::InputArea> _caption = { nullptr };
|
||||
object_ptr<Ui::InputField> _caption = { nullptr };
|
||||
object_ptr<Ui::Radioenum<SendFilesWay>> _sendAlbum = { nullptr };
|
||||
object_ptr<Ui::Radioenum<SendFilesWay>> _sendPhotos = { nullptr };
|
||||
object_ptr<Ui::Radioenum<SendFilesWay>> _sendFiles = { nullptr };
|
||||
|
|
|
@ -12,7 +12,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
namespace Ui {
|
||||
|
||||
class InnerDropdown;
|
||||
class FlatTextarea;
|
||||
|
||||
namespace Emoji {
|
||||
|
||||
|
|
|
@ -9,12 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
#include "history/history_widget.h"
|
||||
#include "base/qthelp_regex.h"
|
||||
#include "styles/style_history.h"
|
||||
#include "window/window_controller.h"
|
||||
#include "emoji_suggestions_data.h"
|
||||
#include "chat_helpers/emoji_suggestions_helper.h"
|
||||
#include "mainwindow.h"
|
||||
#include "auth_session.h"
|
||||
#include "styles/style_history.h"
|
||||
|
||||
namespace {
|
||||
|
||||
|
@ -28,7 +26,8 @@ public:
|
|||
QString tagFromMimeTag(const QString &mimeTag) override {
|
||||
if (mimeTag.startsWith(qstr("mention://"))) {
|
||||
auto match = QRegularExpression(":(\\d+)$").match(mimeTag);
|
||||
if (!match.hasMatch() || match.capturedRef(1).toInt() != Auth().userId()) {
|
||||
if (!match.hasMatch()
|
||||
|| match.capturedRef(1).toInt() != Auth().userId()) {
|
||||
return QString();
|
||||
}
|
||||
return mimeTag.mid(0, mimeTag.size() - match.capturedLength());
|
||||
|
@ -97,8 +96,8 @@ std::unique_ptr<QMimeData> MimeDataFromTextWithEntities(
|
|||
tag.id = ConvertTagToMimeTag(tag.id);
|
||||
}
|
||||
result->setData(
|
||||
Ui::FlatTextarea::tagsMimeType(),
|
||||
Ui::FlatTextarea::serializeTagsList(tags));
|
||||
TextUtilities::TagsMimeType(),
|
||||
TextUtilities::SerializeTags(tags));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -111,33 +110,15 @@ void SetClipboardWithEntities(
|
|||
}
|
||||
}
|
||||
|
||||
MessageField::MessageField(QWidget *parent, not_null<Window::Controller*> controller, const style::FlatTextarea &st, base::lambda<QString()> placeholderFactory, const QString &val) : Ui::FlatTextarea(parent, st, std::move(placeholderFactory), val)
|
||||
MessageField::MessageField(QWidget *parent, not_null<Window::Controller*> controller, const style::FlatTextarea &st, base::lambda<QString()> placeholderFactory)
|
||||
: FlatTextarea(parent, st, std::move(placeholderFactory))
|
||||
, _controller(controller) {
|
||||
setMinHeight(st::historySendSize.height() - 2 * st::historySendPadding);
|
||||
setMaxHeight(st::historyComposeFieldMaxHeight);
|
||||
|
||||
setTagMimeProcessor(std::make_unique<FieldTagMimeProcessor>());
|
||||
|
||||
addInstantReplace("--", QString(1, QChar(8212)));
|
||||
addInstantReplace("<<", QString(1, QChar(171)));
|
||||
addInstantReplace(">>", QString(1, QChar(187)));
|
||||
addInstantReplace(
|
||||
":shrug:",
|
||||
QChar(175) + QString("\\_(") + QChar(12484) + ")_/" + QChar(175));
|
||||
addInstantReplace(":o ", QString(1, QChar(0xD83D)) + QChar(0xDE28));
|
||||
addInstantReplace("xD ", QString(1, QChar(0xD83D)) + QChar(0xDE06));
|
||||
const auto &replacements = Ui::Emoji::internal::GetAllReplacements();
|
||||
for (const auto &one : replacements) {
|
||||
const auto with = Ui::Emoji::QStringFromUTF16(one.emoji);
|
||||
const auto what = Ui::Emoji::QStringFromUTF16(one.replacement);
|
||||
addInstantReplace(what, with);
|
||||
}
|
||||
const auto &pairs = Ui::Emoji::internal::GetReplacementPairs();
|
||||
for (const auto &[what, index] : pairs) {
|
||||
const auto emoji = Ui::Emoji::internal::ByIndex(index);
|
||||
Assert(emoji != nullptr);
|
||||
addInstantReplace(what, emoji->text());
|
||||
}
|
||||
setInstantReplaces(Ui::InstantReplaces::Default());
|
||||
enableInstantReplaces(Global::ReplaceEmoji());
|
||||
subscribe(Global::RefReplaceEmojiChanged(), [=] {
|
||||
enableInstantReplaces(Global::ReplaceEmoji());
|
||||
|
|
|
@ -29,7 +29,7 @@ class MessageField final : public Ui::FlatTextarea {
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MessageField(QWidget *parent, not_null<Window::Controller*> controller, const style::FlatTextarea &st, base::lambda<QString()> placeholderFactory = base::lambda<QString()>(), const QString &val = QString());
|
||||
MessageField(QWidget *parent, not_null<Window::Controller*> controller, const style::FlatTextarea &st, base::lambda<QString()> placeholderFactory = nullptr);
|
||||
|
||||
bool hasSendText() const;
|
||||
|
||||
|
|
|
@ -516,7 +516,7 @@ HistoryWidget::HistoryWidget(
|
|||
int from,
|
||||
int till,
|
||||
const QString &replacement) {
|
||||
_field->commmitInstantReplacement(from, till, replacement);
|
||||
_field->commitInstantReplacement(from, till, replacement);
|
||||
});
|
||||
updateFieldSubmitSettings();
|
||||
|
||||
|
|
|
@ -20,7 +20,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "mainwindow.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "media/media_audio.h"
|
||||
#include "ui/widgets/input_fields.h"
|
||||
#include "mtproto/dc_options.h"
|
||||
#include "messenger.h"
|
||||
#include "application.h"
|
||||
|
@ -2679,8 +2678,10 @@ void writeDrafts(const PeerId &peer, const MessageDraft &localDraft, const Messa
|
|||
_writeMap(WriteMapWhen::Fast);
|
||||
}
|
||||
|
||||
auto msgTags = Ui::FlatTextarea::serializeTagsList(localDraft.textWithTags.tags);
|
||||
auto editTags = Ui::FlatTextarea::serializeTagsList(editDraft.textWithTags.tags);
|
||||
auto msgTags = TextUtilities::SerializeTags(
|
||||
localDraft.textWithTags.tags);
|
||||
auto editTags = TextUtilities::SerializeTags(
|
||||
editDraft.textWithTags.tags);
|
||||
|
||||
int size = sizeof(quint64);
|
||||
size += Serialize::stringSize(localDraft.textWithTags.text) + Serialize::bytearraySize(msgTags) + 2 * sizeof(qint32);
|
||||
|
@ -2786,8 +2787,12 @@ void readDraftsWithCursors(History *h) {
|
|||
return;
|
||||
}
|
||||
|
||||
msgData.tags = Ui::FlatTextarea::deserializeTagsList(msgTagsSerialized, msgData.text.size());
|
||||
editData.tags = Ui::FlatTextarea::deserializeTagsList(editTagsSerialized, editData.text.size());
|
||||
msgData.tags = TextUtilities::DeserializeTags(
|
||||
msgTagsSerialized,
|
||||
msgData.text.size());
|
||||
editData.tags = TextUtilities::DeserializeTags(
|
||||
editTagsSerialized,
|
||||
editData.text.size());
|
||||
|
||||
MessageCursor msgCursor, editCursor;
|
||||
_readDraftCursors(peer, msgCursor, editCursor);
|
||||
|
@ -2796,13 +2801,21 @@ void readDraftsWithCursors(History *h) {
|
|||
if (msgData.text.isEmpty() && !msgReplyTo) {
|
||||
h->clearLocalDraft();
|
||||
} else {
|
||||
h->setLocalDraft(std::make_unique<Data::Draft>(msgData, msgReplyTo, msgCursor, msgPreviewCancelled));
|
||||
h->setLocalDraft(std::make_unique<Data::Draft>(
|
||||
msgData,
|
||||
msgReplyTo,
|
||||
msgCursor,
|
||||
msgPreviewCancelled));
|
||||
}
|
||||
}
|
||||
if (!editMsgId) {
|
||||
h->clearEditDraft();
|
||||
} else {
|
||||
h->setEditDraft(std::make_unique<Data::Draft>(editData, editMsgId, editCursor, editPreviewCancelled));
|
||||
h->setEditDraft(std::make_unique<Data::Draft>(
|
||||
editData,
|
||||
editMsgId,
|
||||
editCursor,
|
||||
editPreviewCancelled));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2241,6 +2241,60 @@ void Trim(TextWithEntities &result) {
|
|||
}
|
||||
}
|
||||
|
||||
QByteArray SerializeTags(const TextWithTags::Tags &tags) {
|
||||
if (tags.isEmpty()) {
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
QByteArray tagsSerialized;
|
||||
{
|
||||
QDataStream stream(&tagsSerialized, QIODevice::WriteOnly);
|
||||
stream.setVersion(QDataStream::Qt_5_1);
|
||||
stream << qint32(tags.size());
|
||||
for (const auto &tag : tags) {
|
||||
stream << qint32(tag.offset) << qint32(tag.length) << tag.id;
|
||||
}
|
||||
}
|
||||
return tagsSerialized;
|
||||
}
|
||||
|
||||
TextWithTags::Tags DeserializeTags(QByteArray data, int textLength) {
|
||||
auto result = TextWithTags::Tags();
|
||||
if (data.isEmpty()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
QDataStream stream(data);
|
||||
stream.setVersion(QDataStream::Qt_5_1);
|
||||
|
||||
qint32 tagCount = 0;
|
||||
stream >> tagCount;
|
||||
if (stream.status() != QDataStream::Ok) {
|
||||
return result;
|
||||
}
|
||||
if (tagCount <= 0 || tagCount > textLength) {
|
||||
return result;
|
||||
}
|
||||
|
||||
for (auto i = 0; i != tagCount; ++i) {
|
||||
qint32 offset = 0, length = 0;
|
||||
QString id;
|
||||
stream >> offset >> length >> id;
|
||||
if (stream.status() != QDataStream::Ok) {
|
||||
return result;
|
||||
}
|
||||
if (offset < 0 || length <= 0 || offset + length > textLength) {
|
||||
return result;
|
||||
}
|
||||
result.push_back({ offset, length, id });
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QString TagsMimeType() {
|
||||
return qsl("application/x-td-field-tags");
|
||||
}
|
||||
|
||||
} // namespace TextUtilities
|
||||
|
||||
namespace Lang {
|
||||
|
|
|
@ -237,6 +237,10 @@ inline QString PrepareForSending(const QString &text, PrepareTextOption option =
|
|||
// Replace bad symbols with space and remove '\r'.
|
||||
void ApplyServerCleaning(TextWithEntities &result);
|
||||
|
||||
QByteArray SerializeTags(const TextWithTags::Tags &tags);
|
||||
TextWithTags::Tags DeserializeTags(QByteArray data, int textLength);
|
||||
QString TagsMimeType();
|
||||
|
||||
} // namespace TextUtilities
|
||||
|
||||
namespace Lang {
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -16,25 +16,36 @@ namespace Ui {
|
|||
|
||||
static UserData * const LookingUpInlineBot = SharedMemoryLocation<UserData, 0>();
|
||||
|
||||
struct InstantReplaces {
|
||||
struct Node {
|
||||
QString text;
|
||||
std::map<QChar, Node> tail;
|
||||
};
|
||||
|
||||
void add(const QString &what, const QString &with);
|
||||
|
||||
static const InstantReplaces &Default();
|
||||
|
||||
int maxLength = 0;
|
||||
Node reverseMap;
|
||||
|
||||
};
|
||||
|
||||
class FlatTextarea : public TWidgetHelper<QTextEdit>, protected base::Subscriber {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
using TagList = TextWithTags::Tags;
|
||||
|
||||
static QByteArray serializeTagsList(const TagList &tags);
|
||||
static TagList deserializeTagsList(QByteArray data, int textLength);
|
||||
static QString tagsMimeType();
|
||||
|
||||
FlatTextarea(QWidget *parent, const style::FlatTextarea &st, base::lambda<QString()> placeholderFactory = base::lambda<QString()>(), const QString &val = QString(), const TagList &tags = TagList());
|
||||
|
||||
void setMaxLength(int maxLength);
|
||||
void setMinHeight(int minHeight);
|
||||
void setMaxHeight(int maxHeight);
|
||||
|
||||
void setInstantReplaces(const InstantReplaces &replaces);
|
||||
void enableInstantReplaces(bool enabled);
|
||||
void addInstantReplace(const QString &what, const QString &with);
|
||||
void commmitInstantReplacement(
|
||||
void commitInstantReplacement(
|
||||
int from,
|
||||
int till,
|
||||
const QString &with,
|
||||
|
@ -150,10 +161,6 @@ protected:
|
|||
void checkContentHeight();
|
||||
|
||||
private:
|
||||
struct InstantReplaceNode {
|
||||
QString text;
|
||||
std::map<QChar, InstantReplaceNode> tail;
|
||||
};
|
||||
void updatePalette();
|
||||
void refreshPlaceholder();
|
||||
|
||||
|
@ -172,6 +179,9 @@ private:
|
|||
// Rule 4 applies only if we inserted chars not in the middle of a tag (but at the end).
|
||||
void processFormatting(int changedPosition, int changedEnd);
|
||||
|
||||
// We don't want accidentally detach InstantReplaces map.
|
||||
// So we access it only by const reference from this method.
|
||||
const InstantReplaces &instantReplaces() const;
|
||||
void processInstantReplaces(const QString &text);
|
||||
void applyInstantReplace(const QString &what, const QString &with);
|
||||
bool revertInstantReplace();
|
||||
|
@ -234,8 +244,7 @@ private:
|
|||
|
||||
QTextCharFormat _defaultCharFormat;
|
||||
|
||||
int _instantReplaceMaxLength = 0;
|
||||
InstantReplaceNode _reverseInstantReplaces;
|
||||
InstantReplaces _mutableInstantReplaces;
|
||||
bool _instantReplacesEnabled = true;
|
||||
|
||||
};
|
||||
|
@ -334,15 +343,33 @@ enum class CtrlEnterSubmit {
|
|||
Both,
|
||||
};
|
||||
|
||||
class InputArea : public RpWidget, private base::Subscriber {
|
||||
class InputField : public RpWidget, private base::Subscriber {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
InputArea(
|
||||
enum class Mode {
|
||||
SingleLine,
|
||||
MultiLine,
|
||||
};
|
||||
using TagList = TextWithTags::Tags;
|
||||
|
||||
InputField(
|
||||
QWidget *parent,
|
||||
const style::InputField &st,
|
||||
base::lambda<QString()> placeholderFactory = base::lambda<QString()>(),
|
||||
const QString &val = QString());
|
||||
base::lambda<QString()> placeholderFactory,
|
||||
const QString &value = QString());
|
||||
InputField(
|
||||
QWidget *parent,
|
||||
const style::InputField &st,
|
||||
Mode mode,
|
||||
base::lambda<QString()> placeholderFactory,
|
||||
const QString &value);
|
||||
InputField(
|
||||
QWidget *parent,
|
||||
const style::InputField &st,
|
||||
Mode mode = Mode::SingleLine,
|
||||
base::lambda<QString()> placeholderFactory = nullptr,
|
||||
const TextWithTags &value = TextWithTags());
|
||||
|
||||
void showError();
|
||||
|
||||
|
@ -350,10 +377,28 @@ public:
|
|||
_maxLength = maxLength;
|
||||
}
|
||||
|
||||
enum class HistoryAction {
|
||||
NewEntry,
|
||||
MergeEntry,
|
||||
Clear,
|
||||
};
|
||||
void setTextWithTags(
|
||||
const TextWithTags &textWithTags,
|
||||
HistoryAction historyAction = HistoryAction::NewEntry);
|
||||
|
||||
void setInstantReplaces(const InstantReplaces &replaces);
|
||||
void enableInstantReplaces(bool enabled);
|
||||
void commitInstantReplacement(
|
||||
int from,
|
||||
int till,
|
||||
const QString &with,
|
||||
base::optional<QString> checkOriginal = base::none);
|
||||
|
||||
const QString &getLastText() const {
|
||||
return _oldtext;
|
||||
return _lastTextWithTags.text;
|
||||
}
|
||||
void setPlaceholder(base::lambda<QString()> placeholderFactory);
|
||||
void setPlaceholderHidden(bool forcePlaceholderHidden);
|
||||
void setDisplayFocused(bool focused);
|
||||
void finishAnimating();
|
||||
void setFocusFast() {
|
||||
|
@ -366,6 +411,7 @@ public:
|
|||
|
||||
QString getText(int start = 0, int end = -1) const;
|
||||
bool hasText() const;
|
||||
void selectAll();
|
||||
|
||||
bool isUndoAvailable() const;
|
||||
bool isRedoAvailable() const;
|
||||
|
@ -373,29 +419,14 @@ public:
|
|||
void customUpDown(bool isCustom);
|
||||
void setCtrlEnterSubmit(CtrlEnterSubmit ctrlEnterSubmit);
|
||||
|
||||
void setTextCursor(const QTextCursor &cursor) {
|
||||
return _inner->setTextCursor(cursor);
|
||||
}
|
||||
QTextCursor textCursor() const {
|
||||
return _inner->textCursor();
|
||||
}
|
||||
void setText(const QString &text) {
|
||||
_inner->setText(text);
|
||||
startPlaceholderAnimation();
|
||||
}
|
||||
void clear() {
|
||||
_inner->clear();
|
||||
startPlaceholderAnimation();
|
||||
}
|
||||
bool hasFocus() const {
|
||||
return _inner->hasFocus();
|
||||
}
|
||||
void setFocus() {
|
||||
_inner->setFocus();
|
||||
}
|
||||
void clearFocus() {
|
||||
_inner->clearFocus();
|
||||
}
|
||||
void setTextCursor(const QTextCursor &cursor);
|
||||
void setCursorPosition(int position);
|
||||
QTextCursor textCursor() const;
|
||||
void setText(const QString &text);
|
||||
void clear();
|
||||
bool hasFocus() const;
|
||||
void setFocus();
|
||||
void clearFocus();
|
||||
|
||||
enum class MimeAction {
|
||||
Check,
|
||||
|
@ -441,7 +472,6 @@ protected:
|
|||
return qobject_cast<const TWidget*>(parentWidget());
|
||||
}
|
||||
|
||||
void touchEvent(QTouchEvent *e);
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
void focusInEvent(QFocusEvent *e) override;
|
||||
void mousePressEvent(QMouseEvent *e) override;
|
||||
|
@ -449,32 +479,13 @@ protected:
|
|||
void resizeEvent(QResizeEvent *e) override;
|
||||
|
||||
private:
|
||||
class Inner : public QTextEdit {
|
||||
public:
|
||||
Inner(InputArea *parent);
|
||||
|
||||
QVariant loadResource(int type, const QUrl &name) override;
|
||||
|
||||
protected:
|
||||
bool viewportEvent(QEvent *e) override;
|
||||
void focusInEvent(QFocusEvent *e) override;
|
||||
void focusOutEvent(QFocusEvent *e) override;
|
||||
void keyPressEvent(QKeyEvent *e) override;
|
||||
void contextMenuEvent(QContextMenuEvent *e) override;
|
||||
|
||||
bool canInsertFromMimeData(const QMimeData *source) const override;
|
||||
void insertFromMimeData(const QMimeData *source) override;
|
||||
QMimeData *createMimeDataFromSelection() const override;
|
||||
|
||||
private:
|
||||
InputArea *f() const {
|
||||
return static_cast<InputArea*>(parentWidget());
|
||||
}
|
||||
friend class InputArea;
|
||||
|
||||
};
|
||||
class Inner;
|
||||
friend class Inner;
|
||||
|
||||
bool viewportEventInner(QEvent *e);
|
||||
QVariant loadResource(int type, const QUrl &name);
|
||||
void handleTouchEvent(QTouchEvent *e);
|
||||
|
||||
void updatePalette();
|
||||
void refreshPlaceholder();
|
||||
|
||||
|
@ -482,19 +493,34 @@ private:
|
|||
void checkContentHeight();
|
||||
void setErrorShown(bool error);
|
||||
|
||||
void focusInInner(bool focusByMouse);
|
||||
void focusOutInner();
|
||||
void focusInEventInner(QFocusEvent *e);
|
||||
void focusOutEventInner(QFocusEvent *e);
|
||||
void setFocused(bool focused);
|
||||
void keyPressEventInner(QKeyEvent *e);
|
||||
void contextMenuEventInner(QContextMenuEvent *e);
|
||||
|
||||
QMimeData *createMimeDataFromSelectionInner() const;
|
||||
bool canInsertFromMimeDataInner(const QMimeData *source) const;
|
||||
void insertFromMimeDataInner(const QMimeData *source);
|
||||
|
||||
void processDocumentContentsChange(int position, int charsAdded);
|
||||
|
||||
// We don't want accidentally detach InstantReplaces map.
|
||||
// So we access it only by const reference from this method.
|
||||
const InstantReplaces &instantReplaces() const;
|
||||
void processInstantReplaces(const QString &text);
|
||||
void applyInstantReplace(const QString &what, const QString &with);
|
||||
bool revertInstantReplace();
|
||||
|
||||
const style::InputField &_st;
|
||||
|
||||
Mode _mode = Mode::SingleLine;
|
||||
int _maxLength = -1;
|
||||
bool _forcePlaceholderHidden = false;
|
||||
|
||||
object_ptr<Inner> _inner;
|
||||
|
||||
QString _oldtext;
|
||||
TextWithTags _lastTextWithTags;
|
||||
|
||||
CtrlEnterSubmit _ctrlEnterSubmit = CtrlEnterSubmit::CtrlEnter;
|
||||
bool _undoAvailable = false;
|
||||
|
@ -529,190 +555,11 @@ private:
|
|||
bool _correcting = false;
|
||||
MimeDataHook _mimeDataHook;
|
||||
|
||||
};
|
||||
QTextCharFormat _defaultCharFormat;
|
||||
|
||||
class InputField : public RpWidget, private base::Subscriber {
|
||||
Q_OBJECT
|
||||
InstantReplaces _mutableInstantReplaces;
|
||||
bool _instantReplacesEnabled = true;
|
||||
|
||||
public:
|
||||
InputField(QWidget *parent, const style::InputField &st, base::lambda<QString()> placeholderFactory = base::lambda<QString()>(), const QString &val = QString());
|
||||
|
||||
void setMaxLength(int maxLength) {
|
||||
_maxLength = maxLength;
|
||||
}
|
||||
|
||||
void showError();
|
||||
|
||||
const QString &getLastText() const {
|
||||
return _oldtext;
|
||||
}
|
||||
void setPlaceholder(base::lambda<QString()> placeholderFactory);
|
||||
void setPlaceholderHidden(bool forcePlaceholderHidden);
|
||||
void setDisplayFocused(bool focused);
|
||||
void finishAnimating();
|
||||
void setFocusFast() {
|
||||
setDisplayFocused(true);
|
||||
setFocus();
|
||||
}
|
||||
|
||||
QSize sizeHint() const override;
|
||||
QSize minimumSizeHint() const override;
|
||||
|
||||
QString getText(int start = 0, int end = -1) const;
|
||||
bool hasText() const;
|
||||
|
||||
bool isUndoAvailable() const;
|
||||
bool isRedoAvailable() const;
|
||||
|
||||
void customUpDown(bool isCustom);
|
||||
|
||||
void setTextCursor(const QTextCursor &cursor) {
|
||||
return _inner->setTextCursor(cursor);
|
||||
}
|
||||
QTextCursor textCursor() const {
|
||||
return _inner->textCursor();
|
||||
}
|
||||
void setText(const QString &text) {
|
||||
_inner->setText(text);
|
||||
startPlaceholderAnimation();
|
||||
}
|
||||
void clear() {
|
||||
_inner->clear();
|
||||
startPlaceholderAnimation();
|
||||
}
|
||||
bool hasFocus() const {
|
||||
return _inner->hasFocus();
|
||||
}
|
||||
void setFocus() {
|
||||
_inner->setFocus();
|
||||
auto cursor = _inner->textCursor();
|
||||
cursor.movePosition(QTextCursor::End);
|
||||
_inner->setTextCursor(cursor);
|
||||
}
|
||||
void clearFocus() {
|
||||
_inner->clearFocus();
|
||||
}
|
||||
void setCursorPosition(int pos) {
|
||||
auto cursor = _inner->textCursor();
|
||||
cursor.setPosition(pos);
|
||||
_inner->setTextCursor(cursor);
|
||||
}
|
||||
|
||||
public slots:
|
||||
void selectAll();
|
||||
|
||||
private slots:
|
||||
void onTouchTimer();
|
||||
|
||||
void onDocumentContentsChange(int position, int charsRemoved, int charsAdded);
|
||||
void onDocumentContentsChanged();
|
||||
|
||||
void onUndoAvailable(bool avail);
|
||||
void onRedoAvailable(bool avail);
|
||||
|
||||
void onFocusInner();
|
||||
|
||||
signals:
|
||||
void changed();
|
||||
void submitted(bool ctrlShiftEnter);
|
||||
void cancelled();
|
||||
void tabbed();
|
||||
|
||||
void focused();
|
||||
void blurred();
|
||||
|
||||
protected:
|
||||
void startPlaceholderAnimation();
|
||||
void startBorderAnimation();
|
||||
|
||||
void insertEmoji(EmojiPtr emoji, QTextCursor c);
|
||||
TWidget *tparent() {
|
||||
return qobject_cast<TWidget*>(parentWidget());
|
||||
}
|
||||
const TWidget *tparent() const {
|
||||
return qobject_cast<const TWidget*>(parentWidget());
|
||||
}
|
||||
|
||||
void touchEvent(QTouchEvent *e);
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
void focusInEvent(QFocusEvent *e) override;
|
||||
void mousePressEvent(QMouseEvent *e) override;
|
||||
void contextMenuEvent(QContextMenuEvent *e) override;
|
||||
void resizeEvent(QResizeEvent *e) override;
|
||||
|
||||
private:
|
||||
class Inner : public QTextEdit {
|
||||
public:
|
||||
Inner(InputField *parent);
|
||||
|
||||
QVariant loadResource(int type, const QUrl &name) override;
|
||||
|
||||
protected:
|
||||
bool viewportEvent(QEvent *e) override;
|
||||
void focusInEvent(QFocusEvent *e) override;
|
||||
void focusOutEvent(QFocusEvent *e) override;
|
||||
void keyPressEvent(QKeyEvent *e) override;
|
||||
void contextMenuEvent(QContextMenuEvent *e) override;
|
||||
|
||||
QMimeData *createMimeDataFromSelection() const override;
|
||||
|
||||
private:
|
||||
InputField *f() const {
|
||||
return static_cast<InputField*>(parentWidget());
|
||||
}
|
||||
friend class InputField;
|
||||
|
||||
};
|
||||
friend class Inner;
|
||||
|
||||
void updatePalette();
|
||||
void refreshPlaceholder();
|
||||
void setErrorShown(bool error);
|
||||
|
||||
void focusInInner(bool focusByMouse);
|
||||
void focusOutInner();
|
||||
void setFocused(bool focused);
|
||||
|
||||
void processDocumentContentsChange(int position, int charsAdded);
|
||||
|
||||
const style::InputField &_st;
|
||||
|
||||
std::unique_ptr<Inner> _inner;
|
||||
|
||||
int _maxLength = -1;
|
||||
bool _forcePlaceholderHidden = false;
|
||||
|
||||
QString _oldtext;
|
||||
|
||||
bool _undoAvailable = false;
|
||||
bool _redoAvailable = false;
|
||||
|
||||
bool _customUpDown = true;
|
||||
|
||||
QString _placeholder;
|
||||
base::lambda<QString()> _placeholderFactory;
|
||||
Animation _a_placeholderShifted;
|
||||
bool _placeholderShifted = false;
|
||||
QPainterPath _placeholderPath;
|
||||
|
||||
Animation _a_borderShown;
|
||||
int _borderAnimationStart = 0;
|
||||
Animation _a_borderOpacity;
|
||||
bool _borderVisible = false;
|
||||
|
||||
Animation _a_focused;
|
||||
Animation _a_error;
|
||||
|
||||
bool _focused = false;
|
||||
bool _error = false;
|
||||
|
||||
QTimer _touchTimer;
|
||||
bool _touchPress = false;
|
||||
bool _touchRightButton = false;
|
||||
bool _touchMove = false;
|
||||
QPoint _touchStart;
|
||||
|
||||
bool _correcting = false;
|
||||
};
|
||||
|
||||
class MaskedInputField
|
||||
|
|
|
@ -760,13 +760,18 @@ void Notification::showReplyField() {
|
|||
_background->setGeometry(0, st::notifyMinHeight, width(), st::notifySendReply.height + st::notifyBorderWidth);
|
||||
_background->show();
|
||||
|
||||
_replyArea.create(this, st::notifyReplyArea, langFactory(lng_message_ph), QString());
|
||||
_replyArea.create(
|
||||
this,
|
||||
st::notifyReplyArea,
|
||||
Ui::InputField::Mode::MultiLine,
|
||||
langFactory(lng_message_ph));
|
||||
_replyArea->resize(width() - st::notifySendReply.width - 2 * st::notifyBorderWidth, st::notifySendReply.height);
|
||||
_replyArea->moveToLeft(st::notifyBorderWidth, st::notifyMinHeight);
|
||||
_replyArea->show();
|
||||
_replyArea->setFocus();
|
||||
_replyArea->setMaxLength(MaxMessageSize);
|
||||
_replyArea->setCtrlEnterSubmit(Ui::CtrlEnterSubmit::Both);
|
||||
_replyArea->setInstantReplaces(Ui::InstantReplaces::Default());
|
||||
|
||||
// Catch mouse press event to activate the window.
|
||||
QCoreApplication::instance()->installEventFilter(this);
|
||||
|
|
|
@ -13,7 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
namespace Ui {
|
||||
class IconButton;
|
||||
class RoundButton;
|
||||
class InputArea;
|
||||
class InputField;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Window {
|
||||
|
@ -233,7 +233,7 @@ private:
|
|||
object_ptr<Ui::IconButton> _close;
|
||||
object_ptr<Ui::RoundButton> _reply;
|
||||
object_ptr<Background> _background = { nullptr };
|
||||
object_ptr<Ui::InputArea> _replyArea = { nullptr };
|
||||
object_ptr<Ui::InputField> _replyArea = { nullptr };
|
||||
object_ptr<Ui::IconButton> _replySend = { nullptr };
|
||||
bool _waitingForInput = true;
|
||||
|
||||
|
|
Loading…
Reference in New Issue