Improve emoji toggle, italic icon.

This commit is contained in:
John Preston
2026-06-16 18:21:45 +04:00
parent 6b970dfee9
commit 732df97eb8
3 changed files with 182 additions and 110 deletions
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24px" height="24px" viewBox="0 0 72 72" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<svg width="72px" height="72px" viewBox="0 0 72 72" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Filled / Page / page_italic</title>
<g id="Filled-/-Page-/-page_italic" stroke="none" fill="none" fill-rule="nonzero">
<path d="M36.265,50.655 C36.265,51.095 36.3475,51.4341667 36.5125,51.6725 C36.6775,51.9108333 36.8883333,52.03 37.145,52.03 C37.5483333,52.03 37.9608333,51.9108333 38.3825,51.6725 C38.8041667,51.4341667 39.3266667,51.0216667 39.95,50.435 L40.665,51.865 C40.115,52.635 39.2716667,53.4416667 38.135,54.285 C36.9983333,55.1283333 35.66,55.55 34.12,55.55 C32.8366667,55.55 31.8558333,55.2566667 31.1775,54.67 C30.4991667,54.0833333 30.215,53.2216667 30.325,52.085 C30.5083333,50.4716667 30.985,47.0066667 31.755,41.69 C32.7816667,34.5766667 33.405,30.1216667 33.625,28.325 L30.215,27.005 L30.6,25.08 L38.74,23.815 L40.005,24.42 L36.32,50.105 L36.265,50.655 Z M37.805,17.985 C36.9616667,17.985 36.2375,17.6733333 35.6325,17.05 C35.0275,16.4266667 34.725,15.675 34.725,14.795 C34.725,13.6583333 35.1191667,12.6866667 35.9075,11.88 C36.6958333,11.0733333 37.6216667,10.67 38.685,10.67 C39.675,10.67 40.445,10.9725 40.995,11.5775 C41.545,12.1825 41.82,12.925 41.82,13.805 C41.7833333,14.9783333 41.3983333,15.9683333 40.665,16.775 C39.9316667,17.5816667 38.9783333,17.985 37.805,17.985 Z" id="Shape" fill="#FFFFFF"></path>
<path d="M25.1704102,55.8051758 L42.7602539,55.8051758 C43.4282227,55.8051758 43.9709473,55.6149902 44.3884277,55.2346191 C44.8059082,54.854248 45.0146484,54.3393555 45.0146484,53.6899414 C45.0146484,53.059082 44.8105469,52.5581055 44.4023437,52.1870117 C43.9941406,51.815918 43.4560547,51.6303711 42.7880859,51.6303711 L36.5537109,51.6303711 L43.3168945,20.1801758 L49.5512695,20.1801758 C50.2006836,20.1801758 50.7341309,19.9899902 51.1516113,19.6096191 C51.5690918,19.229248 51.777832,18.7143555 51.777832,18.0649414 C51.777832,17.434082 51.5737305,16.9331055 51.1655273,16.5620117 C50.7573242,16.190918 50.2285156,16.0053711 49.5791016,16.0053711 L31.9057617,16.0053711 C31.2563477,16.0053711 30.7275391,16.190918 30.3193359,16.5620117 C29.9111328,16.9331055 29.7070313,17.434082 29.7070313,18.0649414 C29.7070313,18.7143555 29.9157715,19.229248 30.333252,19.6096191 C30.7507324,19.9899902 31.2841797,20.1801758 31.9335938,20.1801758 L38.1679688,20.1801758 L31.4047852,51.6303711 L25.1425781,51.6303711 C24.4931641,51.6303711 23.9643555,51.815918 23.5561523,52.1870117 C23.1479492,52.5581055 22.9438477,53.059082 22.9438477,53.6899414 C22.9438477,54.3393555 23.1525879,54.854248 23.5700684,55.2346191 C23.9875488,55.6149902 24.5209961,55.8051758 25.1704102,55.8051758 Z" id="Path" fill="#FFFFFF"></path>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

