Show sending message field in group chats.

This commit is contained in:
John Preston
2025-09-12 21:26:04 +04:00
parent f1d4e48c59
commit 5ed8f0baa5
6 changed files with 444 additions and 38 deletions
+2
View File
@@ -363,6 +363,8 @@ PRIVATE
calls/group/calls_group_members_row.h
calls/group/calls_group_menu.cpp
calls/group/calls_group_menu.h
calls/group/calls_group_message_field.cpp
calls/group/calls_group_message_field.h
calls/group/calls_group_panel.cpp
calls/group/calls_group_panel.h
calls/group/calls_group_rtmp.cpp
@@ -0,0 +1,157 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "calls/group/calls_group_message_field.h"
#include "chat_helpers/compose/compose_show.h"
#include "lang/lang_keys.h"
#include "ui/controls/emoji_button.h"
#include "ui/controls/send_button.h"
#include "ui/widgets/fields/input_field.h"
#include "ui/painter.h"
#include "ui/ui_utility.h"
#include "styles/style_chat_helpers.h"
#include "styles/style_chat.h"
#include "styles/style_media_view.h"
namespace Calls::Group {
MessageField::MessageField(
not_null<QWidget*> parent,
std::shared_ptr<ChatHelpers::Show> show)
: _parent(parent)
, _show(std::move(show))
, _wrap(std::make_unique<Ui::RpWidget>(_parent)) {
createControls();
}
void MessageField::createControls() {
setupBackground();
const auto &st = st::storiesComposeControls;
_field = Ui::CreateChild<Ui::InputField>(
_wrap.get(),
st.field,
Ui::InputField::Mode::MultiLine,
tr::lng_message_ph());
_field->setMinHeight(
st::historySendSize.height() - 2 * st::historySendPadding);
_field->setMaxHeight(st::historyComposeFieldMaxHeight);
_field->setDocumentMargin(4.);
_field->setAdditionalMargin(style::ConvertScale(4) - 4);
_send = Ui::CreateChild<Ui::SendButton>(_wrap.get(), st.send);
_emojiToggle = Ui::CreateChild<Ui::EmojiButton>(_wrap.get(), st.emoji);
_width.value(
) | rpl::filter(
rpl::mappers::_1 > 0
) | rpl::start_with_next([=](int newWidth) {
_wrap->resizeToWidth(newWidth);
const auto fieldWidth = newWidth
- st::historySendPadding
- _emojiToggle->width()
- _send->width();
_field->resizeToWidth(fieldWidth);
_field->moveToLeft(
st::historySendPadding,
st::historySendPadding,
newWidth);
_send->moveToRight(0, 0);
_emojiToggle->moveToRight(_send->width(), 0);
}, _lifetime);
_field->heightValue() | rpl::start_with_next([=](int height) {
_wrap->resize(_wrap->width(), height + 2 * st::historySendPadding);
}, _lifetime);
rpl::merge(
_field->submits() | rpl::to_empty,
_send->clicks() | rpl::to_empty
) | rpl::start_with_next([=] {
_submitted.fire(_field->getTextWithAppliedMarkdown());
}, _lifetime);
}
void MessageField::setupBackground() {
_wrap->paintRequest() | rpl::start_with_next([=] {
const auto radius = st::historySendSize.height() / 2.;
auto p = QPainter(_wrap.get());
auto hq = PainterHighQualityEnabler(p);
p.setPen(Qt::NoPen);
p.setBrush(st::storiesComposeBg);
p.drawRoundedRect(_wrap->rect(), radius, radius);
}, _lifetime);
}
void MessageField::resizeToWidth(int newWidth) {
_width = newWidth;
if (_wrap->isHidden()) {
Ui::SendPendingMoveResizeEvents(_wrap.get());
}
}
void MessageField::move(int x, int y) {
_wrap->move(x, y);
}
void MessageField::toggle(bool shown) {
if (_shown == shown) {
return;
} else if (shown) {
Assert(_width.current() > 0);
Ui::SendPendingMoveResizeEvents(_wrap.get());
}
_shown = shown;
shownAnimationCallback();
//_shownAnimation.start(
// [=] { shownAnimationCallback(); },
// shown ? 0. : 1.,
// shown ? 1. : 0.,
// st::slideWrapDuration,
// anim::easeOutCirc);
}
void MessageField::raise() {
_wrap->raise();
}
void MessageField::shownAnimationCallback() {
if (_shownAnimation.animating()) {
_wrap->update();
} else if (_shown) {
_wrap->show();
_field->setFocusFast();
} else {
_closed.fire({});
}
}
int MessageField::height() const {
return _shownAnimation.value(_shown ? 1. : 0.) * _wrap->height();
}
rpl::producer<int> MessageField::heightValue() const {
return _wrap->heightValue();
}
rpl::producer<TextWithTags> MessageField::submitted() const {
return _submitted.events();
}
rpl::producer<> MessageField::closed() const {
return _closed.events();
}
rpl::lifetime &MessageField::lifetime() {
return _lifetime;
}
} // namespace Calls::Group
@@ -0,0 +1,71 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "ui/effects/animations.h"
struct TextWithTags;
namespace ChatHelpers {
class Show;
} // namespace ChatHelpers
namespace Ui {
class EmojiButton;
class InputField;
class SendButton;
class RpWidget;
} // namespace Ui
namespace Calls::Group {
class MessageField final {
public:
MessageField(
not_null<QWidget*> parent,
std::shared_ptr<ChatHelpers::Show> show);
void resizeToWidth(int newWidth);
void move(int x, int y);
void toggle(bool shown);
void raise();
[[nodiscard]] int height() const;
[[nodiscard]] rpl::producer<int> heightValue() const;
[[nodiscard]] rpl::producer<TextWithTags> submitted() const;
[[nodiscard]] rpl::producer<> closed() const;
[[nodiscard]] rpl::lifetime &lifetime();
private:
void createControls();
void setupBackground();
void shownAnimationCallback();
const not_null<QWidget*> _parent;
const std::shared_ptr<ChatHelpers::Show> _show;
const std::unique_ptr<Ui::RpWidget> _wrap;
Ui::InputField *_field = nullptr;
Ui::SendButton *_send = nullptr;
Ui::EmojiButton *_emojiToggle = nullptr;
rpl::variable<int> _width;
bool _shown = false;
Ui::Animations::Simple _shownAnimation;
rpl::event_stream<TextWithTags> _submitted;
rpl::event_stream<> _closed;
rpl::lifetime _lifetime;
};
} // namespace Calls::Group
@@ -8,16 +8,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "calls/group/calls_group_panel.h"
#include "calls/group/calls_group_common.h"
#include "calls/group/calls_group_members.h"
#include "calls/group/calls_group_settings.h"
#include "calls/group/calls_group_menu.h"
#include "calls/group/calls_group_viewport.h"
#include "calls/group/calls_group_toasts.h"
#include "calls/group/calls_group_invite_controller.h"
#include "calls/group/calls_group_members.h"
#include "calls/group/calls_group_menu.h"
#include "calls/group/calls_group_message_field.h"
#include "calls/group/calls_group_settings.h"
#include "calls/group/calls_group_toasts.h"
#include "calls/group/calls_group_viewport.h"
#include "calls/group/ui/calls_group_scheduled_labels.h"
#include "calls/group/ui/desktop_capture_choose_source.h"
#include "calls/calls_emoji_fingerprint.h"
#include "calls/calls_window.h"
#include "chat_helpers/compose/compose_show.h"
#include "data/data_file_origin.h"
#include "ui/platform/ui_platform_window_title.h" // TitleLayout
#include "ui/platform/ui_platform_utility.h"
#include "ui/controls/call_mute_button.h"
@@ -50,6 +53,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "main/session/session_show.h"
#include "main/main_app_config.h"
#include "main/main_session.h"
#include "menu/menu_send.h"
#include "base/event_filter.h"
#include "base/unixtime.h"
#include "base/qt_signal_producer.h"
@@ -94,6 +98,96 @@ void UnpinMaximized(not_null<QWidget*> widget) {
}
#endif // Q_OS_WIN
class Show final : public ChatHelpers::Show {
public:
Show(not_null<Panel*> panel, std::shared_ptr<Ui::Show> base)
: _panel(panel)
, _base(std::move(base)) {
}
void activate() override {
if (const auto panel = _panel.get()) {
if (!panel->window()->isHidden()) {
panel->window()->activateWindow();
}
}
}
void showOrHideBoxOrLayer(
std::variant<
v::null_t,
object_ptr<Ui::BoxContent>,
std::unique_ptr<Ui::LayerWidget>> &&layer,
Ui::LayerOptions options,
anim::type animated) const override {
_base->showOrHideBoxOrLayer(
std::move(layer),
options,
anim::type::normal);
}
not_null<QWidget*> toastParent() const override {
return _base->toastParent();
}
bool valid() const override {
return _panel.get() != nullptr;
}
operator bool() const override {
return valid();
}
Main::Session &session() const override {
const auto panel = _panel.get();
Assert(panel != nullptr);
return panel->call()->peer()->session();
}
bool paused(ChatHelpers::PauseReason reason) const override {
const auto panel = _panel.get();
if (!panel) {
return false;
} else if (panel->window()->isHidden()
|| (!panel->window()->isFullScreen()
&& !panel->window()->isActiveWindow())) {
return true;
} else if (reason < ChatHelpers::PauseReason::Layer
&& panel->callWindow()->topShownLayer() != nullptr) {
return true;
}
return false;
}
rpl::producer<> pauseChanged() const override {
return rpl::never<>();
}
rpl::producer<bool> adjustShadowLeft() const override {
return rpl::single(false);
}
SendMenu::Details sendMenuDetails() const override {
return { SendMenu::Type::Disabled };
}
bool showMediaPreview(
Data::FileOrigin origin,
not_null<DocumentData*> document) const override {
return false; // #TODO stories
}
bool showMediaPreview(
Data::FileOrigin origin,
not_null<PhotoData*> photo) const override {
return false; // #TODO stories
}
void processChosenSticker(
ChatHelpers::FileChosen &&chosen) const override {
//_panel->emojiChosen(std::move(chosen));
}
private:
const base::weak_ptr<Panel> _panel;
const std::shared_ptr<Ui::Show> _base;
};
} // namespace
struct Panel::ControlsBackgroundNarrow {
@@ -104,6 +198,7 @@ struct Panel::ControlsBackgroundNarrow {
Ui::RpWidget shadow;
Ui::RpWidget blocker;
int shadowHeight = 0;
};
Panel::Panel(not_null<GroupCall*> call)
@@ -191,12 +286,11 @@ bool Panel::isActive() const {
return window()->isActiveWindow() && isVisible();
}
std::shared_ptr<Main::SessionShow> Panel::sessionShow() {
return Main::MakeSessionShow(uiShow(), &_peer->session());
}
std::shared_ptr<Ui::Show> Panel::uiShow() {
return _window->uiShow();
std::shared_ptr<ChatHelpers::Show> Panel::uiShow() {
if (!_cachedShow) {
_cachedShow = std::make_shared<Show>(this, _window->uiShow());
}
return _cachedShow;
}
void Panel::minimize() {
@@ -387,6 +481,37 @@ void Panel::initWidget() {
}, lifetime());
}
void Panel::toggleMessageTyping() {
const auto typing = !_messageTyping.current();
if (_messageField) {
_messageField->toggle(typing);
} else if (typing) {
_messageField = std::make_unique<MessageField>(widget(), uiShow());
updateButtonsGeometry();
_messageField->toggle(true);
_messageField->submitted(
) | rpl::start_with_next([=](TextWithTags text) {
//_call->sendMessage(text);
_messageField->toggle(false);
_messageTyping = false;
updateWideControlsVisibility();
}, _messageField->lifetime());
_messageField->heightValue() | rpl::start_with_next([=] {
updateButtonsGeometry();
}, _messageField->lifetime());
_messageField->closed() | rpl::start_with_next([=] {
_messageField = nullptr;
updateButtonsGeometry();
}, _messageField->lifetime());
}
_messageTyping = typing;
updateWideControlsVisibility();
}
void Panel::endCall() {
if (!_call->canManage()) {
_call->hangup();
@@ -643,6 +768,7 @@ void Panel::refreshVideoButtons(std::optional<bool> overrideWideMode) {
st::groupCallMessageSmall,
&st::groupCallMessageActiveSmall);
_message->show();
_message->setClickedCallback([=] { toggleMessageTyping(); });
_message->setColorOverrides(
toggleableOverrides(_messageTyping.value()));
}
@@ -922,7 +1048,7 @@ Fn<void()> Panel::shareConferenceLinkCallback() {
return [=] {
Expects(_call->conference());
ShowConferenceCallLinkBox(sessionShow(), _call->conferenceCall(), {
ShowConferenceCallLinkBox(uiShow(), _call->conferenceCall(), {
.st = DarkConferenceCallLinkStyle(),
});
};
@@ -930,7 +1056,7 @@ Fn<void()> Panel::shareConferenceLinkCallback() {
void Panel::migrationShowShareLink() {
ShowConferenceCallLinkBox(
sessionShow(),
uiShow(),
_call->conferenceCall(),
{ .st = DarkConferenceCallLinkStyle() });
}
@@ -1025,6 +1151,9 @@ void Panel::raiseControls() {
if (_pinOnTop) {
_pinOnTop->raise();
}
if (_messageField) {
_messageField->raise();
}
_window->raiseLayers();
if (_niceTooltip) {
_niceTooltip->raise();
@@ -1110,7 +1239,8 @@ void Panel::toggleWideControls(bool shown) {
void Panel::updateWideControlsVisibility() {
const auto shown = _showWideControls
|| (_stickedTooltipClose != nullptr);
|| (_stickedTooltipClose != nullptr)
|| _messageTyping.current();
if (_wideControlsShown == shown) {
return;
}
@@ -1845,6 +1975,7 @@ void Panel::setupControlsBackgroundNarrow() {
const auto height = std::max(
st::groupCallMembersShadowHeight,
st::groupCallMembersFadeSkip + st::groupCallMembersFadeHeight);
_controlsBackgroundNarrow->shadowHeight = height;
const auto full = lifetime.make_state<QImage>(
QSize(1, height * factor),
QImage::Format_ARGB32_Premultiplied);
@@ -1897,7 +2028,9 @@ void Panel::setupControlsBackgroundNarrow() {
const auto inner = _members->getInnerGeometry().translated(
_members->x() - _controlsBackgroundNarrow->shadow.x(),
_members->y() - _controlsBackgroundNarrow->shadow.y());
const auto faded = clip.intersected(inner);
const auto bottom = _controlsBackgroundNarrow->shadowHeight;
const auto faded = clip.intersected(inner).intersected(
QRect(clip.x(), 0, clip.width(), bottom));
if (!faded.isEmpty()) {
const auto factor = style::DevicePixelRatio();
p.drawImage(
@@ -1909,9 +2042,8 @@ void Panel::setupControlsBackgroundNarrow() {
full->width(),
faded.height() * factor));
}
const auto bottom = inner.y() + inner.height();
const auto after = clip.intersected(QRect(
0,
inner.x(),
bottom,
inner.width(),
_controlsBackgroundNarrow->shadow.height() - bottom));
@@ -2239,6 +2371,7 @@ void Panel::updateButtonsGeometry() {
widget->setVisible(shown);
}
};
auto messagesBottomSkip = 0;
if (mode() == PanelMode::Wide) {
Assert(_video != nullptr);
Assert(_message != nullptr);
@@ -2256,10 +2389,6 @@ void Panel::updateButtonsGeometry() {
_viewport->setControlsShown(rtmp ? 0. : shown);
}
const auto buttonsTop = widget()->height() - anim::interpolate(
0,
st::groupCallButtonBottomSkipWide,
shown);
const auto addSkip = st::callMuteButtonSmall.active.outerRadius;
const auto muteSize = _mute->innerSize().width() + 2 * addSkip;
const auto skip = st::groupCallButtonSkipSmall;
@@ -2276,6 +2405,28 @@ void Panel::updateButtonsGeometry() {
- membersWidth
- membersSkip
- fullWidth) / 2;
if (_messageField) {
const auto paddedWidth = fullWidth
+ st::groupCallControlsBackMargin.left()
+ st::groupCallControlsBackMargin.right();
_messageField->resizeToWidth(paddedWidth);
messagesBottomSkip += _messageField->height();
const auto existing = st::groupCallButtonBottomSkipWide
- _hangup->height()
- st::groupCallControlsBackMargin.bottom();
const auto x = left - st::groupCallControlsBackMargin.left();
const auto y = widget()->height()
- messagesBottomSkip
- (existing / 2);
_messageField->move(x, y);
}
const auto buttonsTop = widget()->height()
- messagesBottomSkip
- anim::interpolate(0, st::groupCallButtonBottomSkipWide, shown);
//toggle(_screenShare, !hidden && !rtmp);
toggle(_message, !hidden && !rtmp);
//if (!rtmp) {
@@ -2326,16 +2477,33 @@ void Panel::updateButtonsGeometry() {
refreshTitleGeometry();
}
} else {
const auto muteTop = widget()->height()
- st::groupCallMuteBottomSkip;
const auto buttonsTop = widget()->height()
- st::groupCallButtonBottomSkip;
const auto muteSize = _mute->innerSize().width();
const auto single = (_settings ? _settings : _callShare)->width();
const auto five = !_callShare && _message;
const auto fullWidth = five
? st::groupCallWidth
: (muteSize + 2 * (single + st::groupCallButtonSkip));
if (_messageField) {
const auto paddedWidth = st::groupCallWidth
- st::groupCallMembersMargin.left()
- st::groupCallMembersMargin.right();
_messageField->resizeToWidth(paddedWidth);
const auto existing = st::groupCallButtonBottomSkip
- _mute->innerSize().height();
const auto height = _messageField->height();
messagesBottomSkip += height + std::min(height, existing / 3);
const auto x = (widget()->width() - paddedWidth) / 2;
const auto y = widget()->height() - messagesBottomSkip;
_messageField->move(x, y);
}
const auto muteTop = widget()->height()
- messagesBottomSkip
- st::groupCallMuteBottomSkip;
const auto buttonsTop = widget()->height()
- messagesBottomSkip
- st::groupCallButtonBottomSkip;
toggle(_mute, true);
_mute->moveInner({ (widget()->width() - muteSize) / 2, muteTop });
const auto leftButtonLeft = (widget()->width() - fullWidth) / 2;
@@ -2372,17 +2540,19 @@ void Panel::updateButtonsGeometry() {
_controlsBackgroundNarrow->shadow.setGeometry(
left,
(widget()->height()
- messagesBottomSkip
- st::groupCallMembersMargin.bottom()
- _controlsBackgroundNarrow->shadow.height()),
- _controlsBackgroundNarrow->shadowHeight),
width,
_controlsBackgroundNarrow->shadow.height());
messagesBottomSkip + _controlsBackgroundNarrow->shadowHeight);
_controlsBackgroundNarrow->blocker.setGeometry(
left,
(widget()->height()
- messagesBottomSkip
- st::groupCallMembersMargin.bottom()
- st::groupCallMembersBottomSkip),
width,
st::groupCallMembersBottomSkip);
messagesBottomSkip + st::groupCallMembersBottomSkip);
}
updateTooltipGeometry();
}
@@ -2659,6 +2829,10 @@ bool Panel::handleClose() {
return false;
}
not_null<Window*> Panel::callWindow() const {
return _window.get();
}
not_null<Ui::RpWindow*> Panel::window() const {
return _window->window();
}
@@ -16,21 +16,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
class Image;
namespace ChatHelpers {
class Show;
} // namespace ChatHelpers
namespace Data {
class PhotoMedia;
class GroupCall;
} // namespace Data
namespace Main {
class SessionShow;
} // namespace Main
namespace Ui {
class Show;
class BoxContent;
class LayerWidget;
enum class LayerOption;
using LayerOptions = base::flags<LayerOption>;
class AbstractButton;
class ImportantTooltip;
class DropdownMenu;
@@ -73,6 +70,7 @@ class Viewport;
enum class PanelMode;
enum class StickedTooltip;
class MicLevelTester;
class MessageField;
class Panel final
: public base::has_weak_ptr
@@ -97,9 +95,8 @@ public:
void showAndActivate();
void closeBeforeDestroy();
[[nodiscard]] std::shared_ptr<Main::SessionShow> sessionShow();
[[nodiscard]] std::shared_ptr<Ui::Show> uiShow();
[[nodiscard]] std::shared_ptr<ChatHelpers::Show> uiShow();
[[nodiscard]] not_null<Window*> callWindow() const;
[[nodiscard]] not_null<Ui::RpWindow*> window() const;
rpl::lifetime &lifetime();
@@ -171,6 +168,7 @@ private:
void updateWideControlsVisibility();
[[nodiscard]] bool videoButtonInNarrowMode() const;
[[nodiscard]] Fn<void()> shareConferenceLinkCallback();
void toggleMessageTyping();
void endCall();
@@ -258,6 +256,9 @@ private:
StickedTooltips _stickedTooltipsShown;
Fn<void()> _callShareLinkCallback;
std::shared_ptr<ChatHelpers::Show> _cachedShow;
std::unique_ptr<MessageField> _messageField;
const std::unique_ptr<Toasts> _toasts;
std::unique_ptr<MicLevelTester> _micLevelTester;
@@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "calls/group/calls_group_call.h"
#include "calls/group/calls_group_common.h"
#include "calls/group/calls_group_panel.h"
#include "chat_helpers/compose/compose_show.h"
#include "data/data_peer.h"
#include "data/data_group_call.h"
#include "ui/layers/show.h"