Add emoji autocomplete to all fields.

This commit is contained in:
John Preston 2018-11-21 14:09:46 +04:00
parent a1c61daea6
commit 9f5b09c263
22 changed files with 265 additions and 232 deletions

View file

@ -375,6 +375,10 @@ void AbstractBox::updateButtonsPositions() {
}
}
QPointer<QWidget> AbstractBox::outerContainer() {
return parentWidget();
}
void AbstractBox::updateTitlePosition() {
_titleLeft = _layerType ? st::boxLayerTitlePosition.x() : st::boxTitlePosition.x();
_titleTop = _layerType ? st::boxLayerTitlePosition.y() : st::boxTitlePosition.y();

View file

@ -56,6 +56,8 @@ public:
return result;
}
virtual QPointer<QWidget> outerContainer() = 0;
};
class BoxContent : public Ui::RpWidget, protected base::Subscriber {
@ -247,6 +249,7 @@ public:
QPointer<Ui::RoundButton> addButton(Fn<QString()> textFactory, Fn<void()> clickCallback, const style::RoundButton &st) override;
QPointer<Ui::RoundButton> addLeftButton(Fn<QString()> textFactory, Fn<void()> clickCallback, const style::RoundButton &st) override;
void updateButtonsPositions() override;
QPointer<QWidget> outerContainer() override;
void setDimensions(int newWidth, int maxHeight) override;

View file

@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/photo_crop_box.h"
#include "boxes/peer_list_controllers.h"
#include "core/file_utilities.h"
#include "chat_helpers/emoji_suggestions_widget.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/input_fields.h"
@ -342,6 +343,9 @@ void GroupInfoBox::prepare() {
_title->setMaxLength(kMaxGroupChannelTitle);
_title->setInstantReplaces(Ui::InstantReplaces::Default());
_title ->setInstantReplacesEnabled(Global::ReplaceEmojiValue());
Ui::Emoji::SuggestionsController::Init(
getDelegate()->outerContainer(),
_title);
if (_creating == CreatingGroupChannel) {
_description.create(
@ -358,6 +362,10 @@ void GroupInfoBox::prepare() {
connect(_description, &Ui::InputField::resized, [=] { descriptionResized(); });
connect(_description, &Ui::InputField::submitted, [=] { submit(); });
connect(_description, &Ui::InputField::cancelled, [=] { closeBox(); });
Ui::Emoji::SuggestionsController::Init(
getDelegate()->outerContainer(),
_description);
}
connect(_title, &Ui::InputField::submitted, [=] { submitName(); });
@ -1080,83 +1088,6 @@ bool EditNameBox::saveSelfFail(const RPCError &error) {
return true;
}
EditBioBox::EditBioBox(QWidget*, not_null<UserData*> self) : BoxContent()
, _dynamicFieldStyle(CreateBioFieldStyle())
, _self(self)
, _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) {
}
void EditBioBox::prepare() {
setTitle(langFactory(lng_bio_title));
addButton(langFactory(lng_settings_save), [this] { save(); });
addButton(langFactory(lng_cancel), [this] { closeBox(); });
_bio->setMaxLength(kMaxBioLength);
_bio->setSubmitSettings(Ui::InputField::SubmitSettings::Both);
auto cursor = _bio->textCursor();
cursor.setPosition(_bio->getLastText().size());
_bio->setTextCursor(cursor);
connect(_bio, &Ui::InputField::submitted, [=] { save(); });
connect(_bio, &Ui::InputField::resized, [=] { updateMaxHeight(); });
connect(_bio, &Ui::InputField::changed, [=] { handleBioUpdated(); });
_bio->setInstantReplaces(Ui::InstantReplaces::Default());
_bio->setInstantReplacesEnabled(Global::ReplaceEmojiValue());
handleBioUpdated();
updateMaxHeight();
}
void EditBioBox::updateMaxHeight() {
auto newHeight = st::contactPadding.top() + _bio->height() + st::boxLittleSkip + _about->height() + st::boxPadding.bottom() + st::contactPadding.bottom();
setDimensions(st::boxWideWidth, newHeight);
}
void EditBioBox::handleBioUpdated() {
auto text = _bio->getLastText();
if (text.indexOf('\n') >= 0) {
auto position = _bio->textCursor().position();
_bio->setText(text.replace('\n', ' '));
auto cursor = _bio->textCursor();
cursor.setPosition(position);
_bio->setTextCursor(cursor);
}
auto countLeft = qMax(kMaxBioLength - text.size(), 0);
_countdown->setText(QString::number(countLeft));
}
void EditBioBox::setInnerFocus() {
_bio->setFocusFast();
}
void EditBioBox::resizeEvent(QResizeEvent *e) {
BoxContent::resizeEvent(e);
_bio->resize(width() - st::boxPadding.left() - st::newGroupInfoPadding.left() - st::boxPadding.right(), _bio->height());
_bio->moveToLeft(st::boxPadding.left() + st::newGroupInfoPadding.left(), st::contactPadding.top());
_countdown->moveToRight(st::boxPadding.right(), _bio->y() + _dynamicFieldStyle.textMargins.top());
_about->moveToLeft(st::boxPadding.left(), _bio->y() + _bio->height() + st::boxLittleSkip);
}
void EditBioBox::save() {
if (_requestId) return;
auto text = TextUtilities::PrepareForSending(_bio->getLastText());
_sentBio = text;
auto flags = MTPaccount_UpdateProfile::Flag::f_about;
_requestId = request(MTPaccount_UpdateProfile(MTP_flags(flags), MTPstring(), MTPstring(), MTP_string(text))).done([this](const MTPUser &result) {
App::feedUsers(MTP_vector<MTPUser>(1, result));
_self->setAbout(_sentBio);
closeBox();
}).send();
}
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)
@ -1190,6 +1121,10 @@ void EditChannelBox::prepare() {
_title->setMaxLength(kMaxGroupChannelTitle);
_title->setInstantReplaces(Ui::InstantReplaces::Default());
_title->setInstantReplacesEnabled(Global::ReplaceEmojiValue());
Ui::Emoji::SuggestionsController::Init(
getDelegate()->outerContainer(),
_title);
_description->setMaxLength(kMaxChannelDescription);
_description->setInstantReplaces(Ui::InstantReplaces::Default());
_description->setInstantReplacesEnabled(Global::ReplaceEmojiValue());
@ -1197,6 +1132,9 @@ void EditChannelBox::prepare() {
connect(_description, &Ui::InputField::resized, [=] { descriptionResized(); });
connect(_description, &Ui::InputField::submitted, [=] { save(); });
connect(_description, &Ui::InputField::cancelled, [=] { closeBox(); });
Ui::Emoji::SuggestionsController::Init(
getDelegate()->outerContainer(),
_description);
_publicLink->addClickHandler([=] { setupPublicLink(); });
_publicLink->setVisible(_channel->canEditUsername());

View file

@ -201,32 +201,6 @@ private:
};
class EditBioBox : public BoxContent, private MTP::Sender {
public:
EditBioBox(QWidget*, not_null<UserData*> self);
protected:
void setInnerFocus() override;
void prepare() override;
void resizeEvent(QResizeEvent *e) override;
private:
void updateMaxHeight();
void handleBioUpdated();
void save();
style::InputField _dynamicFieldStyle;
not_null<UserData*> _self;
object_ptr<Ui::InputField> _bio;
object_ptr<Ui::FlatLabel> _countdown;
object_ptr<Ui::FlatLabel> _about;
mtpRequestId _requestId = 0;
QString _sentBio;
};
class EditChannelBox : public BoxContent, public RPCSender {
public:
EditChannelBox(QWidget*, not_null<ChannelData*> channel);

View file

@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_document.h"
#include "lang/lang_keys.h"
#include "chat_helpers/message_field.h"
#include "chat_helpers/emoji_suggestions_widget.h"
#include "window/window_controller.h"
#include "mainwidget.h"
#include "layout.h"
@ -261,6 +262,9 @@ void EditCaptionBox::prepare() {
connect(_field, &Ui::InputField::submitted, [=] { save(); });
connect(_field, &Ui::InputField::cancelled, [=] { closeBox(); });
connect(_field, &Ui::InputField::resized, [=] { captionResized(); });
Ui::Emoji::SuggestionsController::Init(
getDelegate()->outerContainer(),
_field);
auto cursor = _field->textCursor();
cursor.movePosition(QTextCursor::End);

View file

@ -238,7 +238,7 @@ void EditPrivacyBox::setupContent() {
const auto group = std::make_shared<Ui::RadioenumGroup<Option>>(
_value.option);
const auto toggle = Ui::AttachAsChild(content, rpl::event_stream<>());
const auto toggle = Ui::CreateChild<rpl::event_stream<>>(content);
group->setChangedCallback([=](Option value) {
_value.option = value;
@ -251,9 +251,7 @@ void EditPrivacyBox::setupContent() {
: nullptr;
};
const auto addExceptionLink = [=](Exception exception) {
const auto update = Ui::AttachAsChild(
content,
rpl::event_stream<>());
const auto update = Ui::CreateChild<rpl::event_stream<>>(content);
auto label = update->events_starting_with(
rpl::empty_value()
) | rpl::map([=] {

View file

@ -23,6 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/add_contact_box.h"
#include "boxes/stickers_box.h"
#include "boxes/peer_list_controllers.h"
#include "chat_helpers/emoji_suggestions_widget.h"
#include "mtproto/sender.h"
#include "lang/lang_keys.h"
#include "mainwidget.h"
@ -303,6 +304,9 @@ object_ptr<Ui::RpWidget> Controller::createTitleEdit() {
result->entity()->setInstantReplaces(Ui::InstantReplaces::Default());
result->entity()->setInstantReplacesEnabled(
Global::ReplaceEmojiValue());
Ui::Emoji::SuggestionsController::Init(
_wrap->window(),
result->entity());
QObject::connect(
result->entity(),
@ -334,6 +338,9 @@ object_ptr<Ui::RpWidget> Controller::createDescriptionEdit() {
result->entity()->setInstantReplaces(Ui::InstantReplaces::Default());
result->entity()->setInstantReplacesEnabled(
Global::ReplaceEmojiValue());
Ui::Emoji::SuggestionsController::Init(
_wrap->window(),
result->entity());
QObject::connect(
result->entity(),
@ -1430,10 +1437,10 @@ EditPeerInfoBox::EditPeerInfoBox(
}
void EditPeerInfoBox::prepare() {
auto controller = std::make_unique<Controller>(this, _peer);
auto controller = Ui::CreateChild<Controller>(this, this, _peer);
_focusRequests.events(
) | rpl::start_with_next(
[c = controller.get()] { c->setFocus(); },
[=] { controller->setFocus(); },
lifetime());
auto content = controller->createContent();
content->heightValue(
@ -1443,5 +1450,4 @@ void EditPeerInfoBox::prepare() {
setInnerWidget(object_ptr<Ui::OverrideMargins>(
this,
std::move(content)));
Ui::AttachAsChild(this, std::move(controller));
}

View file

@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "mainwidget.h"
#include "history/history_media_types.h"
#include "chat_helpers/message_field.h"
#include "chat_helpers/emoji_suggestions_widget.h"
#include "core/file_utilities.h"
#include "core/mime_type.h"
#include "ui/widgets/checkbox.h"
@ -1583,6 +1584,9 @@ void SendFilesBox::setupCaption() {
_caption->setInstantReplacesEnabled(Global::ReplaceEmojiValue());
_caption->setMarkdownReplacesEnabled(rpl::single(true));
_caption->setEditLinkCallback(DefaultEditLinkCallback(_caption));
Ui::Emoji::SuggestionsController::Init(
getDelegate()->outerContainer(),
_caption);
}
void SendFilesBox::captionResized() {

View file

@ -29,6 +29,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history_message.h"
#include "window/themes/window_theme.h"
#include "boxes/peer_list_box.h"
#include "chat_helpers/emoji_suggestions_widget.h"
#include "auth_session.h"
#include "messenger.h"
#include "styles/style_boxes.h"
@ -252,6 +253,10 @@ void ShareBox::prepare() {
innerSelectedChanged(peer, checked);
});
Ui::Emoji::SuggestionsController::Init(
getDelegate()->outerContainer(),
_comment->entity());
_select->raise();
}

View file

@ -11,8 +11,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/effects/ripple_animation.h"
#include "ui/widgets/shadow.h"
#include "ui/widgets/inner_dropdown.h"
#include "ui/widgets/input_fields.h"
#include "ui/emoji_config.h"
#include "platform/platform_specific.h"
#include "core/event_filter.h"
#include "styles/style_chat_helpers.h"
namespace Ui {
@ -73,6 +75,14 @@ SuggestionsWidget::SuggestionsWidget(QWidget *parent, const style::Menu &st) : T
setMouseTracking(true);
}
rpl::producer<bool> SuggestionsWidget::toggleAnimated() const {
return _toggleAnimated.events();
}
rpl::producer<QString> SuggestionsWidget::triggered() const {
return _triggered.events();
}
void SuggestionsWidget::showWithQuery(const QString &query) {
if (_query == query) {
return;
@ -80,7 +90,7 @@ void SuggestionsWidget::showWithQuery(const QString &query) {
_query = query;
auto rows = getRowsByQuery();
if (rows.empty()) {
toggleAnimated.notify(false, true);
_toggleAnimated.fire(false);
}
clearSelection();
_rows = std::move(rows);
@ -90,7 +100,7 @@ void SuggestionsWidget::showWithQuery(const QString &query) {
setSelected(0);
}
if (!_rows.empty()) {
toggleAnimated.notify(true, true);
_toggleAnimated.fire(true);
}
}
@ -336,7 +346,7 @@ void SuggestionsWidget::triggerSelectedRow() {
}
void SuggestionsWidget::triggerRow(const Row &row) {
triggered.notify(row.emoji()->text(), true);
_triggered.fire(row.emoji()->text());
}
void SuggestionsWidget::enterEventHook(QEvent *e) {
@ -352,26 +362,68 @@ void SuggestionsWidget::leaveEventHook(QEvent *e) {
return TWidget::leaveEventHook(e);
}
SuggestionsController::SuggestionsController(QWidget *parent, not_null<QTextEdit*> field)
: QObject(nullptr)
, _field(field)
, _container(parent, st::emojiSuggestionsDropdown)
, _suggestions(_container->setOwnedWidget(object_ptr<Ui::Emoji::SuggestionsWidget>(parent, st::emojiSuggestionsMenu))) {
SuggestionsController::SuggestionsController(
not_null<QWidget*> outer,
not_null<QTextEdit*> field)
: _field(field) {
_container = base::make_unique_q<InnerDropdown>(
outer,
st::emojiSuggestionsDropdown);
_container->setAutoHiding(false);
_suggestions = _container->setOwnedWidget(
object_ptr<Ui::Emoji::SuggestionsWidget>(
_container,
st::emojiSuggestionsMenu));
setReplaceCallback(nullptr);
_field->installEventFilter(this);
connect(_field, &QTextEdit::textChanged, this, [this] { handleTextChange(); });
connect(_field, &QTextEdit::cursorPositionChanged, this, [this] { handleCursorPositionChange(); });
_fieldFilter.reset(Core::InstallEventFilter(
_field,
[=](not_null<QEvent*> event) { return fieldFilter(event); }));
_outerFilter.reset(Core::InstallEventFilter(
outer,
[=](not_null<QEvent*> event) { return outerFilter(event); }));
QObject::connect(
_field,
&QTextEdit::textChanged,
_container,
[=] { handleTextChange(); });
QObject::connect(
_field,
&QTextEdit::cursorPositionChanged,
_container,
[=] { handleCursorPositionChange(); });
_suggestions->toggleAnimated(
) | rpl::start_with_next([=](bool visible) {
suggestionsUpdated(visible);
}, _lifetime);
_suggestions->triggered(
) | rpl::start_with_next([=](QString replacement) {
replaceCurrent(replacement);
}, _lifetime);
subscribe(_suggestions->toggleAnimated, [this](bool visible) { suggestionsUpdated(visible); });
subscribe(_suggestions->triggered, [this](QString replacement) { replaceCurrent(replacement); });
updateForceHidden();
handleTextChange();
}
SuggestionsController *SuggestionsController::Init(
not_null<QWidget*> outer,
not_null<Ui::InputField*> field) {
const auto result = Ui::CreateChild<SuggestionsController>(
field.get(),
outer,
field->rawTextEdit());
result->setReplaceCallback([=](
int from,
int till,
const QString &replacement) {
field->commitInstantReplacement(from, till, replacement);
});
return result;
}
void SuggestionsController::setReplaceCallback(
Fn<void(
int from,
@ -391,9 +443,9 @@ void SuggestionsController::setReplaceCallback(
void SuggestionsController::handleTextChange() {
_ignoreCursorPositionChange = true;
InvokeQueued(this, [this] { _ignoreCursorPositionChange = false; });
InvokeQueued(_container, [=] { _ignoreCursorPositionChange = false; });
auto query = getEmojiQuery();
const auto query = getEmojiQuery();
if (query.isEmpty() || _textChangeAfterKeyPress) {
_suggestions->showWithQuery(query);
}
@ -509,7 +561,7 @@ void SuggestionsController::replaceCurrent(const QString &replacement) {
}
void SuggestionsController::handleCursorPositionChange() {
InvokeQueued(this, [this] {
InvokeQueued(_container, [=] {
if (_ignoreCursorPositionChange) {
return;
}
@ -523,7 +575,11 @@ void SuggestionsController::suggestionsUpdated(bool visible) {
_container->resizeToContent();
updateGeometry();
if (!_forceHidden) {
_container->showAnimated(Ui::PanelAnimation::Origin::BottomLeft);
if (_container->isHidden() || _container->isHiding()) {
raise();
}
_container->showAnimated(
Ui::PanelAnimation::Origin::BottomLeft);
}
} else if (!_forceHidden) {
_container->hideAnimated();
@ -563,7 +619,7 @@ void SuggestionsController::updateGeometry() {
}
void SuggestionsController::updateForceHidden() {
_forceHidden = !_field->isVisible();
_forceHidden = !_field->isVisible() || !_field->hasFocus();
if (_forceHidden) {
_container->hideFast();
} else if (_shown) {
@ -571,51 +627,64 @@ void SuggestionsController::updateForceHidden() {
}
}
bool SuggestionsController::eventFilter(QObject *object, QEvent *event) {
if (object == _field) {
auto type = event->type();
switch (type) {
case QEvent::Move:
case QEvent::Resize: {
if (_shown) {
updateGeometry();
}
} break;
case QEvent::Show:
case QEvent::ShowToParent:
case QEvent::Hide:
case QEvent::HideToParent: {
updateForceHidden();
} break;
case QEvent::KeyPress: {
auto key = static_cast<QKeyEvent*>(event)->key();
switch (key) {
case Qt::Key_Enter:
case Qt::Key_Return:
case Qt::Key_Tab:
case Qt::Key_Up:
case Qt::Key_Down:
if (_shown && !_forceHidden) {
_suggestions->handleKeyEvent(key);
return true;
}
break;
case Qt::Key_Escape:
if (_shown && !_forceHidden) {
_suggestions->showWithQuery(QString());
return true;
}
break;
}
_textChangeAfterKeyPress = true;
InvokeQueued(this, [this] { _textChangeAfterKeyPress = false; });
} break;
bool SuggestionsController::fieldFilter(not_null<QEvent*> event) {
auto type = event->type();
switch (type) {
case QEvent::Move:
case QEvent::Resize: {
if (_shown) {
updateGeometry();
}
} break;
case QEvent::Show:
case QEvent::ShowToParent:
case QEvent::Hide:
case QEvent::HideToParent:
case QEvent::FocusIn:
case QEvent::FocusOut: {
updateForceHidden();
} break;
case QEvent::KeyPress: {
const auto key = static_cast<QKeyEvent*>(event.get())->key();
switch (key) {
case Qt::Key_Enter:
case Qt::Key_Return:
case Qt::Key_Tab:
case Qt::Key_Up:
case Qt::Key_Down:
if (_shown && !_forceHidden) {
_suggestions->handleKeyEvent(key);
return true;
}
break;
case Qt::Key_Escape:
if (_shown && !_forceHidden) {
_suggestions->showWithQuery(QString());
return true;
}
break;
}
_textChangeAfterKeyPress = true;
InvokeQueued(_container, [=] { _textChangeAfterKeyPress = false; });
} break;
}
return QObject::eventFilter(object, event);
return false;
}
bool SuggestionsController::outerFilter(not_null<QEvent*> event) {
auto type = event->type();
switch (type) {
case QEvent::Move:
case QEvent::Resize: {
if (_shown) {
updateGeometry();
}
} break;
}
return false;
}
void SuggestionsController::raise() {

View file

@ -8,10 +8,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once
#include "ui/effects/panel_animation.h"
#include "base/unique_qptr.h"
namespace Ui {
class InnerDropdown;
class InputField;
namespace Emoji {
@ -22,8 +24,8 @@ public:
void showWithQuery(const QString &query);
void handleKeyEvent(int key);
base::Observable<bool> toggleAnimated;
base::Observable<QString> triggered;
rpl::producer<bool> toggleAnimated() const;
rpl::producer<QString> triggered() const;
protected:
void paintEvent(QPaintEvent *e) override;
@ -61,11 +63,16 @@ private:
int _selected = -1;
int _pressed = -1;
rpl::event_stream<bool> _toggleAnimated;
rpl::event_stream<QString> _triggered;
};
class SuggestionsController : public QObject, private base::Subscriber {
class SuggestionsController {
public:
SuggestionsController(QWidget *parent, not_null<QTextEdit*> field);
SuggestionsController(
not_null<QWidget*> outer,
not_null<QTextEdit*> field);
void raise();
void setReplaceCallback(Fn<void(
@ -73,8 +80,9 @@ public:
int till,
const QString &replacement)> callback);
protected:
bool eventFilter(QObject *object, QEvent *event) override;
static SuggestionsController *Init(
not_null<QWidget*> outer,
not_null<Ui::InputField*> field);
private:
void handleCursorPositionChange();
@ -84,6 +92,8 @@ private:
void updateGeometry();
void updateForceHidden();
void replaceCurrent(const QString &replacement);
bool fieldFilter(not_null<QEvent*> event);
bool outerFilter(not_null<QEvent*> event);
bool _shown = false;
bool _forceHidden = false;
@ -95,8 +105,12 @@ private:
int from,
int till,
const QString &replacement)> _replaceCallback;
object_ptr<InnerDropdown> _container;
base::unique_qptr<InnerDropdown> _container;
QPointer<SuggestionsWidget> _suggestions;
base::unique_qptr<QObject> _fieldFilter;
base::unique_qptr<QObject> _outerFilter;
rpl::lifetime _lifetime;
};

View file

@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/qthelp_url.h"
#include "boxes/abstract_box.h"
#include "ui/wrap/vertical_layout.h"
#include "chat_helpers/emoji_suggestions_widget.h"
#include "window/window_controller.h"
#include "lang/lang_keys.h"
#include "mainwindow.h"
@ -112,6 +113,9 @@ void EditLinkBox::prepare() {
st::markdownLinkFieldPadding);
text->setInstantReplaces(Ui::InstantReplaces::Default());
text->setInstantReplacesEnabled(Global::ReplaceEmojiValue());
Ui::Emoji::SuggestionsController::Init(
getDelegate()->outerContainer(),
text);
const auto url = content->add(
object_ptr<Ui::InputField>(

View file

@ -558,13 +558,10 @@ HistoryWidget::HistoryWidget(
Unexpected("action in MimeData hook.");
});
_emojiSuggestions.create(this, _field->rawTextEdit());
_emojiSuggestions->setReplaceCallback([=](
int from,
int till,
const QString &replacement) {
_field->commitInstantReplacement(from, till, replacement);
});
const auto suggestions = Ui::Emoji::SuggestionsController::Init(
this,
_field);
_raiseEmojiSuggestions = [=] { suggestions->raise(); };
updateFieldSubmitSettings();
_field->hide();
@ -1180,7 +1177,7 @@ void HistoryWidget::orderWidgets() {
if (_tabbedPanel) {
_tabbedPanel->raise();
}
_emojiSuggestions->raise();
_raiseEmojiSuggestions();
if (_tabbedSelectorToggleTooltip) {
_tabbedSelectorToggleTooltip->raise();
}

View file

@ -55,9 +55,6 @@ class SilentToggle;
class FlatButton;
class LinkButton;
class RoundButton;
namespace Emoji {
class SuggestionsController;
} // namespace Emoji
} // namespace Ui
namespace Window {
@ -861,7 +858,7 @@ private:
DragState _attachDragState;
object_ptr<DragArea> _attachDragDocument, _attachDragPhoto;
object_ptr<Ui::Emoji::SuggestionsController> _emojiSuggestions = { nullptr };
Fn<void()> _raiseEmojiSuggestions;
bool _nonEmptySelection = false;

View file

@ -77,12 +77,10 @@ void SetupUpdate(not_null<Ui::VerticalLayout*> container) {
return;
}
const auto texts = Ui::AttachAsChild(
container,
rpl::event_stream<QString>());
const auto downloading = Ui::AttachAsChild(
container,
rpl::event_stream<bool>());
const auto texts = Ui::CreateChild<rpl::event_stream<QString>>(
container.get());
const auto downloading = Ui::CreateChild<rpl::event_stream<bool>>(
container.get());
const auto version = lng_settings_current_version(
lt_version,
currentVersionText());

View file

@ -578,9 +578,7 @@ void SetupDataStorage(not_null<Ui::VerticalLayout*> container) {
)->toggleOn(rpl::single(Global::AskDownloadPath()));
#ifndef OS_WIN_STORE
const auto showpath = Ui::AttachAsChild(
ask,
rpl::event_stream<bool>());
const auto showpath = Ui::CreateChild<rpl::event_stream<bool>>(ask);
const auto path = container->add(
object_ptr<Ui::SlideWrap<Button>>(
container,

View file

@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/input_fields.h"
#include "ui/widgets/popup_menu.h"
#include "ui/special_buttons.h"
#include "chat_helpers/emoji_suggestions_widget.h"
#include "boxes/add_contact_box.h"
#include "boxes/confirm_box.h"
#include "boxes/change_phone_box.h"
@ -125,7 +126,7 @@ void AddRow(
rpl::single(QString()),
st::settingsInfoRow,
&icon);
const auto forcopy = Ui::AttachAsChild(wrap, QString());
const auto forcopy = Ui::CreateChild<QString>(wrap.get());
wrap->setAcceptBoth();
wrap->clicks(
) | rpl::filter([=] {
@ -287,9 +288,8 @@ BioManager SetupBio(
};
const auto style = Ui::AttachAsChild(container, bioStyle());
const auto current = Ui::AttachAsChild(container, self->about());
const auto changed = Ui::AttachAsChild(
container,
rpl::event_stream<bool>());
const auto changed = Ui::CreateChild<rpl::event_stream<bool>>(
container.get());
const auto bio = container->add(
object_ptr<Ui::InputField>(
container,
@ -350,7 +350,7 @@ BioManager SetupBio(
}
}, bio->lifetime());
const auto generation = Ui::AttachAsChild(bio, 0);
const auto generation = Ui::CreateChild<int>(bio);
changed->events(
) | rpl::start_with_next([=](bool changed) {
if (changed) {
@ -385,6 +385,7 @@ BioManager SetupBio(
QObject::connect(bio, &Ui::InputField::changed, updated);
bio->setInstantReplaces(Ui::InstantReplaces::Default());
bio->setInstantReplacesEnabled(Global::ReplaceEmojiValue());
Ui::Emoji::SuggestionsController::Init(container->window(), bio);
updated();
container->add(

View file

@ -36,7 +36,7 @@ void SetupLanguageButton(
rpl::single(Lang::Current().nativeName()),
icon ? st::settingsSectionButton : st::settingsButton,
icon ? &st::settingsIconLanguage : nullptr);
const auto guard = Ui::AttachAsChild(button, base::binary_guard());
const auto guard = Ui::CreateChild<base::binary_guard>(button.get());
button->addClickHandler([=] {
const auto m = button->clickModifiers();
if ((m & Qt::ShiftModifier) && (m & Qt::AltModifier)) {
@ -108,9 +108,8 @@ void SetupInterfaceScale(
return;
}
const auto toggled = Ui::AttachAsChild(
container,
rpl::event_stream<bool>());
const auto toggled = Ui::CreateChild<rpl::event_stream<bool>>(
container.get());
const auto switched = (cConfigScale() == kInterfaceScaleAuto);
const auto button = AddButton(
@ -138,7 +137,7 @@ void SetupInterfaceScale(
}
return (result == ScaleValues.size()) ? (result - 1) : result;
};
const auto inSetScale = Ui::AttachAsChild(container, false);
const auto inSetScale = Ui::CreateChild<bool>(container.get());
const auto setScale = std::make_shared<Fn<void(int)>>();
*setScale = [=](int scale) {
if (*inSetScale) return;

View file

@ -117,7 +117,7 @@ void SetupPrivacy(not_null<Ui::VerticalLayout*> container) {
});
};
const auto add = [&](LangKey label, Privacy::Key key, auto controller) {
const auto shower = Ui::AttachAsChild(container, rpl::lifetime());
const auto shower = Ui::CreateChild<rpl::lifetime>(container.get());
AddButtonWithLabel(
container,
label,

View file

@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/text/text_entity.h"
#include "ui/text_options.h"
#include "chat_helpers/message_field.h"
#include "chat_helpers/emoji_suggestions_widget.h"
#include "lang/lang_keys.h"
#include "window/window_controller.h"
#include "auth_session.h"
@ -84,6 +85,9 @@ void EditInfoBox::prepare() {
connect(_field, &Ui::InputField::submitted, save);
connect(_field, &Ui::InputField::cancelled, [=] { closeBox(); });
Ui::Emoji::SuggestionsController::Init(
getDelegate()->outerContainer(),
_field);
auto cursor = _field->textCursor();
cursor.movePosition(QTextCursor::End);

View file

@ -15,15 +15,27 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Ui {
namespace details {
struct ForwardTag {
};
struct InPlaceTag {
};
template <typename Value>
class AttachmentOwner : public QObject {
public:
template <typename OtherValue>
AttachmentOwner(QObject *parent, OtherValue &&value)
AttachmentOwner(QObject *parent, const ForwardTag&, OtherValue &&value)
: QObject(parent)
, _value(std::forward<OtherValue>(value)) {
}
template <typename ...Args>
AttachmentOwner(QObject *parent, const InPlaceTag&, Args &&...args)
: QObject(parent)
, _value(std::forward<Args>(args)...) {
}
not_null<Value*> value() {
return &_value;
}
@ -44,12 +56,20 @@ inline base::unique_qptr<Widget> CreateObject(Args &&...args) {
std::forward<Args>(args)...);
}
template <typename Widget, typename Parent, typename ...Args>
inline Widget *CreateChild(
template <typename Value, typename Parent, typename ...Args>
inline Value *CreateChild(
Parent *parent,
Args &&...args) {
Expects(parent != nullptr);
return new Widget(parent, std::forward<Args>(args)...);
if constexpr (std::is_base_of_v<QObject, Value>) {
return new Value(parent, std::forward<Args>(args)...);
} else {
return CreateChild<details::AttachmentOwner<Value>>(
parent,
details::InPlaceTag{},
std::forward<Args>(args)...)->value();
}
}
inline void DestroyChild(QWidget *child) {
@ -66,8 +86,8 @@ inline not_null<std::decay_t<Value>*> AttachAsChild(
Value &&value) {
return CreateChild<details::AttachmentOwner<std::decay_t<Value>>>(
parent.get(),
std::forward<Value>(value)
)->value();
details::ForwardTag{},
std::forward<Value>(value))->value();
}
template <typename Widget>

View file

@ -201,15 +201,14 @@ void Filler::addPinToggle() {
};
auto pinAction = _addAction(pinText(isPinned), pinToggle);
auto lifetime = Notify::PeerUpdateViewer(
const auto lifetime = Ui::CreateChild<rpl::lifetime>(pinAction);
Notify::PeerUpdateViewer(
peer,
Notify::PeerUpdate::Flag::ChatPinnedChanged
) | rpl::start_with_next([peer, pinAction, pinText] {
auto isPinned = App::history(peer)->isPinnedDialog();
pinAction->setText(pinText(isPinned));
});
Ui::AttachAsChild(pinAction, std::move(lifetime));
}, *lifetime);
}
void Filler::addInfo() {
@ -263,14 +262,13 @@ void Filler::addToggleUnreadMark() {
}
});
auto lifetime = Notify::PeerUpdateViewer(
const auto lifetime = Ui::CreateChild<rpl::lifetime>(action);
Notify::PeerUpdateViewer(
_peer,
Notify::PeerUpdate::Flag::UnreadViewChanged
) | rpl::start_with_next([=] {
action->setText(label(peer));
});
Ui::AttachAsChild(action, std::move(lifetime));
}, *lifetime);
}
void Filler::addBlockUser(not_null<UserData*> user) {
@ -301,14 +299,13 @@ void Filler::addBlockUser(not_null<UserData*> user) {
}
});
auto lifetime = Notify::PeerUpdateViewer(
const auto lifetime = Ui::CreateChild<rpl::lifetime>(blockAction);
Notify::PeerUpdateViewer(
_peer,
Notify::PeerUpdate::Flag::UserIsBlocked
) | rpl::start_with_next([=] {
blockAction->setText(blockText(user));
});
Ui::AttachAsChild(blockAction, std::move(lifetime));
}, *lifetime);
if (user->blockStatus() == UserData::BlockStatus::Unknown) {
Auth().api().requestFullPeer(user);
@ -714,13 +711,12 @@ void PeerMenuAddMuteAction(
}
});
auto lifetime = Info::Profile::NotificationsEnabledValue(
const auto lifetime = Ui::CreateChild<rpl::lifetime>(muteAction);
Info::Profile::NotificationsEnabledValue(
peer
) | rpl::start_with_next([=](bool enabled) {
muteAction->setText(muteText(!enabled));
});
Ui::AttachAsChild(muteAction, std::move(lifetime));
}, *lifetime);
}
// #feed
//void PeerMenuUngroupFeed(not_null<Data::Feed*> feed) {