tdesktop/Telegram/SourceFiles/calls/group/ui/calls_group_recording_box.cpp

377 lines
9.5 KiB
C++

/*
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/ui/calls_group_recording_box.h"
#include "lang/lang_keys.h"
#include "ui/effects/animations.h"
#include "ui/image/image_prepare.h"
#include "ui/layers/generic_box.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/fields/input_field.h"
#include "ui/widgets/labels.h"
#include "styles/style_basic.h"
#include "styles/style_calls.h"
#include "styles/style_layers.h"
#include "styles/style_boxes.h"
#include <QSvgRenderer>
namespace Calls::Group {
namespace {
constexpr auto kRoundRadius = 9;
constexpr auto kMaxGroupCallLength = 40;
constexpr auto kSwitchDuration = 200;
class GraphicButton final : public Ui::AbstractButton {
public:
GraphicButton(
not_null<Ui::RpWidget*> parent,
const QString &filename,
int selectWidth = 0);
void setToggled(bool value);
protected:
void paintEvent(QPaintEvent *e);
private:
const style::margins _margins;
QSvgRenderer _renderer;
Ui::RoundRect _roundRect;
Ui::RoundRect _roundRectSelect;
Ui::Animations::Simple _animation;
bool _toggled = false;
};
class RecordingInfo final : public Ui::RpWidget {
public:
RecordingInfo(not_null<Ui::RpWidget*> parent);
void prepareAudio();
void prepareVideo();
RecordingType type() const;
private:
void setLabel(const QString &text);
const object_ptr<Ui::VerticalLayout> _container;
RecordingType _type = RecordingType::AudioOnly;
};
class Switcher final : public Ui::RpWidget {
public:
Switcher(
not_null<Ui::RpWidget*> parent,
rpl::producer<bool> &&toggled);
RecordingType type() const;
private:
const object_ptr<Ui::BoxContentDivider> _background;
const object_ptr<RecordingInfo> _audio;
const object_ptr<RecordingInfo> _video;
bool _toggled = false;
Ui::Animations::Simple _animation;
};
GraphicButton::GraphicButton(
not_null<Ui::RpWidget*> parent,
const QString &filename,
int selectWidth)
: AbstractButton(parent)
, _margins(selectWidth, selectWidth, selectWidth, selectWidth)
, _renderer(u":/gui/recording/%1.svg"_q.arg(filename))
, _roundRect(kRoundRadius, st::groupCallMembersBg)
, _roundRectSelect(kRoundRadius, st::groupCallActiveFg) {
const auto size = style::ConvertScale(_renderer.defaultSize());
resize((QRect(QPoint(), size) + _margins).size());
}
void GraphicButton::setToggled(bool value) {
if (_toggled == value) {
return;
}
_toggled = value;
_animation.start(
[=] { update(); },
_toggled ? 0. : 1.,
_toggled ? 1. : 0.,
st::universalDuration);
}
void GraphicButton::paintEvent(QPaintEvent *e) {
auto p = QPainter(this);
const auto progress = _animation.value(_toggled ? 1. : 0.);
p.setOpacity(progress);
_roundRectSelect.paint(p, rect());
p.setOpacity(1.);
const auto r = rect() - _margins;
_roundRect.paint(p, r);
_renderer.render(&p, r);
}
RecordingInfo::RecordingInfo(not_null<Ui::RpWidget*> parent)
: RpWidget(parent)
, _container(this) {
sizeValue(
) | rpl::start_with_next([=](const QSize &size) {
_container->resizeToWidth(size.width());
}, _container->lifetime());
}
void RecordingInfo::prepareAudio() {
_type = RecordingType::AudioOnly;
setLabel(tr::lng_group_call_recording_start_audio_subtitle(tr::now));
const auto wrap = _container->add(
object_ptr<Ui::RpWidget>(_container),
style::margins(0, st::groupCallRecordingAudioSkip, 0, 0));
const auto audioIcon = Ui::CreateChild<GraphicButton>(
wrap,
"info_audio");
wrap->resize(width(), audioIcon->height());
audioIcon->setAttribute(Qt::WA_TransparentForMouseEvents);
sizeValue(
) | rpl::start_with_next([=](const QSize &size) {
audioIcon->moveToLeft((size.width() - audioIcon->width()) / 2, 0);
}, lifetime());
}
void RecordingInfo::prepareVideo() {
setLabel(tr::lng_group_call_recording_start_video_subtitle(tr::now));
const auto wrap = _container->add(
object_ptr<Ui::RpWidget>(_container),
style::margins());
const auto landscapeIcon = Ui::CreateChild<GraphicButton>(
wrap,
"info_video_landscape",
st::groupCallRecordingSelectWidth);
const auto portraitIcon = Ui::CreateChild<GraphicButton>(
wrap,
"info_video_portrait",
st::groupCallRecordingSelectWidth);
wrap->resize(width(), portraitIcon->height());
landscapeIcon->setToggled(true);
_type = RecordingType::VideoLandscape;
const auto icons = std::vector<GraphicButton*>{
landscapeIcon,
portraitIcon,
};
const auto types = std::map<GraphicButton*, RecordingType>{
{ landscapeIcon, RecordingType::VideoLandscape },
{ portraitIcon, RecordingType::VideoPortrait },
};
for (const auto icon : icons) {
icon->clicks(
) | rpl::start_with_next([=] {
for (const auto &i : icons) {
i->setToggled(icon == i);
}
_type = types.at(icon);
}, lifetime());
}
wrap->sizeValue(
) | rpl::start_with_next([=](const QSize &size) {
const auto wHalf = size.width() / icons.size();
for (auto i = 0; i < icons.size(); i++) {
const auto &icon = icons[i];
icon->moveToLeft(
wHalf * i + (wHalf - icon->width()) / 2,
(size.height() - icon->height()) / 2);
}
}, lifetime());
}
void RecordingInfo::setLabel(const QString &text) {
const auto label = _container->add(
object_ptr<Ui::FlatLabel>(
_container,
text,
st::groupCallRecordingSubLabel),
st::groupCallRecordingSubLabelMargins);
rpl::combine(
sizeValue(),
label->sizeValue()
) | rpl::start_with_next([=](QSize my, QSize labelSize) {
label->moveToLeft(
(my.width() - labelSize.width()) / 2,
label->y(),
my.width());
}, label->lifetime());
}
RecordingType RecordingInfo::type() const {
return _type;
}
Switcher::Switcher(
not_null<Ui::RpWidget*> parent,
rpl::producer<bool> &&toggled)
: RpWidget(parent)
, _background(this, st::groupCallRecordingInfoHeight, st::groupCallBg)
, _audio(this)
, _video(this) {
_audio->prepareAudio();
_video->prepareVideo();
resize(0, st::groupCallRecordingInfoHeight);
const auto updatePositions = [=](float64 progress) {
_audio->moveToLeft(-width() * progress, 0);
_video->moveToLeft(_audio->x() + _audio->width(), 0);
};
sizeValue(
) | rpl::start_with_next([=](const QSize &size) {
_audio->resize(size.width(), size.height());
_video->resize(size.width(), size.height());
updatePositions(_toggled ? 1. : 0.);
_background->lower();
_background->setGeometry(QRect(QPoint(), size));
}, lifetime());
std::move(
toggled
) | rpl::start_with_next([=](bool toggled) {
_toggled = toggled;
_animation.start(
updatePositions,
toggled ? 0. : 1.,
toggled ? 1. : 0.,
kSwitchDuration);
}, lifetime());
}
RecordingType Switcher::type() const {
return _toggled ? _video->type() : _audio->type();
}
} // namespace
void EditGroupCallTitleBox(
not_null<Ui::GenericBox*> box,
const QString &placeholder,
const QString &title,
bool livestream,
Fn<void(QString)> done) {
box->setTitle(livestream
? tr::lng_group_call_edit_title_channel()
: tr::lng_group_call_edit_title());
const auto input = box->addRow(object_ptr<Ui::InputField>(
box,
st::groupCallField,
rpl::single(placeholder),
title));
input->setMaxLength(kMaxGroupCallLength);
box->setFocusCallback([=] {
input->setFocusFast();
});
const auto submit = [=] {
const auto result = input->getLastText().trimmed();
box->closeBox();
done(result);
};
input->submits() | rpl::start_with_next(submit, input->lifetime());
box->addButton(tr::lng_settings_save(), submit);
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
}
void StartGroupCallRecordingBox(
not_null<Ui::GenericBox*> box,
Fn<void(RecordingType)> done) {
box->setTitle(tr::lng_group_call_recording_start());
box->addRow(
object_ptr<Ui::FlatLabel>(
box.get(),
tr::lng_group_call_recording_start_sure(),
st::groupCallBoxLabel));
const auto checkbox = box->addRow(
object_ptr<Ui::Checkbox>(
box,
tr::lng_group_call_recording_start_checkbox(),
false,
st::groupCallCheckbox),
style::margins(
st::boxRowPadding.left(),
st::boxRowPadding.left(),
st::boxRowPadding.right(),
st::boxRowPadding.bottom()));
const auto switcher = box->addRow(
object_ptr<Switcher>(box, checkbox->checkedChanges()),
st::groupCallRecordingInfoMargins);
box->addButton(tr::lng_continue(), [=] {
const auto type = switcher->type();
box->closeBox();
done(type);
});
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
}
void AddTitleGroupCallRecordingBox(
not_null<Ui::GenericBox*> box,
const QString &title,
Fn<void(QString)> done) {
box->setTitle(tr::lng_group_call_recording_start_title());
const auto input = box->addRow(object_ptr<Ui::InputField>(
box,
st::groupCallField,
tr::lng_group_call_recording_start_field(),
title));
box->setFocusCallback([=] {
input->setFocusFast();
});
const auto submit = [=] {
const auto result = input->getLastText().trimmed();
box->closeBox();
done(result);
};
input->submits() | rpl::start_with_next(submit, input->lifetime());
box->addButton(tr::lng_group_call_recording_start_button(), submit);
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
}
void StopGroupCallRecordingBox(
not_null<Ui::GenericBox*> box,
Fn<void(QString)> done) {
box->addRow(
object_ptr<Ui::FlatLabel>(
box.get(),
tr::lng_group_call_recording_stop_sure(),
st::groupCallBoxLabel),
style::margins(
st::boxRowPadding.left(),
st::boxPadding.top(),
st::boxRowPadding.right(),
st::boxPadding.bottom()));
box->addButton(tr::lng_box_ok(), [=] {
box->closeBox();
done(QString());
});
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
}
} // namespace Calls::Group