2
0
mirror of https://github.com/telegramdesktop/tdesktop synced 2025-08-31 06:26:18 +00:00

Improve scheduled voice chat top bar design.

This commit is contained in:
John Preston
2021-04-06 13:59:14 +04:00
parent 088fda4ed8
commit 66e7f05df1
12 changed files with 260 additions and 55 deletions

View File

@@ -21,16 +21,84 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Ui {
GroupCallScheduledLeft::GroupCallScheduledLeft(TimeId date)
: _date(date)
, _datePrecise(computePreciseDate())
, _timer([=] { update(); }) {
update();
base::unixtime::updates(
) | rpl::start_with_next([=] {
restart();
}, _lifetime);
}
crl::time GroupCallScheduledLeft::computePreciseDate() const {
return crl::now() + (_date - base::unixtime::now()) * crl::time(1000);
}
void GroupCallScheduledLeft::setDate(TimeId date) {
if (_date == date) {
return;
}
_date = date;
restart();
}
void GroupCallScheduledLeft::restart() {
_datePrecise = computePreciseDate();
_timer.cancel();
update();
}
rpl::producer<QString> GroupCallScheduledLeft::text() const {
return _text.value();
}
void GroupCallScheduledLeft::update() {
const auto now = crl::now();
const auto duration = (_datePrecise - now);
const auto left = crl::time(std::round(std::abs(duration) / 1000.));
constexpr auto kDay = 24 * 60 * 60;
if (left >= kDay) {
const auto days = ((left / kDay) + 1);
_text = tr::lng_group_call_duration_days(
tr::now,
lt_count,
(duration < 0) ? (-days) : days);
} else {
const auto hours = left / (60 * 60);
const auto minutes = (left % (60 * 60)) / 60;
const auto seconds = (left % 60);
if (hours > 0) {
_text = (duration < 0 ? u"\x2212%1:%2:%3"_q : u"%1:%2:%3"_q)
.arg(hours, 2, 10, QChar('0'))
.arg(minutes, 2, 10, QChar('0'))
.arg(seconds, 2, 10, QChar('0'));
} else {
_text = (duration < 0 && left > 0 ? u"\x2212%1:%2"_q : u"%1:%2"_q)
.arg(minutes, 2, 10, QChar('0'))
.arg(seconds, 2, 10, QChar('0'));
}
}
if (left >= kDay) {
_timer.callOnce((left % kDay) * crl::time(1000));
} else {
const auto fraction = (std::abs(duration) + 500) % 1000;
if (fraction < 400 || fraction > 600) {
const auto next = std::abs(duration) % 1000;
_timer.callOnce((duration < 0) ? (1000 - next) : next);
} else if (!_timer.isActive()) {
_timer.callEach(1000);
}
}
}
GroupCallBar::GroupCallBar(
not_null<QWidget*> parent,
rpl::producer<GroupCallBarContent> content,
rpl::producer<bool> &&hideBlobs)
: _wrap(parent, object_ptr<RpWidget>(parent))
, _inner(_wrap.entity())
, _join(std::make_unique<RoundButton>(
_inner.get(),
tr::lng_group_call_join(),
st::groupCallTopBarJoin))
, _shadow(std::make_unique<PlainShadow>(_wrap.parentWidget()))
, _userpics(std::make_unique<GroupCallUserpics>(
st::historyGroupCallUserpics,
@@ -55,6 +123,7 @@ GroupCallBar::GroupCallBar(
_content = content;
_userpics->update(_content.users, !_wrap.isHidden());
_inner->update();
refreshScheduledProcess();
}, lifetime());
std::move(
@@ -76,6 +145,54 @@ GroupCallBar::GroupCallBar(
GroupCallBar::~GroupCallBar() = default;
void GroupCallBar::refreshOpenBrush() {
Expects(_open != nullptr);
const auto width = _open->width();
if (_openBrushForWidth == width) {
return;
}
auto gradient = QLinearGradient(QPoint(width, 0), QPoint(-width, 0));
gradient.setStops(QGradientStops{
{ 0.0, st::groupCallForceMutedBar1->c },
{ .35, st::groupCallForceMutedBar2->c },
{ 1.0, st::groupCallForceMutedBar3->c }
});
_openBrushOverride = QBrush(std::move(gradient));
_openBrushForWidth = width;
_open->setBrushOverride(_openBrushOverride);
}
void GroupCallBar::refreshScheduledProcess() {
const auto date = _content.scheduleDate;
if (!date) {
if (_scheduledProcess) {
_scheduledProcess = nullptr;
_open = nullptr;
_join = std::make_unique<RoundButton>(
_inner.get(),
tr::lng_group_call_join(),
st::groupCallTopBarJoin);
setupRightButton(_join.get());
}
return;
} else if (!_scheduledProcess) {
_scheduledProcess = std::make_unique<GroupCallScheduledLeft>(date);
_join = nullptr;
_open = std::make_unique<RoundButton>(
_inner.get(),
_scheduledProcess->text(),
st::groupCallTopBarOpen);
setupRightButton(_open.get());
_open->widthValue(
) | rpl::start_with_next([=] {
refreshOpenBrush();
}, _open->lifetime());
} else {
_scheduledProcess->setDate(date);
}
}
void GroupCallBar::setupInner() {
_inner->resize(0, st::historyReplyHeight);
_inner->paintRequest(
@@ -102,17 +219,6 @@ void GroupCallBar::setupInner() {
return rpl::empty_value();
}) | rpl::start_to_stream(_barClicks, _inner->lifetime());
rpl::combine(
_inner->widthValue(),
_join->widthValue()
) | rpl::start_with_next([=](int outerWidth, int) {
// Skip shadow of the bar above.
const auto top = (st::historyReplyHeight
- st::lineWidth
- _join->height()) / 2 + st::lineWidth;
_join->moveToRight(top, top, outerWidth);
}, _join->lifetime());
_wrap.geometryValue(
) | rpl::start_with_next([=](QRect rect) {
updateShadowGeometry(rect);
@@ -120,6 +226,21 @@ void GroupCallBar::setupInner() {
}, _inner->lifetime());
}
void GroupCallBar::setupRightButton(not_null<RoundButton*> button) {
rpl::combine(
_inner->widthValue(),
button->widthValue()
) | rpl::start_with_next([=](int outerWidth, int) {
// Skip shadow of the bar above.
const auto top = (st::historyReplyHeight
- st::lineWidth
- button->height()) / 2 + st::lineWidth;
button->moveToRight(top, top, outerWidth);
}, button->lifetime());
button->clicks() | rpl::start_to_stream(_joinClicks, button->lifetime());
}
void GroupCallBar::paint(Painter &p) {
p.fillRect(_inner->rect(), st::historyComposeAreaBg);
@@ -131,7 +252,7 @@ void GroupCallBar::paint(Painter &p) {
p.setPen(st::defaultMessageBar.textFg);
p.setFont(font);
const auto available = _join->x() - left;
const auto available = (_join ? _join->x() : _open->x()) - left;
const auto titleWidth = font->width(_content.title);
p.drawTextLeft(
left,
@@ -279,7 +400,7 @@ rpl::producer<> GroupCallBar::barClicks() const {
}
rpl::producer<> GroupCallBar::joinClicks() const {
return _join->clicks() | rpl::to_empty;
return _joinClicks.events() | rpl::to_empty;
}
} // namespace Ui

View File

@@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/wrap/slide_wrap.h"
#include "ui/effects/animations.h"
#include "base/object_ptr.h"
#include "base/timer.h"
class Painter;
@@ -28,6 +29,27 @@ struct GroupCallBarContent {
std::vector<GroupCallUser> users;
};
class GroupCallScheduledLeft final {
public:
explicit GroupCallScheduledLeft(TimeId date);
void setDate(TimeId date);
[[nodiscard]] rpl::producer<QString> text() const;
private:
[[nodiscard]] crl::time computePreciseDate() const;
void restart();
void update();
rpl::variable<QString> _text;
TimeId _date = 0;
crl::time _datePrecise = 0;
base::Timer _timer;
rpl::lifetime _lifetime;
};
class GroupCallBar final {
public:
GroupCallBar(
@@ -57,15 +79,22 @@ public:
private:
using User = GroupCallUser;
void refreshOpenBrush();
void refreshScheduledProcess();
void updateShadowGeometry(QRect wrapGeometry);
void updateControlsGeometry(QRect wrapGeometry);
void updateUserpics();
void setupInner();
void setupRightButton(not_null<RoundButton*> button);
void paint(Painter &p);
SlideWrap<> _wrap;
not_null<RpWidget*> _inner;
std::unique_ptr<RoundButton> _join;
std::unique_ptr<RoundButton> _open;
rpl::event_stream<Qt::MouseButton> _joinClicks;
QBrush _openBrushOverride;
int _openBrushForWidth = 0;
std::unique_ptr<PlainShadow> _shadow;
rpl::event_stream<> _barClicks;
Fn<QRect(QRect)> _shadowGeometryPostprocess;
@@ -73,6 +102,7 @@ private:
bool _forceHidden = false;
GroupCallBarContent _content;
std::unique_ptr<GroupCallScheduledLeft> _scheduledProcess;
std::unique_ptr<GroupCallUserpics> _userpics;
};