+170 -103
View File
@@ -26,7 +26,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_document.h"
#include "data/stickers/data_custom_emoji.h"
#include "data/stickers/data_stickers.h"
#include "chat_helpers/tabbed_panel.h"
#include "chat_helpers/tabbed_selector.h"
#include "iv/editor/iv_editor_state.h"
#include "iv/editor/iv_editor_widget.h"
@@ -269,10 +268,9 @@ public:
QPointer<QWidget> tooltipParent,
Fn<void(not_null<Widget*>, QPointer<QWidget>)> requestMedia,
Fn<void(not_null<Widget*>, QPointer<QWidget>, rpl::producer<>)> requestMap,
Fn<void(not_null<Ui::IconButton*>)> toggleEmoji);
Fn<void()> toggleEmoji);
int resizeGetHeight(int width) override;
[[nodiscard]] Ui::IconButton *emojiButton() const;
void hideShownTooltip();
protected:
@@ -306,7 +304,7 @@ private:
const QPointer<QWidget> _tooltipParent;
const Fn<void(not_null<Widget*>, QPointer<QWidget>)> _requestMedia;
const Fn<void(not_null<Widget*>, QPointer<QWidget>, rpl::producer<>)> _requestMap;
const Fn<void(not_null<Ui::IconButton*>)> _toggleEmoji;
const Fn<void()> _toggleEmoji;
Widget::ToolbarState _toolbarState = {};
std::vector<ToolbarButton> _buttons;
ToolbarButton _emoji;
@@ -315,7 +313,6 @@ private:
std::vector<int> _dividerXs;
base::unique_qptr<Ui::ImportantTooltip> _tooltip;
base::unique_qptr<Ui::PopupMenu> _menu;
Ui::IconButton *_emojiButton = nullptr;
Ui::RippleButton *_hovered = nullptr;
bool _overflowStateInitialized = false;
bool _overflowEnabled = false;
@@ -331,6 +328,53 @@ private:
return result;
}
[[nodiscard]] int MaximalExtendBy(not_null<Window*> window) {
const auto screen = window->screen()
? window->screen()
: QGuiApplication::primaryScreen();
if (!screen) {
return 0;
}
const auto desktop = screen->availableGeometry();
return std::max(desktop.width() - window->body()->width(), 0);
}
[[nodiscard]] bool CanExtendNoMove(
not_null<Window*> window,
int extendBy) {
const auto screen = window->screen()
? window->screen()
: QGuiApplication::primaryScreen();
if (!screen) {
return false;
}
const auto desktop = screen->availableGeometry();
const auto inner = window->body()->mapToGlobal(window->body()->rect());
const auto innerRight = inner.x() + inner.width() + extendBy;
const auto desktopRight = desktop.x() + desktop.width();
return innerRight <= desktopRight;
}
int TryToExtendWidthBy(not_null<Window*> window, int addToWidth) {
const auto screen = window->screen()
? window->screen()
: QGuiApplication::primaryScreen();
if (!screen) {
return 0;
}
const auto desktop = screen->availableGeometry();
const auto inner = window->body()->mapToGlobal(window->body()->rect());
accumulate_min(addToWidth, MaximalExtendBy(window));
const auto newWidth = inner.width() + addToWidth;
const auto newLeft = std::min(
inner.x(),
desktop.x() + desktop.width() - newWidth);
if (inner.x() != newLeft || inner.width() != newWidth) {
window->setGeometry(QRect(newLeft, inner.y(), newWidth, inner.height()));
}
return addToWidth;
}
[[nodiscard]] QString HeadingLabel(int level) {
switch (level) {
case 1: return tr::lng_article_insert_heading1(tr::now);
@@ -472,7 +516,7 @@ Toolbar::Toolbar(
QPointer<QWidget> tooltipParent,
Fn<void(not_null<Widget*>, QPointer<QWidget>)> requestMedia,
Fn<void(not_null<Widget*>, QPointer<QWidget>, rpl::producer<>)> requestMap,
Fn<void(not_null<Ui::IconButton*>)> toggleEmoji)
Fn<void()> toggleEmoji)
: Ui::RpWidget(parent)
, _editor(editor.get())
, _tooltipParent(std::move(tooltipParent))
@@ -780,8 +824,8 @@ void Toolbar::addEmojiButton() {
.icon = &st::ivEditorToolbarEmojiIcon,
.iconOver = &st::ivEditorToolbarEmojiIcon,
.callback = [=] {
if (_toggleEmoji && _emojiButton) {
_toggleEmoji(not_null<Ui::IconButton*>(_emojiButton));
if (_toggleEmoji) {
_toggleEmoji();
}
},
};
@@ -797,7 +841,6 @@ void Toolbar::addEmojiButton() {
}
});
data.button->installEventFilter(this);
_emojiButton = data.iconButton;
_emoji = std::move(data);
}
@@ -1115,10 +1158,6 @@ int Toolbar::resizeGetHeight(int width) {
return padding.top() + buttonHeight + padding.bottom();
}
Ui::IconButton *Toolbar::emojiButton() const {
return _emojiButton;
}
void Toolbar::hideShownTooltip() {
hideTooltip();
}
@@ -1240,11 +1279,15 @@ public:
private:
void setupWindow(ShowWindowDescriptor &&descriptor);
void setupEmojiPanel(const ShowWindowDescriptor &descriptor);
void setupEmojiColumn(const ShowWindowDescriptor &descriptor);
void layout();
void updateEmojiPanelGeometry();
void toggleEmojiColumn();
void showEmojiColumn();
void hideEmojiColumn(bool skipResize = false);
void updateEditorVisibleTopBottom();
void setEmojiPanelInteractionActive(bool active);
void setEmojiColumnInteractionActive(bool active);
[[nodiscard]] int emojiColumnWidth() const;
[[nodiscard]] int minimalWindowWidthWithEmojiColumn() const;
void finishCloseFromAcceptedEvent();
void finishClose();
[[nodiscard]] bool articleChanged();
@@ -1263,13 +1306,15 @@ private:
object_ptr<Toolbar> _toolbar = { nullptr };
object_ptr<Ui::RoundButton> _cancel = { nullptr };
object_ptr<Ui::RoundButton> _submit = { nullptr };
base::unique_qptr<ChatHelpers::TabbedPanel> _emojiPanel;
object_ptr<ChatHelpers::TabbedSelector> _emojiColumn = { nullptr };
Fn<bool()> _cancelled;
Fn<bool()> _confirmed;
Fn<void()> _closed;
base::weak_qptr<Ui::GenericBox> _closeConfirmation;
rpl::lifetime _lifetime;
bool _emojiPanelInteractionActive = false;
int _emojiColumnExtendedBy = 0;
bool _emojiColumnShown = false;
bool _emojiColumnInteractionActive = false;
bool _closingApproved = false;
bool _closedNotified = false;
@@ -1280,7 +1325,7 @@ WindowHost::Impl::Impl(ShowWindowDescriptor descriptor) {
}
WindowHost::Impl::~Impl() {
setEmojiPanelInteractionActive(false);
hideEmojiColumn(true);
}
void WindowHost::Impl::setupWindow(ShowWindowDescriptor &&descriptor) {
@@ -1333,6 +1378,7 @@ void WindowHost::Impl::setupWindow(ShowWindowDescriptor &&descriptor) {
std::move(descriptor.showLimitToast)));
const auto editor = not_null<Widget*>(_editor.data());
const auto body = QPointer<QWidget>(window->body().get());
setupEmojiColumn(descriptor);
_toolbar = object_ptr<Toolbar>(
_top.data(),
@@ -1340,11 +1386,9 @@ void WindowHost::Impl::setupWindow(ShowWindowDescriptor &&descriptor) {
body,
std::move(descriptor.requestMedia),
std::move(descriptor.requestMap),
[=](not_null<Ui::IconButton*>) {
[=] {
_toolbar->hideShownTooltip();
setEmojiPanelInteractionActive(true);
updateEmojiPanelGeometry();
_emojiPanel->toggleAnimated();
toggleEmojiColumn();
});
_cancel = object_ptr<Ui::RoundButton>(
_bottom.data(),
@@ -1368,8 +1412,6 @@ void WindowHost::Impl::setupWindow(ShowWindowDescriptor &&descriptor) {
_confirmed = std::move(descriptor.confirmed);
_closed = std::move(descriptor.closed);
setupEmojiPanel(descriptor);
window->body()->sizeValue() | rpl::on_next([=](QSize) {
layout();
}, _lifetime);
@@ -1411,40 +1453,29 @@ void WindowHost::Impl::setupWindow(ShowWindowDescriptor &&descriptor) {
editor->activateInitialNode();
}
void WindowHost::Impl::setupEmojiPanel(const ShowWindowDescriptor &descriptor) {
void WindowHost::Impl::setupEmojiColumn(const ShowWindowDescriptor &descriptor) {
using Selector = ChatHelpers::TabbedSelector;
_emojiPanel = base::make_unique_q<ChatHelpers::TabbedPanel>(
_emojiColumn = object_ptr<Selector>(
_window->body().get(),
ChatHelpers::TabbedPanelDescriptor{
.ownedSelector = object_ptr<Selector>(
nullptr,
ChatHelpers::TabbedSelectorDescriptor{
.show = _show,
.st = st::defaultEmojiPan,
.level = ChatHelpers::PauseReason::Layer,
.mode = Selector::Mode::EmojiOnly,
.features = {
.stickersSettings = false,
.openStickerSets = false,
},
}),
ChatHelpers::TabbedSelectorDescriptor{
.show = _show,
.st = st::defaultEmojiPan,
.level = ChatHelpers::PauseReason::Layer,
.mode = Selector::Mode::EmojiOnly,
.features = {
.stickersSettings = false,
.openStickerSets = false,
},
});
const auto panel = _emojiPanel.get();
panel->setDesiredHeightValues(
1.,
st::emojiPanMinHeight / 2,
st::emojiPanMinHeight);
panel->setDropDown(true);
panel->hide();
panel->selector()->setCurrentPeer(descriptor.peer);
panel->selector()->emojiChosen(
_emojiColumn->hide();
_emojiColumn->setCurrentPeer(descriptor.peer);
_emojiColumn->emojiChosen(
) | rpl::on_next([=](ChatHelpers::EmojiChosen data) {
setEmojiPanelInteractionActive(true);
if (_editor) {
_editor->insertEmoji(data.emoji);
}
}, _lifetime);
panel->selector()->customEmojiChosen(
_emojiColumn->customEmojiChosen(
) | rpl::on_next([=](ChatHelpers::FileChosen data) {
const auto document = data.document;
if (!IsEmojiDocument(document)) {
@@ -1457,84 +1488,115 @@ void WindowHost::Impl::setupEmojiPanel(const ShowWindowDescriptor &descriptor) {
_show,
PremiumFeature::AnimatedEmoji);
} else if (_editor) {
setEmojiPanelInteractionActive(true);
_editor->insertCustomEmoji(document);
}
}, _lifetime);
if (const auto button = _toolbar->emojiButton()) {
button->installEventFilter(panel);
base::install_event_filter(panel, button, [=](not_null<QEvent*> event) {
const auto type = event->type();
if (type == QEvent::Enter || type == QEvent::MouseButtonPress) {
setEmojiPanelInteractionActive(true);
updateEmojiPanelGeometry();
} else if (type == QEvent::Leave && panel->isHidden()) {
setEmojiPanelInteractionActive(false);
}
return base::EventFilterResult::Continue;
});
}
base::install_event_filter(panel, [=](not_null<QEvent*> event) {
const auto type = event->type();
if (type == QEvent::Hide || type == QEvent::Close) {
setEmojiPanelInteractionActive(false);
} else if (type == QEvent::Show) {
setEmojiPanelInteractionActive(true);
}
return base::EventFilterResult::Continue;
});
}
void WindowHost::Impl::layout() {
if (!_window || !_top || !_bottom || !_toolbar || !_editor) {
if (!_window || !_top || !_bottom || !_toolbar || !_editor || !_emojiColumn) {
return;
}
const auto width = _window->body()->width();
const auto height = _window->body()->height();
const auto padding = st::ivEditorBottomControlsPadding;
const auto toolbarHeight = _toolbar->resizeGetHeight(width);
const auto emojiWidth = _emojiColumnShown ? emojiColumnWidth() : 0;
const auto editorWidth = std::max(width - emojiWidth, 0);
const auto toolbarHeight = _toolbar->resizeGetHeight(editorWidth);
const auto buttonsHeight = std::max(_cancel->height(), _submit->height());
const auto bottomHeight = padding.top() + buttonsHeight + padding.bottom();
const auto contentHeight = std::max(height - toolbarHeight - bottomHeight, 0);
const auto buttonsTop = padding.top();
_top->setGeometry(0, 0, width, toolbarHeight);
_toolbar->setGeometry(0, 0, width, toolbarHeight);
_top->setGeometry(0, 0, editorWidth, toolbarHeight);
_toolbar->setGeometry(0, 0, editorWidth, toolbarHeight);
_bottom->setGeometry(
0,
std::max(height - bottomHeight, toolbarHeight),
width,
editorWidth,
bottomHeight);
_submit->moveToRight(padding.right(), buttonsTop, width);
_submit->moveToRight(padding.right(), buttonsTop, editorWidth);
_cancel->moveToRight(
padding.right()
+ _submit->width()
+ st::ivEditorBottomControlsButtonSkip,
buttonsTop,
width);
editorWidth);
_scroll->setGeometry(
0,
toolbarHeight,
width,
std::max(height - toolbarHeight - bottomHeight, 0));
editorWidth,
contentHeight);
if (_emojiColumnShown) {
_emojiColumn->setGeometry(
editorWidth,
0,
emojiWidth,
height);
} else {
_emojiColumn->hide();
}
_editor->resizeToWidth(std::max(_scroll->width(), 1));
updateEditorVisibleTopBottom();
updateEmojiPanelGeometry();
}
void WindowHost::Impl::updateEmojiPanelGeometry() {
if (!_emojiPanel || !_toolbar) {
void WindowHost::Impl::toggleEmojiColumn() {
if (_emojiColumnShown) {
hideEmojiColumn();
} else {
showEmojiColumn();
}
}
void WindowHost::Impl::showEmojiColumn() {
if (_emojiColumnShown || !_window || !_emojiColumn) {
return;
}
const auto button = _toolbar->emojiButton();
if (!button) {
const auto window = not_null<Window*>(_window.get());
const auto wanted = emojiColumnWidth();
const auto minimal = std::max(
minimalWindowWidthWithEmojiColumn() - window->width(),
0);
_emojiColumnExtendedBy = 0;
if (!window->isMaximized() && !window->isFullScreen()) {
if (CanExtendNoMove(window, wanted)) {
_emojiColumnExtendedBy = TryToExtendWidthBy(window, wanted);
} else if (minimal > 0 && CanExtendNoMove(window, minimal)) {
_emojiColumnExtendedBy = TryToExtendWidthBy(window, minimal);
} else if (window->width() < minimalWindowWidthWithEmojiColumn()) {
_emojiColumnExtendedBy = TryToExtendWidthBy(window, minimal);
}
}
window->setMinimumWidth(minimalWindowWidthWithEmojiColumn());
_emojiColumnShown = true;
_emojiColumn->setRoundRadius(0);
setEmojiColumnInteractionActive(true);
_emojiColumn->showStarted();
_emojiColumn->show();
layout();
_emojiColumn->afterShown();
}
void WindowHost::Impl::hideEmojiColumn(bool skipResize) {
if (!_emojiColumnShown || !_window || !_emojiColumn) {
return;
}
const auto local = _emojiPanel->parentWidget()->mapFromGlobal(
button->mapToGlobal(QPoint()));
_emojiPanel->setDropDown(true);
_emojiPanel->moveTopRight(
local.y() + button->height(),
local.x() + button->width());
const auto window = not_null<Window*>(_window.get());
_emojiColumn->beforeHiding();
_emojiColumn->hide();
_emojiColumn->hideFinished();
_emojiColumnShown = false;
window->setMinimumWidth(st::ivEditorWindowMinSize.width());
setEmojiColumnInteractionActive(false);
if (!skipResize
&& _emojiColumnExtendedBy > 0
&& !window->isMaximized()
&& !window->isFullScreen()) {
window->resize(
window->width() - _emojiColumnExtendedBy,
window->height());
}
_emojiColumnExtendedBy = 0;
layout();
}
void WindowHost::Impl::updateEditorVisibleTopBottom() {
@@ -1545,25 +1607,30 @@ void WindowHost::Impl::updateEditorVisibleTopBottom() {
_editor->setVisibleTopBottom(scrollTop, scrollTop + _scroll->height());
}
void WindowHost::Impl::setEmojiPanelInteractionActive(bool active) {
if (_emojiPanelInteractionActive == active || !_editor) {
_emojiPanelInteractionActive = active;
void WindowHost::Impl::setEmojiColumnInteractionActive(bool active) {
if (_emojiColumnInteractionActive == active || !_editor) {
_emojiColumnInteractionActive = active;
return;
}
_emojiPanelInteractionActive = active;
_emojiColumnInteractionActive = active;
_editor->setInlineFieldExternalInteractionActive(active);
}
int WindowHost::Impl::emojiColumnWidth() const {
return st::emojiPanWidth;
}
int WindowHost::Impl::minimalWindowWidthWithEmojiColumn() const {
return st::ivEditorWindowMinSize.width() + emojiColumnWidth();
}
void WindowHost::Impl::finishCloseFromAcceptedEvent() {
_closingApproved = true;
if (_closedNotified) {
return;
}
_closedNotified = true;
setEmojiPanelInteractionActive(false);
if (_emojiPanel) {
_emojiPanel->hideFast();
}
hideEmojiColumn(true);
if (const auto closed = base::take(_closed)) {
crl::on_main(closed);
}
@@ -3977,9 +3977,17 @@ void Widget::setupInlineField() {
handleFieldContextMenuRequest(std::move(request));
});
const auto field = QPointer<Ui::InputField>(_field.get());
const auto revealActiveField = [=] {
if (!field || (_field.get() != field.data())) {
return;
}
revealActiveInlineField();
};
_field->heightChanges(
) | rpl::on_next([=] {
updateInlineFieldHeightOverride();
revealActiveField();
}, _field->lifetime());
_field->focusedChanges(
) | rpl::on_next([=](bool focused) {
@@ -3995,7 +4003,6 @@ void Widget::setupInlineField() {
}
}
}, _field->lifetime());
const auto field = QPointer<Ui::InputField>(_field.get());
QObject::connect(
raw->document(),
&QTextDocument::contentsChange,
@@ -4030,10 +4037,8 @@ void Widget::setupInlineField() {
raw,
&QTextEdit::cursorPositionChanged,
_field.get(),
[this, field] {
if (!field || (_field.get() != field.data())) {
return;
}
[this, revealActiveField] {
revealActiveField();
notifyToolbarStateChanged();
});
_fieldUndoAvailable = _field->isUndoAvailable();