mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-16 06:07:06 +02:00
Finish theming for voice recording in stories.
This commit is contained in:
parent
a02876562a
commit
00b4f77384
10 changed files with 244 additions and 123 deletions
|
@ -139,6 +139,29 @@ SendButton {
|
|||
sendDisabledFg: color;
|
||||
}
|
||||
|
||||
RecordBarLock {
|
||||
ripple: RippleAnimation;
|
||||
originTop: icon;
|
||||
originBottom: icon;
|
||||
originBody: icon;
|
||||
shadowTop: icon;
|
||||
shadowBottom: icon;
|
||||
shadowBody: icon;
|
||||
arrow: icon;
|
||||
fg: color;
|
||||
}
|
||||
|
||||
RecordBar {
|
||||
radius: pixels;
|
||||
bg: color;
|
||||
durationFg: color;
|
||||
cancel: color;
|
||||
cancelActive: color;
|
||||
cancelRipple: RippleAnimation;
|
||||
lock: RecordBarLock;
|
||||
remove: IconButton;
|
||||
}
|
||||
|
||||
ComposeControls {
|
||||
bg: color;
|
||||
radius: pixels;
|
||||
|
@ -149,6 +172,7 @@ ComposeControls {
|
|||
emoji: EmojiButton;
|
||||
suggestions: EmojiSuggestions;
|
||||
tabbed: EmojiPan;
|
||||
record: RecordBar;
|
||||
}
|
||||
|
||||
switchPmButton: RoundButton(defaultBoxButton) {
|
||||
|
@ -875,7 +899,6 @@ historyRecordTextRight: 25px;
|
|||
historyRecordLockShowDuration: historyToDownDuration;
|
||||
historyRecordLockSize: size(75px, 133px);
|
||||
|
||||
historyRecordLockIconFg: historyToDownFg;
|
||||
historyRecordLockIconSize: size(14px, 17px);
|
||||
historyRecordLockIconBottomHeight: 9px;
|
||||
historyRecordLockIconLineHeight: 2px;
|
||||
|
@ -916,6 +939,29 @@ historySilentToggle: IconButton(historyBotKeyboardShow) {
|
|||
historySilentToggleOn: icon {{ "chat/input_silent_on", historyComposeIconFg }};
|
||||
historySilentToggleOnOver: icon {{ "chat/input_silent_on", historyComposeIconFgOver }};
|
||||
|
||||
defaultRecordBarLock: RecordBarLock {
|
||||
ripple: defaultRippleAnimation;
|
||||
originTop: historyRecordLockTop;
|
||||
originBottom: historyRecordLockBottom;
|
||||
originBody: historyRecordLockBody;
|
||||
shadowTop: historyRecordLockTopShadow;
|
||||
shadowBottom: historyRecordLockBottomShadow;
|
||||
shadowBody: historyRecordLockBodyShadow;
|
||||
arrow: historyRecordLockArrow;
|
||||
fg: historyToDownFg;
|
||||
}
|
||||
defaultRecordBar: RecordBar {
|
||||
bg: historyComposeAreaBg;
|
||||
durationFg: historyRecordDurationFg;
|
||||
cancel: historyRecordCancel;
|
||||
cancelActive: historyRecordCancelActive;
|
||||
cancelRipple: RippleAnimation(defaultRippleAnimation) {
|
||||
color: lightButtonBgRipple;
|
||||
}
|
||||
lock: defaultRecordBarLock;
|
||||
remove: historyRecordDelete;
|
||||
}
|
||||
|
||||
historySend: SendButton {
|
||||
inner: IconButton(historyAttach) {
|
||||
icon: historySendIcon;
|
||||
|
@ -936,4 +982,5 @@ defaultComposeControls: ComposeControls {
|
|||
emoji: historyAttachEmoji;
|
||||
suggestions: defaultEmojiSuggestions;
|
||||
tabbed: defaultEmojiPan;
|
||||
record: defaultRecordBar;
|
||||
}
|
||||
|
|
|
@ -943,18 +943,6 @@ void HistoryWidget::refreshTabbedPanel() {
|
|||
}
|
||||
|
||||
void HistoryWidget::initVoiceRecordBar() {
|
||||
{
|
||||
auto scrollHeight = rpl::combine(
|
||||
_scroll->topValue(),
|
||||
_scroll->heightValue()
|
||||
) | rpl::map([](int top, int height) {
|
||||
return top + height - st::historyRecordLockPosition.y();
|
||||
});
|
||||
_voiceRecordBar->setLockBottom(std::move(scrollHeight));
|
||||
}
|
||||
|
||||
_voiceRecordBar->setSendButtonGeometryValue(_send->geometryValue());
|
||||
|
||||
_voiceRecordBar->setStartRecordingFilter([=] {
|
||||
const auto error = [&]() -> std::optional<QString> {
|
||||
if (_peer) {
|
||||
|
|
|
@ -989,10 +989,14 @@ ComposeControls::ComposeControls(
|
|||
, _header(std::make_unique<FieldHeader>(_wrap.get(), _show))
|
||||
, _voiceRecordBar(std::make_unique<VoiceRecordBar>(
|
||||
_wrap.get(),
|
||||
parent,
|
||||
_show,
|
||||
_send,
|
||||
st::historySendSize.height()))
|
||||
Controls::VoiceRecordBarDescriptor{
|
||||
.outerContainer = parent,
|
||||
.show = _show,
|
||||
.send = _send,
|
||||
.stOverride = &_st.record,
|
||||
.recorderHeight = st::historySendSize.height(),
|
||||
.lockFromBottom = descriptor.voiceLockFromBottom,
|
||||
}))
|
||||
, _sendMenuType(descriptor.sendMenuType)
|
||||
, _unavailableEmojiPasted(std::move(descriptor.unavailableEmojiPasted))
|
||||
, _saveDraftTimer([=] { saveDraft(); })
|
||||
|
@ -2278,26 +2282,6 @@ void ComposeControls::initVoiceRecordBar() {
|
|||
return false;
|
||||
});
|
||||
|
||||
{
|
||||
auto geometry = rpl::merge(
|
||||
_wrap->geometryValue(),
|
||||
_send->geometryValue()
|
||||
) | rpl::map([=](QRect geometry) {
|
||||
auto r = _send->geometry();
|
||||
r.setY(r.y() + _wrap->y());
|
||||
return r;
|
||||
});
|
||||
_voiceRecordBar->setSendButtonGeometryValue(std::move(geometry));
|
||||
}
|
||||
|
||||
{
|
||||
auto bottom = _wrap->geometryValue(
|
||||
) | rpl::map([=](QRect geometry) {
|
||||
return geometry.y() - st::historyRecordLockPosition.y();
|
||||
});
|
||||
_voiceRecordBar->setLockBottom(std::move(bottom));
|
||||
}
|
||||
|
||||
_voiceRecordBar->updateSendButtonTypeRequests(
|
||||
) | rpl::start_with_next([=] {
|
||||
updateSendButtonType();
|
||||
|
|
|
@ -102,6 +102,7 @@ struct ComposeControlsDescriptor {
|
|||
SendMenu::Type sendMenuType = {};
|
||||
Window::SessionController *regularWindow = nullptr;
|
||||
rpl::producer<ChatHelpers::FileChosen> stickerOrEmojiChosen;
|
||||
bool voiceLockFromBottom = false;
|
||||
ChatHelpers::ComposeFeatures features;
|
||||
};
|
||||
|
||||
|
|
|
@ -27,17 +27,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "media/audio/media_audio_capture.h"
|
||||
#include "media/player/media_player_button.h"
|
||||
#include "media/player/media_player_instance.h"
|
||||
#include "styles/style_chat.h"
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_media_player.h"
|
||||
#include "ui/controls/send_button.h"
|
||||
#include "ui/effects/animation_value.h"
|
||||
#include "ui/effects/ripple_animation.h"
|
||||
#include "ui/text/format_values.h"
|
||||
#include "ui/painter.h"
|
||||
#include "styles/style_chat.h"
|
||||
#include "styles/style_chat_helpers.h"
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_media_player.h"
|
||||
|
||||
namespace HistoryView::Controls {
|
||||
|
||||
namespace {
|
||||
|
||||
using SendActionUpdate = VoiceRecordBar::SendActionUpdate;
|
||||
|
@ -206,6 +206,7 @@ class ListenWrap final {
|
|||
public:
|
||||
ListenWrap(
|
||||
not_null<Ui::RpWidget*> parent,
|
||||
const style::RecordBar &st,
|
||||
not_null<Main::Session*> session,
|
||||
::Media::Capture::Result &&data,
|
||||
const style::font &font);
|
||||
|
@ -231,12 +232,12 @@ private:
|
|||
|
||||
not_null<Ui::RpWidget*> _parent;
|
||||
|
||||
const style::RecordBar &_st;
|
||||
const not_null<Main::Session*> _session;
|
||||
const not_null<DocumentData*> _document;
|
||||
const std::unique_ptr<VoiceData> _voiceData;
|
||||
const std::shared_ptr<Data::DocumentMedia> _mediaView;
|
||||
const std::unique_ptr<::Media::Capture::Result> _data;
|
||||
const style::IconButton &_stDelete;
|
||||
const base::unique_qptr<Ui::IconButton> _delete;
|
||||
const style::font &_durationFont;
|
||||
const QString _duration;
|
||||
|
@ -264,17 +265,18 @@ private:
|
|||
|
||||
ListenWrap::ListenWrap(
|
||||
not_null<Ui::RpWidget*> parent,
|
||||
const style::RecordBar &st,
|
||||
not_null<Main::Session*> session,
|
||||
::Media::Capture::Result &&data,
|
||||
const style::font &font)
|
||||
: _parent(parent)
|
||||
, _st(st)
|
||||
, _session(session)
|
||||
, _document(DummyDocument(&session->data()))
|
||||
, _voiceData(ProcessCaptureResult(data))
|
||||
, _mediaView(_document->createMediaView())
|
||||
, _data(std::make_unique<::Media::Capture::Result>(std::move(data)))
|
||||
, _stDelete(st::historyRecordDelete)
|
||||
, _delete(base::make_unique_q<Ui::IconButton>(parent, _stDelete))
|
||||
, _delete(base::make_unique_q<Ui::IconButton>(parent, _st.remove))
|
||||
, _durationFont(font)
|
||||
, _duration(Ui::FormatDurationText(
|
||||
float64(_data->samples) / ::Media::Player::kDefaultFrequency))
|
||||
|
@ -299,7 +301,7 @@ void ListenWrap::init() {
|
|||
_waveformBgRect = QRect({ 0, 0 }, size)
|
||||
.marginsRemoved(st::historyRecordWaveformBgMargins);
|
||||
{
|
||||
const auto m = _stDelete.width + _waveformBgRect.height() / 2;
|
||||
const auto m = _st.remove.width + _waveformBgRect.height() / 2;
|
||||
_waveformBgFinalCenterRect = _waveformBgRect.marginsRemoved(
|
||||
style::margins(m, 0, m, 0));
|
||||
}
|
||||
|
@ -319,22 +321,23 @@ void ListenWrap::init() {
|
|||
PainterHighQualityEnabler hq(p);
|
||||
const auto progress = _showProgress.current();
|
||||
p.setOpacity(progress);
|
||||
const auto &remove = _st.remove;
|
||||
if (progress > 0. && progress < 1.) {
|
||||
_stDelete.icon.paint(p, _stDelete.iconPosition, _parent->width());
|
||||
remove.icon.paint(p, remove.iconPosition, _parent->width());
|
||||
}
|
||||
|
||||
{
|
||||
const auto hideOffset = _isShowAnimation
|
||||
? 0
|
||||
: anim::interpolate(kHideWaveformBgOffset, 0, progress);
|
||||
const auto deleteIconLeft = _stDelete.iconPosition.x();
|
||||
const auto deleteIconLeft = remove.iconPosition.x();
|
||||
const auto bgRectRight = anim::interpolate(
|
||||
deleteIconLeft,
|
||||
_stDelete.width,
|
||||
remove.width,
|
||||
_isShowAnimation ? progress : 1.);
|
||||
const auto bgRectLeft = anim::interpolate(
|
||||
_parent->width() - deleteIconLeft - _waveformBgRect.height(),
|
||||
_stDelete.width,
|
||||
remove.width,
|
||||
_isShowAnimation ? progress : 1.);
|
||||
const auto bgRectMargins = style::margins(
|
||||
bgRectLeft - hideOffset,
|
||||
|
@ -357,7 +360,7 @@ void ListenWrap::init() {
|
|||
p.setOpacity(progress);
|
||||
}
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(st::historyRecordCancelActive);
|
||||
p.setBrush(_st.cancelActive);
|
||||
QPainterPath path;
|
||||
path.setFillRule(Qt::WindingFill);
|
||||
path.addEllipse(bgLeftCircleRect);
|
||||
|
@ -605,10 +608,13 @@ rpl::lifetime &ListenWrap::lifetime() {
|
|||
|
||||
class RecordLock final : public Ui::RippleButton {
|
||||
public:
|
||||
RecordLock(not_null<Ui::RpWidget*> parent);
|
||||
RecordLock(
|
||||
not_null<Ui::RpWidget*> parent,
|
||||
const style::RecordBarLock &st);
|
||||
|
||||
void requestPaintProgress(float64 progress);
|
||||
void requestPaintLockToStopProgress(float64 progress);
|
||||
void setVisibleTopPart(int part);
|
||||
|
||||
[[nodiscard]] rpl::producer<> locks() const;
|
||||
[[nodiscard]] bool isLocked() const;
|
||||
|
@ -627,6 +633,7 @@ private:
|
|||
void setProgress(float64 progress);
|
||||
void startLockingAnimation(float64 to);
|
||||
|
||||
const style::RecordBarLock &_st;
|
||||
const QRect _rippleRect;
|
||||
const QPen _arcPen;
|
||||
|
||||
|
@ -634,10 +641,15 @@ private:
|
|||
|
||||
float64 _lockToStopProgress = 0.;
|
||||
rpl::variable<float64> _progress = 0.;
|
||||
int _visibleTopPart = -1;
|
||||
|
||||
};
|
||||
|
||||
RecordLock::RecordLock(not_null<Ui::RpWidget*> parent)
|
||||
: RippleButton(parent, st::defaultRippleAnimation)
|
||||
RecordLock::RecordLock(
|
||||
not_null<Ui::RpWidget*> parent,
|
||||
const style::RecordBarLock &st)
|
||||
: RippleButton(parent, st.ripple)
|
||||
, _st(st)
|
||||
, _rippleRect(QRect(
|
||||
0,
|
||||
0,
|
||||
|
@ -653,6 +665,10 @@ RecordLock::RecordLock(not_null<Ui::RpWidget*> parent)
|
|||
init();
|
||||
}
|
||||
|
||||
void RecordLock::setVisibleTopPart(int part) {
|
||||
_visibleTopPart = part;
|
||||
}
|
||||
|
||||
void RecordLock::init() {
|
||||
shownValue(
|
||||
) | rpl::start_with_next([=](bool shown) {
|
||||
|
@ -670,7 +686,13 @@ void RecordLock::init() {
|
|||
|
||||
paintRequest(
|
||||
) | rpl::start_with_next([=](const QRect &clip) {
|
||||
if (!_visibleTopPart) {
|
||||
return;
|
||||
}
|
||||
Painter p(this);
|
||||
if (_visibleTopPart > 0 && _visibleTopPart < height()) {
|
||||
p.setClipRect(0, 0, width(), _visibleTopPart);
|
||||
}
|
||||
if (isLocked()) {
|
||||
const auto top = anim::interpolate(
|
||||
0,
|
||||
|
@ -687,12 +709,12 @@ void RecordLock::init() {
|
|||
void RecordLock::drawProgress(Painter &p) {
|
||||
const auto progress = _progress.current();
|
||||
|
||||
const auto &originTop = st::historyRecordLockTop;
|
||||
const auto &originBottom = st::historyRecordLockBottom;
|
||||
const auto &originBody = st::historyRecordLockBody;
|
||||
const auto &shadowTop = st::historyRecordLockTopShadow;
|
||||
const auto &shadowBottom = st::historyRecordLockBottomShadow;
|
||||
const auto &shadowBody = st::historyRecordLockBodyShadow;
|
||||
const auto &originTop = _st.originTop;
|
||||
const auto &originBottom = _st.originBottom;
|
||||
const auto &originBody = _st.originBody;
|
||||
const auto &shadowTop = _st.shadowTop;
|
||||
const auto &shadowBottom = _st.shadowBottom;
|
||||
const auto &shadowBody = _st.shadowBody;
|
||||
const auto &shadowMargins = st::historyRecordLockMargin;
|
||||
|
||||
const auto bottomMargin = anim::interpolate(
|
||||
|
@ -739,7 +761,7 @@ void RecordLock::drawProgress(Painter &p) {
|
|||
originBody.fill(p, content);
|
||||
}
|
||||
{
|
||||
const auto &arrow = st::historyRecordLockArrow;
|
||||
const auto &arrow = _st.arrow;
|
||||
const auto arrowRect = QRect(
|
||||
inner.x(),
|
||||
content.y() + content.height() - arrow.height() / 2,
|
||||
|
@ -791,7 +813,7 @@ void RecordLock::drawProgress(Painter &p) {
|
|||
PainterHighQualityEnabler hq(p);
|
||||
p.translate(inner.topLeft() + lockTranslation);
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(st::historyRecordLockIconFg);
|
||||
p.setBrush(_st.fg);
|
||||
p.drawRoundedRect(blockRect, xRadius, 3);
|
||||
} else {
|
||||
// Paint an animation frame.
|
||||
|
@ -844,7 +866,7 @@ void RecordLock::drawProgress(Painter &p) {
|
|||
|
||||
p.drawImage(
|
||||
inner.topLeft(),
|
||||
style::colorizeImage(frame, st::historyRecordLockIconFg));
|
||||
style::colorizeImage(frame, _st.fg));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -914,7 +936,10 @@ QPoint RecordLock::prepareRippleStartPosition() const {
|
|||
|
||||
class CancelButton final : public Ui::RippleButton {
|
||||
public:
|
||||
CancelButton(not_null<Ui::RpWidget*> parent, int height);
|
||||
CancelButton(
|
||||
not_null<Ui::RpWidget*> parent,
|
||||
const style::RecordBar &st,
|
||||
int height);
|
||||
|
||||
void requestPaintProgress(float64 progress);
|
||||
|
||||
|
@ -925,6 +950,7 @@ protected:
|
|||
private:
|
||||
void init();
|
||||
|
||||
const style::RecordBar &_st;
|
||||
const int _width;
|
||||
const QRect _rippleRect;
|
||||
|
||||
|
@ -934,8 +960,12 @@ private:
|
|||
|
||||
};
|
||||
|
||||
CancelButton::CancelButton(not_null<Ui::RpWidget*> parent, int height)
|
||||
: Ui::RippleButton(parent, st::defaultLightButton.ripple)
|
||||
CancelButton::CancelButton(
|
||||
not_null<Ui::RpWidget*> parent,
|
||||
const style::RecordBar &st,
|
||||
int height)
|
||||
: Ui::RippleButton(parent, st.cancelRipple)
|
||||
, _st(st)
|
||||
, _width(st::historyRecordCancelButtonWidth)
|
||||
, _rippleRect(QRect(0, (height - _width) / 2, _width, _width))
|
||||
, _text(st::semiboldTextStyle, tr::lng_selected_clear(tr::now)) {
|
||||
|
@ -958,7 +988,7 @@ void CancelButton::init() {
|
|||
|
||||
paintRipple(p, _rippleRect.x(), _rippleRect.y());
|
||||
|
||||
p.setPen(st::historyRecordCancelButtonFg);
|
||||
p.setPen(_st.cancelActive);
|
||||
_text.draw(
|
||||
p,
|
||||
0,
|
||||
|
@ -983,24 +1013,23 @@ void CancelButton::requestPaintProgress(float64 progress) {
|
|||
|
||||
VoiceRecordBar::VoiceRecordBar(
|
||||
not_null<Ui::RpWidget*> parent,
|
||||
not_null<Ui::RpWidget*> sectionWidget,
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
std::shared_ptr<Ui::SendButton> send,
|
||||
int recorderHeight)
|
||||
VoiceRecordBarDescriptor &&descriptor)
|
||||
: RpWidget(parent)
|
||||
, _sectionWidget(sectionWidget)
|
||||
, _show(std::move(show))
|
||||
, _send(send)
|
||||
, _lock(std::make_unique<RecordLock>(sectionWidget))
|
||||
, _level(std::make_unique<VoiceRecordButton>(sectionWidget))
|
||||
, _cancel(std::make_unique<CancelButton>(this, recorderHeight))
|
||||
, _st(descriptor.stOverride ? *descriptor.stOverride : st::defaultRecordBar)
|
||||
, _outerContainer(descriptor.outerContainer)
|
||||
, _show(std::move(descriptor.show))
|
||||
, _send(std::move(descriptor.send))
|
||||
, _lock(std::make_unique<RecordLock>(_outerContainer, _st.lock))
|
||||
, _level(std::make_unique<VoiceRecordButton>(_outerContainer, _st))
|
||||
, _cancel(std::make_unique<CancelButton>(this, _st, descriptor.recorderHeight))
|
||||
, _startTimer([=] { startRecording(); })
|
||||
, _message(
|
||||
st::historyRecordTextStyle,
|
||||
tr::lng_record_cancel(tr::now),
|
||||
TextParseOptions{ TextParseMultiline, 0, 0, Qt::LayoutDirectionAuto })
|
||||
, _lockFromBottom(descriptor.lockFromBottom)
|
||||
, _cancelFont(st::historyRecordFont) {
|
||||
resize(QSize(parent->width(), recorderHeight));
|
||||
resize(QSize(parent->width(), descriptor.recorderHeight));
|
||||
init();
|
||||
hideFast();
|
||||
}
|
||||
|
@ -1010,7 +1039,12 @@ VoiceRecordBar::VoiceRecordBar(
|
|||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
std::shared_ptr<Ui::SendButton> send,
|
||||
int recorderHeight)
|
||||
: VoiceRecordBar(parent, parent, std::move(show), send, recorderHeight) {
|
||||
: VoiceRecordBar(parent, {
|
||||
.outerContainer = parent,
|
||||
.show = std::move(show),
|
||||
.send = std::move(send),
|
||||
.recorderHeight = recorderHeight,
|
||||
}) {
|
||||
}
|
||||
|
||||
VoiceRecordBar::~VoiceRecordBar() {
|
||||
|
@ -1040,14 +1074,32 @@ void VoiceRecordBar::updateMessageGeometry() {
|
|||
}
|
||||
|
||||
void VoiceRecordBar::updateLockGeometry() {
|
||||
const auto right = anim::interpolate(
|
||||
-_lock->width(),
|
||||
st::historyRecordLockPosition.x(),
|
||||
_showLockAnimation.value(_lockShowing.current() ? 1. : 0.));
|
||||
_lock->moveToRight(right, _lock->y());
|
||||
const auto parent = parentWidget();
|
||||
const auto me = Ui::MapFrom(_outerContainer, parent, geometry());
|
||||
const auto finalTop = me.y()
|
||||
- st::historyRecordLockPosition.y()
|
||||
- _lock->height();
|
||||
const auto finalRight = _outerContainer->width()
|
||||
- (me.x() + me.width())
|
||||
+ st::historyRecordLockPosition.x();
|
||||
const auto progress = _showLockAnimation.value(
|
||||
_lockShowing.current() ? 1. : 0.);
|
||||
if (_lockFromBottom) {
|
||||
const auto top = anim::interpolate(me.y(), finalTop, progress);
|
||||
_lock->moveToRight(finalRight, top);
|
||||
_lock->setVisibleTopPart(me.y() - top);
|
||||
} else {
|
||||
const auto from = -_lock->width();
|
||||
const auto right = anim::interpolate(from, finalRight, progress);
|
||||
_lock->moveToRight(right, finalTop);
|
||||
}
|
||||
}
|
||||
|
||||
void VoiceRecordBar::init() {
|
||||
if (_st.radius > 0) {
|
||||
_backgroundRect.emplace(_st.radius, _st.bg);
|
||||
}
|
||||
|
||||
// Keep VoiceRecordBar behind SendButton.
|
||||
rpl::single(
|
||||
) | rpl::then(
|
||||
|
@ -1087,7 +1139,6 @@ void VoiceRecordBar::init() {
|
|||
}
|
||||
_cancel->moveToLeft((size.width() - _cancel->width()) / 2, 0);
|
||||
updateMessageGeometry();
|
||||
updateLockGeometry();
|
||||
}, lifetime());
|
||||
|
||||
paintRequest(
|
||||
|
@ -1096,7 +1147,11 @@ void VoiceRecordBar::init() {
|
|||
if (_showAnimation.animating()) {
|
||||
p.setOpacity(showAnimationRatio());
|
||||
}
|
||||
p.fillRect(clip, st::historyComposeAreaBg);
|
||||
if (_backgroundRect) {
|
||||
_backgroundRect->paint(p, rect());
|
||||
} else {
|
||||
p.fillRect(clip, _st.bg);
|
||||
}
|
||||
|
||||
p.setOpacity(std::min(p.opacity(), 1. - showListenAnimationRatio()));
|
||||
const auto opacity = p.opacity();
|
||||
|
@ -1237,6 +1292,9 @@ void VoiceRecordBar::init() {
|
|||
_cancel->setClickedCallback([=] {
|
||||
hideAnimated();
|
||||
});
|
||||
|
||||
initLockGeometry();
|
||||
initLevelGeometry();
|
||||
}
|
||||
|
||||
void VoiceRecordBar::activeAnimate(bool active) {
|
||||
|
@ -1278,22 +1336,25 @@ void VoiceRecordBar::setStartRecordingFilter(Fn<bool()> &&callback) {
|
|||
_startRecordingFilter = std::move(callback);
|
||||
}
|
||||
|
||||
void VoiceRecordBar::setLockBottom(rpl::producer<int> &&bottom) {
|
||||
void VoiceRecordBar::initLockGeometry() {
|
||||
rpl::combine(
|
||||
std::move(bottom),
|
||||
_lock->sizeValue() | rpl::map_to(true) // Dummy value.
|
||||
) | rpl::start_with_next([=](int value, bool dummy) {
|
||||
_lock->moveToLeft(_lock->x(), value - _lock->height());
|
||||
_lock->heightValue(),
|
||||
geometryValue(),
|
||||
static_cast<Ui::RpWidget*>(parentWidget())->geometryValue()
|
||||
) | rpl::start_with_next([=] {
|
||||
updateLockGeometry();
|
||||
}, lifetime());
|
||||
}
|
||||
|
||||
void VoiceRecordBar::setSendButtonGeometryValue(
|
||||
rpl::producer<QRect> &&geometry) {
|
||||
std::move(
|
||||
geometry
|
||||
) | rpl::start_with_next([=](QRect r) {
|
||||
const auto center = (r.width() - _level->width()) / 2;
|
||||
_level->moveToLeft(r.x() + center, r.y() + center);
|
||||
void VoiceRecordBar::initLevelGeometry() {
|
||||
rpl::combine(
|
||||
_send->geometryValue(),
|
||||
geometryValue(),
|
||||
static_cast<Ui::RpWidget*>(parentWidget())->geometryValue()
|
||||
) | rpl::start_with_next([=](QRect send, QRect me, QRect parent) {
|
||||
const auto mapped = Ui::MapFrom(_outerContainer, this, send);
|
||||
const auto center = (send.width() - _level->width()) / 2;
|
||||
_level->moveToLeft(mapped.x() + center, mapped.y() + center);
|
||||
}, lifetime());
|
||||
}
|
||||
|
||||
|
@ -1431,6 +1492,7 @@ void VoiceRecordBar::stopRecording(StopType type) {
|
|||
} else if (type == StopType::Listen) {
|
||||
_listen = std::make_unique<ListenWrap>(
|
||||
this,
|
||||
_st,
|
||||
&_show->session(),
|
||||
std::move(data),
|
||||
_cancelFont);
|
||||
|
@ -1444,7 +1506,7 @@ void VoiceRecordBar::stopRecording(StopType type) {
|
|||
void VoiceRecordBar::drawDuration(Painter &p) {
|
||||
const auto duration = FormatVoiceDuration(_recordingSamples);
|
||||
p.setFont(_cancelFont);
|
||||
p.setPen(st::historyRecordDurationFg);
|
||||
p.setPen(_st.durationFg);
|
||||
|
||||
p.drawText(_durationRect, style::al_left, duration);
|
||||
}
|
||||
|
@ -1478,11 +1540,7 @@ void VoiceRecordBar::drawRedCircle(Painter &p) {
|
|||
}
|
||||
|
||||
void VoiceRecordBar::drawMessage(Painter &p, float64 recordActive) {
|
||||
p.setPen(
|
||||
anim::pen(
|
||||
st::historyRecordCancel,
|
||||
st::historyRecordCancelActive,
|
||||
1. - recordActive));
|
||||
p.setPen(anim::pen(_st.cancel, _st.cancelActive, 1. - recordActive));
|
||||
|
||||
const auto opacity = p.opacity();
|
||||
p.setOpacity(opacity * (1. - _lock->lockToStopProgress()));
|
||||
|
@ -1621,8 +1679,8 @@ void VoiceRecordBar::computeAndSetLockProgress(QPoint globalPos) {
|
|||
|
||||
void VoiceRecordBar::orderControls() {
|
||||
stackUnder(_send.get());
|
||||
_level->raise();
|
||||
_lock->raise();
|
||||
_level->raise();
|
||||
}
|
||||
|
||||
void VoiceRecordBar::installListenStateFilter() {
|
||||
|
|
|
@ -11,10 +11,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "base/timer.h"
|
||||
#include "history/view/controls/compose_controls_common.h"
|
||||
#include "ui/effects/animations.h"
|
||||
#include "ui/round_rect.h"
|
||||
#include "ui/rp_widget.h"
|
||||
|
||||
struct VoiceData;
|
||||
|
||||
namespace style {
|
||||
struct RecordBar;
|
||||
} // namespace style
|
||||
|
||||
namespace Ui {
|
||||
class SendButton;
|
||||
} // namespace Ui
|
||||
|
@ -34,6 +39,15 @@ class ListenWrap;
|
|||
class RecordLock;
|
||||
class CancelButton;
|
||||
|
||||
struct VoiceRecordBarDescriptor {
|
||||
not_null<Ui::RpWidget*> outerContainer;
|
||||
std::shared_ptr<ChatHelpers::Show> show;
|
||||
std::shared_ptr<Ui::SendButton> send;
|
||||
const style::RecordBar *stOverride = nullptr;
|
||||
int recorderHeight = 0;
|
||||
bool lockFromBottom = false;
|
||||
};
|
||||
|
||||
class VoiceRecordBar final : public Ui::RpWidget {
|
||||
public:
|
||||
using SendActionUpdate = Controls::SendActionUpdate;
|
||||
|
@ -41,10 +55,7 @@ public:
|
|||
|
||||
VoiceRecordBar(
|
||||
not_null<Ui::RpWidget*> parent,
|
||||
not_null<Ui::RpWidget*> sectionWidget,
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
std::shared_ptr<Ui::SendButton> send,
|
||||
int recorderHeight);
|
||||
VoiceRecordBarDescriptor &&descriptor);
|
||||
VoiceRecordBar(
|
||||
not_null<Ui::RpWidget*> parent,
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
|
@ -75,8 +86,6 @@ public:
|
|||
|
||||
void requestToSendWithOptions(Api::SendOptions options);
|
||||
|
||||
void setLockBottom(rpl::producer<int> &&bottom);
|
||||
void setSendButtonGeometryValue(rpl::producer<QRect> &&geometry);
|
||||
void setStartRecordingFilter(Fn<bool()> &&callback);
|
||||
|
||||
[[nodiscard]] bool isRecording() const;
|
||||
|
@ -93,6 +102,8 @@ private:
|
|||
};
|
||||
|
||||
void init();
|
||||
void initLockGeometry();
|
||||
void initLevelGeometry();
|
||||
|
||||
void updateMessageGeometry();
|
||||
void updateLockGeometry();
|
||||
|
@ -125,7 +136,8 @@ private:
|
|||
|
||||
void computeAndSetLockProgress(QPoint globalPos);
|
||||
|
||||
const not_null<Ui::RpWidget*> _sectionWidget;
|
||||
const style::RecordBar &_st;
|
||||
const not_null<Ui::RpWidget*> _outerContainer;
|
||||
const std::shared_ptr<ChatHelpers::Show> _show;
|
||||
const std::shared_ptr<Ui::SendButton> _send;
|
||||
const std::unique_ptr<RecordLock> _lock;
|
||||
|
@ -159,11 +171,13 @@ private:
|
|||
|
||||
rpl::event_stream<> _recordingTipRequests;
|
||||
bool _recordingTipRequired = false;
|
||||
bool _lockFromBottom = false;
|
||||
|
||||
const style::font &_cancelFont;
|
||||
|
||||
rpl::lifetime _recordingLifetime;
|
||||
|
||||
std::optional<Ui::RoundRect> _backgroundRect;
|
||||
Ui::Animations::Simple _showLockAnimation;
|
||||
Ui::Animations::Simple _lockToStopAnimation;
|
||||
Ui::Animations::Simple _showListenAnimation;
|
||||
|
|
|
@ -47,8 +47,11 @@ auto Blobs() {
|
|||
|
||||
} // namespace
|
||||
|
||||
VoiceRecordButton::VoiceRecordButton(not_null<Ui::RpWidget*> parent)
|
||||
VoiceRecordButton::VoiceRecordButton(
|
||||
not_null<Ui::RpWidget*> parent,
|
||||
const style::RecordBar &st)
|
||||
: AbstractButton(parent)
|
||||
, _st(st)
|
||||
, _blobs(std::make_unique<Ui::Paint::Blobs>(
|
||||
Blobs(),
|
||||
kLevelDuration,
|
||||
|
|
|
@ -11,17 +11,21 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "ui/effects/animations.h"
|
||||
#include "ui/rp_widget.h"
|
||||
|
||||
namespace Ui {
|
||||
namespace Paint {
|
||||
namespace style {
|
||||
struct RecordBar;
|
||||
} // namespace style
|
||||
|
||||
namespace Ui::Paint {
|
||||
class Blobs;
|
||||
} // namespace Paint
|
||||
} // namespace Ui
|
||||
} // namespace Ui::Paint
|
||||
|
||||
namespace HistoryView::Controls {
|
||||
|
||||
class VoiceRecordButton final : public Ui::AbstractButton {
|
||||
public:
|
||||
explicit VoiceRecordButton(not_null<Ui::RpWidget*> parent);
|
||||
VoiceRecordButton(
|
||||
not_null<Ui::RpWidget*> parent,
|
||||
const style::RecordBar &st);
|
||||
~VoiceRecordButton();
|
||||
|
||||
enum class Type {
|
||||
|
@ -43,6 +47,7 @@ public:
|
|||
private:
|
||||
void init();
|
||||
|
||||
const style::RecordBar &_st;
|
||||
std::unique_ptr<Ui::Paint::Blobs> _blobs;
|
||||
|
||||
crl::time _lastUpdateTime = 0;
|
||||
|
|
|
@ -35,6 +35,7 @@ ReplyArea::ReplyArea(not_null<Controller*> controller)
|
|||
.mode = HistoryView::ComposeControlsMode::Normal,
|
||||
.sendMenuType = SendMenu::Type::SilentOnly,
|
||||
.stickerOrEmojiChosen = _controller->stickerOrEmojiChosen(),
|
||||
.voiceLockFromBottom = true,
|
||||
.features = {
|
||||
.sendAs = false,
|
||||
.ttlInfo = false,
|
||||
|
|
|
@ -448,24 +448,23 @@ storiesComposeWhiteText: groupCallMembersFg;
|
|||
storiesComposeGrayText: groupCallMemberNotJoinedStatus;
|
||||
storiesComposeGrayIcon: groupCallMemberInactiveIcon;
|
||||
storiesComposeBlue: groupCallActiveFg;
|
||||
storiesComposeRippleLight: RippleAnimation(defaultRippleAnimation) {
|
||||
color: storiesComposeBgOver;
|
||||
}
|
||||
storiesComposeRipple: RippleAnimation(defaultRippleAnimation) {
|
||||
color: groupCallMembersBgRipple;
|
||||
}
|
||||
storiesAttach: IconButton(historyAttach) {
|
||||
icon: icon {{ "chat/input_attach", storiesComposeGrayIcon }};
|
||||
iconOver: icon {{ "chat/input_attach", storiesComposeGrayIcon }};
|
||||
ripple: RippleAnimation(defaultRippleAnimation) {
|
||||
color: storiesComposeBgOver;
|
||||
}
|
||||
ripple: storiesComposeRippleLight;
|
||||
}
|
||||
storiesRecordVoice: icon {{ "chat/input_record", storiesComposeGrayIcon }};
|
||||
storiesRecordVoiceOver: icon {{ "chat/input_record", storiesComposeGrayIcon }};
|
||||
storiesRemoveSet: IconButton(stickerPanRemoveSet) {
|
||||
icon: icon {{ "simple_close", storiesComposeGrayIcon }};
|
||||
iconOver: icon {{ "simple_close", storiesComposeGrayIcon }};
|
||||
ripple: RippleAnimation(defaultRippleAnimation) {
|
||||
color: storiesComposeBgOver;
|
||||
}
|
||||
ripple: storiesComposeRippleLight;
|
||||
}
|
||||
storiesMenu: Menu(defaultMenu) {
|
||||
itemBg: groupCallMenuBg;
|
||||
|
@ -641,4 +640,25 @@ storiesComposeControls: ComposeControls(defaultComposeControls) {
|
|||
}
|
||||
autocompleteBottomSkip: 10px;
|
||||
}
|
||||
record: RecordBar(defaultRecordBar) {
|
||||
bg: storiesComposeBg;
|
||||
durationFg: storiesComposeWhiteText;
|
||||
radius: storiesRadius;
|
||||
cancel: storiesComposeGrayText;
|
||||
cancelActive: storiesComposeBlue;
|
||||
cancelRipple: storiesComposeRippleLight;
|
||||
lock: RecordBarLock(defaultRecordBarLock) {
|
||||
ripple: storiesComposeRipple;
|
||||
originTop: icon {{ "voice_lock/record_lock_top", storiesComposeBg }};
|
||||
originBottom: icon {{ "voice_lock/record_lock_bottom", storiesComposeBg }};
|
||||
originBody: icon {{ "voice_lock/record_lock_body", storiesComposeBg }};
|
||||
arrow: icon {{ "voice_lock/voice_arrow", storiesComposeGrayIcon }};
|
||||
fg: storiesComposeGrayIcon;
|
||||
}
|
||||
remove: IconButton(storiesAttach) {
|
||||
icon: icon {{ "info/info_media_delete", storiesComposeGrayIcon }};
|
||||
iconOver: icon {{ "info/info_media_delete", storiesComposeGrayIcon }};
|
||||
iconPosition: point(10px, 11px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue