Finish theming for voice recording in stories.

This commit is contained in:
John Preston 2023-05-18 12:56:15 +04:00
parent a02876562a
commit 00b4f77384
10 changed files with 244 additions and 123 deletions

View File

@ -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;
}

View File

@ -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) {

View File

@ -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();

View File

@ -102,6 +102,7 @@ struct ComposeControlsDescriptor {
SendMenu::Type sendMenuType = {};
Window::SessionController *regularWindow = nullptr;
rpl::producer<ChatHelpers::FileChosen> stickerOrEmojiChosen;
bool voiceLockFromBottom = false;
ChatHelpers::ComposeFeatures features;
};

View File

@ -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() {

View File

@ -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;

View File

@ -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,

View File

@ -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;

View File

@ -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,

View File

@ -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);
}
}
}