tdesktop/Telegram/SourceFiles/calls/calls_group_panel.cpp

400 lines
11 KiB
C++
Raw Normal View History

2020-11-24 12:54:20 +00:00
/*
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/calls_group_panel.h"
2020-11-25 14:09:59 +00:00
#include "calls/calls_group_members.h"
#include "calls/calls_group_settings.h"
2020-11-24 12:54:20 +00:00
#include "ui/widgets/buttons.h"
#include "ui/widgets/window.h"
2020-11-28 12:00:06 +00:00
#include "ui/widgets/call_button.h"
#include "ui/widgets/call_mute_button.h"
#include "ui/widgets/checkbox.h"
#include "ui/layers/layer_manager.h"
#include "ui/layers/generic_box.h"
2020-11-24 12:54:20 +00:00
#include "core/application.h"
#include "lang/lang_keys.h"
#include "data/data_channel.h"
2020-11-25 14:09:59 +00:00
#include "base/event_filter.h"
2020-11-24 12:54:20 +00:00
#include "app.h"
#include "styles/style_calls.h"
#include "styles/style_layers.h"
2020-11-24 12:54:20 +00:00
#ifdef Q_OS_WIN
#include "ui/platform/win/ui_window_title_win.h"
#endif // Q_OS_WIN
#include <QtWidgets/QDesktopWidget>
#include <QtWidgets/QApplication>
#include <QtGui/QWindow>
namespace Calls {
void LeaveGroupCallBox(
not_null<Ui::GenericBox*> box,
not_null<GroupCall*> call,
bool discardChecked,
BoxContext context) {
box->setTitle(tr::lng_group_call_leave_title());
box->addRow(object_ptr<Ui::FlatLabel>(
box.get(),
tr::lng_group_call_leave_sure(),
st::boxLabel));
const auto discard = call->channel()->canManageCall()
? box->addRow(object_ptr<Ui::Checkbox>(
box.get(),
tr::lng_group_call_end(),
discardChecked,
st::defaultBoxCheckbox),
style::margins(
st::boxRowPadding.left(),
st::boxRowPadding.left(),
st::boxRowPadding.right(),
st::boxRowPadding.bottom()))
: nullptr;
const auto weak = base::make_weak(call.get());
box->addButton(tr::lng_group_call_leave(), [=] {
const auto discardCall = (discard && discard->checked());
box->closeBox();
if (!weak) {
return;
} else if (discardCall) {
call->discard();
} else {
call->hangup();
}
});
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
}
2020-11-24 12:54:20 +00:00
GroupPanel::GroupPanel(not_null<GroupCall*> call)
: _call(call)
, _channel(call->channel())
, _window(std::make_unique<Ui::Window>(Core::App().getModalParent()))
, _layerBg(std::make_unique<Ui::LayerManager>(_window->body()))
2020-11-24 12:54:20 +00:00
#ifdef Q_OS_WIN
, _controls(std::make_unique<Ui::Platform::TitleControls>(
_window.get(),
2020-11-25 14:09:59 +00:00
st::callTitle))
2020-11-24 12:54:20 +00:00
#endif // Q_OS_WIN
2020-11-25 14:09:59 +00:00
, _members(widget(), call)
2020-11-28 12:00:06 +00:00
, _settings(widget(), st::groupCallSettings)
, _mute(std::make_unique<Ui::CallMuteButton>(
widget(),
Ui::CallMuteButtonState{
.text = tr::lng_group_call_connecting(tr::now),
.type = Ui::CallMuteButtonType::Connecting,
}))
, _hangup(widget(), st::callHangup) {
2020-11-24 12:54:20 +00:00
initWindow();
initWidget();
initControls();
initLayout();
showAndActivate();
}
GroupPanel::~GroupPanel() = default;
void GroupPanel::showAndActivate() {
2020-11-25 14:09:59 +00:00
if (_window->isHidden()) {
_window->show();
}
2020-11-24 12:54:20 +00:00
_window->raise();
_window->setWindowState(_window->windowState() | Qt::WindowActive);
_window->activateWindow();
_window->setFocus();
}
void GroupPanel::initWindow() {
_window->setAttribute(Qt::WA_OpaquePaintEvent);
_window->setAttribute(Qt::WA_NoSystemBackground);
_window->setWindowIcon(
QIcon(QPixmap::fromImage(Image::Empty()->original(), Qt::ColorOnly)));
_window->setTitleStyle(st::callTitle);
2020-11-26 13:04:11 +00:00
_window->setTitle(computeTitleRect()
? u" "_q
: tr::lng_group_call_title(tr::now));
2020-11-24 12:54:20 +00:00
2020-11-25 14:09:59 +00:00
base::install_event_filter(_window.get(), [=](not_null<QEvent*> e) {
if (e->type() == QEvent::Close && handleClose()) {
e->ignore();
return base::EventFilterResult::Cancel;
2020-11-24 12:54:20 +00:00
}
2020-11-25 14:09:59 +00:00
return base::EventFilterResult::Continue;
});
2020-11-24 12:54:20 +00:00
_window->setBodyTitleArea([=](QPoint widgetPoint) {
using Flag = Ui::WindowTitleHitTestFlag;
if (!widget()->rect().contains(widgetPoint)) {
return Flag::None | Flag(0);
}
#ifdef Q_OS_WIN
if (_controls->geometry().contains(widgetPoint)) {
return Flag::None | Flag(0);
}
#endif // Q_OS_WIN
const auto inControls = _settings->geometry().contains(widgetPoint)
|| _mute->innerGeometry().contains(widgetPoint)
|| _hangup->geometry().contains(widgetPoint)
|| _members->geometry().contains(widgetPoint);
2020-11-24 12:54:20 +00:00
return inControls
? Flag::None
2020-11-25 14:09:59 +00:00
: (Flag::Move | Flag::Maximize);
2020-11-24 12:54:20 +00:00
});
}
void GroupPanel::initWidget() {
widget()->setMouseTracking(true);
widget()->paintRequest(
) | rpl::start_with_next([=](QRect clip) {
paint(clip);
}, widget()->lifetime());
widget()->sizeValue(
) | rpl::skip(1) | rpl::start_with_next([=] {
updateControlsGeometry();
}, widget()->lifetime());
}
void GroupPanel::copyShareLink() {
}
void GroupPanel::hangup(bool discardCallChecked) {
if (!_call) {
return;
}
_layerBg->showBox(Box(
LeaveGroupCallBox,
_call,
discardCallChecked,
BoxContext::GroupCallPanel));
}
2020-11-24 12:54:20 +00:00
void GroupPanel::initControls() {
2020-11-28 12:00:06 +00:00
_mute->clicks(
) | rpl::filter([=](Qt::MouseButton button) {
return (button == Qt::LeftButton);
}) | rpl::start_with_next([=] {
2020-11-28 07:18:46 +00:00
if (_call && _call->muted() != MuteState::ForceMuted) {
_call->setMuted((_call->muted() == MuteState::Active)
? MuteState::Muted
: MuteState::Active);
2020-11-24 12:54:20 +00:00
}
2020-11-28 12:00:06 +00:00
}, _mute->lifetime());
_hangup->setClickedCallback([=] { hangup(false); });
2020-11-24 12:54:20 +00:00
_settings->setClickedCallback([=] {
_layerBg->showBox(Box(
GroupCallSettingsBox,
[=] { copyShareLink(); },
[=] { hangup(true); }));
2020-11-24 12:54:20 +00:00
});
2020-11-28 12:00:06 +00:00
_settings->setText(tr::lng_menu_settings());
_hangup->setText(tr::lng_box_leave());
_members->desiredHeightValue(
) | rpl::start_with_next([=] {
updateControlsGeometry();
}, _members->lifetime());
2020-11-24 12:54:20 +00:00
initWithCall(_call);
}
void GroupPanel::initWithCall(GroupCall *call) {
_callLifetime.destroy();
_call = call;
if (!_call) {
return;
}
_channel = _call->channel();
2020-11-28 12:00:06 +00:00
call->levelUpdates(
) | rpl::filter([=](const LevelUpdate &update) {
return update.self;
}) | rpl::start_with_next([=](const LevelUpdate &update) {
_mute->setLevel(update.value);
2020-11-24 12:54:20 +00:00
}, _callLifetime);
2020-11-25 14:09:59 +00:00
_members->toggleMuteRequests(
) | rpl::start_with_next([=](GroupMembers::MuteRequest request) {
if (_call) {
_call->toggleMute(request.user, request.mute);
}
}, _callLifetime);
2020-11-28 12:00:06 +00:00
using namespace rpl::mappers;
rpl::combine(
_call->mutedValue(),
_call->stateValue() | rpl::map(
_1 == State::Creating
|| _1 == State::Joining
|| _1 == State::Connecting
2020-11-28 12:00:06 +00:00
)
) | rpl::start_with_next([=](MuteState mute, bool connecting) {
_mute->setState(Ui::CallMuteButtonState{
.text = (connecting
? tr::lng_group_call_connecting(tr::now)
: mute == MuteState::ForceMuted
? tr::lng_group_call_force_muted(tr::now)
: mute != MuteState::Active
? tr::lng_call_unmute_audio(tr::now)
: tr::lng_call_mute_audio(tr::now)),
.type = (connecting
? Ui::CallMuteButtonType::Connecting
: mute == MuteState::ForceMuted
? Ui::CallMuteButtonType::ForceMuted
: mute == MuteState::Muted
? Ui::CallMuteButtonType::Muted
: Ui::CallMuteButtonType::Active),
});
}, _callLifetime);
2020-11-24 12:54:20 +00:00
}
void GroupPanel::initLayout() {
initGeometry();
#ifdef Q_OS_WIN
_controls->raise();
#endif // Q_OS_WIN
}
void GroupPanel::showControls() {
Expects(_call != nullptr);
widget()->showChildren();
}
void GroupPanel::closeBeforeDestroy() {
_window->close();
initWithCall(nullptr);
}
void GroupPanel::initGeometry() {
const auto center = Core::App().getPointForCallPanelCenter();
const auto rect = QRect(0, 0, st::groupCallWidth, st::groupCallHeight);
_window->setGeometry(rect.translated(center - rect.center()));
_window->setMinimumSize(rect.size());
_window->show();
updateControlsGeometry();
}
2020-11-26 13:04:11 +00:00
int GroupPanel::computeMembersListTop() const {
#ifdef Q_OS_WIN
return st::callTitleButton.height + st::groupCallMembersMargin.top() / 2;
#elif defined Q_OS_MAC // Q_OS_WIN
return st::groupCallMembersMargin.top() * 2;
#else // Q_OS_WIN || Q_OS_MAC
return st::groupCallMembersMargin.top();
#endif // Q_OS_WIN || Q_OS_MAC
}
std::optional<QRect> GroupPanel::computeTitleRect() const {
#ifdef Q_OS_WIN
const auto controls = _controls->geometry();
return QRect(0, 0, controls.x(), controls.height());
#else // Q_OS_WIN
return std::nullopt;
#endif // Q_OS_WIN
}
2020-11-24 12:54:20 +00:00
void GroupPanel::updateControlsGeometry() {
if (widget()->size().isEmpty()) {
return;
}
2020-11-25 14:09:59 +00:00
const auto desiredHeight = _members->desiredHeight();
2020-11-26 13:04:11 +00:00
const auto membersWidthAvailable = widget()->width()
- st::groupCallMembersMargin.left()
- st::groupCallMembersMargin.right();
const auto membersWidthMin = st::groupCallWidth
2020-11-25 14:09:59 +00:00
- st::groupCallMembersMargin.left()
- st::groupCallMembersMargin.right();
2020-11-26 13:04:11 +00:00
const auto membersWidth = std::clamp(
membersWidthAvailable,
membersWidthMin,
st::groupCallMembersWidthMax);
2020-11-28 12:00:06 +00:00
const auto muteTop = widget()->height() - st::groupCallMuteBottomSkip;
const auto buttonsTop = widget()->height() - st::groupCallButtonBottomSkip;
2020-11-26 13:04:11 +00:00
const auto membersTop = computeMembersListTop();
2020-11-28 12:00:06 +00:00
const auto availableHeight = muteTop
2020-11-25 14:09:59 +00:00
- membersTop
- st::groupCallMembersMargin.bottom();
_members->setGeometry(
(widget()->width() - membersWidth) / 2,
2020-11-25 14:09:59 +00:00
membersTop,
membersWidth,
std::min(desiredHeight, availableHeight));
2020-11-28 12:00:06 +00:00
const auto muteSize = _mute->innerSize().width();
const auto fullWidth = muteSize
+ 2 * _settings->width()
+ 2 * st::groupCallButtonSkip;
_mute->moveInner({ (widget()->width() - muteSize) / 2, muteTop });
_settings->moveToLeft((widget()->width() - fullWidth) / 2, buttonsTop);
_hangup->moveToRight((widget()->width() - fullWidth) / 2, buttonsTop);
2020-11-26 13:04:11 +00:00
refreshTitle();
}
void GroupPanel::refreshTitle() {
if (const auto titleRect = computeTitleRect()) {
if (!_title) {
_title.create(
widget(),
tr::lng_group_call_title(),
st::groupCallHeaderLabel);
_title->setAttribute(Qt::WA_TransparentForMouseEvents);
2020-11-26 13:04:11 +00:00
_window->setTitle(u" "_q);
}
const auto best = _title->naturalWidth();
const auto from = (widget()->width() - best) / 2;
const auto top = (computeMembersListTop() - _title->height()) / 2;
const auto left = titleRect->x();
if (from >= left && from + best <= left + titleRect->width()) {
_title->resizeToWidth(best);
_title->moveToLeft(from, top);
} else if (titleRect->width() < best) {
_title->resizeToWidth(titleRect->width());
_title->moveToLeft(left, top);
} else if (from < left) {
_title->resizeToWidth(best);
_title->moveToLeft(left, top);
} else {
_title->resizeToWidth(best);
_title->moveToLeft(left + titleRect->width() - best, top);
}
} else if (_title) {
_title.destroy();
_window->setTitle(tr::lng_group_call_title(tr::now));
}
2020-11-24 12:54:20 +00:00
}
void GroupPanel::paint(QRect clip) {
Painter p(widget());
auto region = QRegion(clip);
for (const auto rect : region) {
2020-11-25 14:09:59 +00:00
p.fillRect(rect, st::groupCallBg);
2020-11-24 12:54:20 +00:00
}
}
2020-11-25 14:09:59 +00:00
bool GroupPanel::handleClose() {
2020-11-24 12:54:20 +00:00
if (_call) {
2020-11-25 14:09:59 +00:00
_window->hide();
return true;
2020-11-24 12:54:20 +00:00
}
2020-11-25 14:09:59 +00:00
return false;
2020-11-24 12:54:20 +00:00
}
not_null<Ui::RpWidget*> GroupPanel::widget() const {
return _window->body();
}
} // namespace Calls