Improve design of mediaview controls over state.

This commit is contained in:
John Preston 2023-02-28 17:49:18 +04:00
parent df9bd91d9a
commit 29224fea66
10 changed files with 129 additions and 55 deletions

View File

@ -245,6 +245,7 @@ mediaviewTextTop: 26px;
mediaviewControlSize: 90px;
mediaviewIconSize: size(46px, 54px);
mediaviewIconOver: 36px;
mediaviewWaitHide: 2000;
mediaviewHideDuration: 1000;

View File

@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/gl/gl_shader.h"
#include "ui/painter.h"
#include "media/streaming/media_streaming_common.h"
#include "platform/platform_overlay_widget.h"
#include "base/platform/base_platform_info.h"
#include "core/crash_reports.h"
#include "styles/style_media_view.h"
@ -27,7 +28,7 @@ constexpr auto kFooterOffset = kSaveMsgOffset + 4;
constexpr auto kCaptionOffset = kFooterOffset + 4;
constexpr auto kGroupThumbsOffset = kCaptionOffset + 4;
constexpr auto kControlsOffset = kGroupThumbsOffset + 4;
constexpr auto kControlValues = 2 * 4 + 4 * 4;
constexpr auto kControlValues = 4 * 4 + 4 * 4; // over + icon
[[nodiscard]] ShaderPart FragmentApplyControlsFade() {
return {
@ -557,28 +558,37 @@ void OverlayWidget::RendererGL::paintControlsStart() {
void OverlayWidget::RendererGL::paintControl(
OverState control,
QRect outer,
float64 outerOpacity,
QRect over,
float64 overOpacity,
QRect inner,
float64 innerOpacity,
const style::icon &icon) {
const auto meta = ControlMeta(control);
Assert(meta.icon == &icon);
const auto &bg = st::mediaviewControlBg->c;
const auto bgAlpha = int(base::SafeRound(bg.alpha() * outerOpacity));
const auto overAlpha = overOpacity * kOverBackgroundOpacity;
const auto offset = kControlsOffset + (meta.index * kControlValues) / 4;
const auto fgOffset = offset + 2;
const auto bgRect = transformRect(outer);
const auto fgOffset = offset + 4;
const auto overRect = _controlsImage.texturedRect(
over,
_controlsTextures[kControlsCount]);
const auto overGeometry = transformRect(over);
const auto iconRect = _controlsImage.texturedRect(
inner,
_controlsTextures[meta.index]);
const auto iconGeometry = transformRect(iconRect.geometry);
const GLfloat coords[] = {
bgRect.left(), bgRect.top(),
bgRect.right(), bgRect.top(),
bgRect.right(), bgRect.bottom(),
bgRect.left(), bgRect.bottom(),
overGeometry.left(), overGeometry.top(),
overRect.texture.left(), overRect.texture.bottom(),
overGeometry.right(), overGeometry.top(),
overRect.texture.right(), overRect.texture.bottom(),
overGeometry.right(), overGeometry.bottom(),
overRect.texture.right(), overRect.texture.top(),
overGeometry.left(), overGeometry.bottom(),
overRect.texture.left(), overRect.texture.top(),
iconGeometry.left(), iconGeometry.top(),
iconRect.texture.left(), iconRect.texture.bottom(),
@ -592,27 +602,22 @@ void OverlayWidget::RendererGL::paintControl(
iconGeometry.left(), iconGeometry.bottom(),
iconRect.texture.left(), iconRect.texture.top(),
};
if (!outer.isEmpty() && bgAlpha > 0) {
_controlsProgram->bind();
_controlsProgram->setUniformValue("viewport", _uniformViewport);
if (!over.isEmpty() && overOpacity > 0) {
_contentBuffer->write(
offset * 4 * sizeof(GLfloat),
coords,
sizeof(coords));
_fillProgram->bind();
_fillProgram->setUniformValue("viewport", _uniformViewport);
FillRectangle(
*_f,
&*_fillProgram,
offset,
QColor(bg.red(), bg.green(), bg.blue(), bgAlpha));
_controlsProgram->setUniformValue("g_opacity", GLfloat(overAlpha));
FillTexturedRectangle(*_f, &*_controlsProgram, offset);
} else {
_contentBuffer->write(
fgOffset * 4 * sizeof(GLfloat),
coords + (fgOffset - offset) * 4,
sizeof(coords) - (fgOffset - offset) * 4 * sizeof(GLfloat));
}
_controlsProgram->bind();
_controlsProgram->setUniformValue("g_opacity", GLfloat(innerOpacity));
_controlsProgram->setUniformValue("viewport", _uniformViewport);
FillTexturedRectangle(*_f, &*_controlsProgram, fgOffset);
}
@ -645,6 +650,8 @@ void OverlayWidget::RendererGL::validateControls() {
maxWidth = std::max(meta.icon->width(), maxWidth);
fullHeight += meta.icon->height();
}
maxWidth = std::max(st::mediaviewIconOver, maxWidth);
fullHeight += st::mediaviewIconOver;
auto image = QImage(
QSize(maxWidth, fullHeight) * _factor,
QImage::Format_ARGB32_Premultiplied);
@ -661,6 +668,15 @@ void OverlayWidget::RendererGL::validateControls() {
meta.icon->size() * _factor);
height += meta.icon->height();
}
auto hq = PainterHighQualityEnabler(p);
p.setPen(Qt::NoPen);
p.setBrush(OverBackgroundColor());
p.drawEllipse(
QRect(0, height, st::mediaviewIconOver, st::mediaviewIconOver));
_controlsTextures[index++] = QRect(
QPoint(0, height) * _factor,
QSize(st::mediaviewIconOver, st::mediaviewIconOver) * _factor);
height += st::mediaviewIconOver;
}
_controlsImage.setImage(std::move(image));
}

View File

@ -60,8 +60,8 @@ private:
void paintControlsStart() override;
void paintControl(
OverState control,
QRect outer,
float64 outerOpacity,
QRect over,
float64 overOpacity,
QRect inner,
float64 innerOpacity,
const style::icon &icon) override;
@ -135,7 +135,9 @@ private:
static constexpr auto kControlsCount = 5;
[[nodiscard]] static Control ControlMeta(OverState control);
std::array<QRect, kControlsCount> _controlsTextures;
// Last one is for the over circle image.
std::array<QRect, kControlsCount + 1> _controlsTextures;
QRect _shadowTopTexture;
QRect _shadowBottomTexture;

View File

@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/painter.h"
#include "media/view/media_view_pip.h"
#include "platform/platform_overlay_widget.h"
#include "styles/style_media_view.h"
namespace Media::View {
@ -173,22 +174,20 @@ void OverlayWidget::RendererSW::paintControlsStart() {
void OverlayWidget::RendererSW::paintControl(
OverState control,
QRect outer,
float64 outerOpacity,
QRect over,
float64 overOpacity,
QRect inner,
float64 innerOpacity,
const style::icon &icon) {
if (!outer.isEmpty() && !outer.intersects(_clipOuter)) {
if (!over.isEmpty() && !over.intersects(_clipOuter)) {
return;
}
if (!outer.isEmpty() && outerOpacity > 0) {
_p->setOpacity(outerOpacity);
for (const auto &rect : *_clip) {
const auto fill = outer.intersected(rect);
if (!fill.isEmpty()) {
_p->fillRect(fill, st::mediaviewControlBg);
}
if (!over.isEmpty() && overOpacity > 0) {
if (_overControlImage.isNull()) {
validateOverControlImage();
}
_p->setOpacity(overOpacity);
_p->drawImage(over.topLeft(), _overControlImage);
}
if (inner.intersects(_clipOuter)) {
_p->setOpacity(innerOpacity);
@ -220,4 +219,22 @@ void OverlayWidget::RendererSW::paintRoundedCorners(int radius) {
// The RpWindow rounding overlay will do the job.
}
void OverlayWidget::RendererSW::validateOverControlImage() {
const auto size = QSize(st::mediaviewIconOver, st::mediaviewIconOver);
const auto alpha = base::SafeRound(kOverBackgroundOpacity * 255);
_overControlImage = QImage(
size * style::DevicePixelRatio(),
QImage::Format_ARGB32_Premultiplied);
_overControlImage.setDevicePixelRatio(style::DevicePixelRatio());
_overControlImage.fill(Qt::transparent);
Painter p(&_overControlImage);
PainterHighQualityEnabler hq(p);
p.setPen(Qt::NoPen);
auto color = OverBackgroundColor();
color.setAlpha(alpha);
p.setBrush(color);
p.drawEllipse(QRect(QPoint(), size));
}
} // namespace Media::View

View File

@ -43,8 +43,8 @@ private:
void paintControlsStart() override;
void paintControl(
OverState control,
QRect outer,
float64 outerOpacity,
QRect over,
float64 overOpacity,
QRect inner,
float64 innerOpacity,
const style::icon &icon) override;
@ -53,6 +53,8 @@ private:
void paintGroupThumbs(QRect outer, float64 opacity) override;
void paintRoundedCorners(int radius) override;
void validateOverControlImage();
[[nodiscard]] static QRect TransformRect(QRectF geometry, int rotation);
const not_null<OverlayWidget*> _owner;
@ -62,6 +64,8 @@ private:
const QRegion *_clip = nullptr;
QRect _clipOuter;
QImage _overControlImage;
};
} // namespace Media::View

View File

@ -30,8 +30,8 @@ public:
virtual void paintControlsStart() = 0;
virtual void paintControl(
OverState control,
QRect outer,
float64 outerOpacity,
QRect over,
float64 overOpacity,
QRect inner,
float64 innerOpacity,
const style::icon &icon) = 0;

View File

@ -781,10 +781,15 @@ void OverlayWidget::updateGeometryToScreen(bool inMove) {
}
void OverlayWidget::updateControlsGeometry() {
const auto overRect = QRect(
QPoint(),
QSize(st::mediaviewIconOver, st::mediaviewIconOver));
const auto navSkip = st::mediaviewHeaderTop;
_leftNav = QRect(0, navSkip, st::mediaviewControlSize, height() - 2 * navSkip);
_leftNavOver = style::centerrect(_leftNav, overRect);
_leftNavIcon = style::centerrect(_leftNav, st::mediaviewLeft);
_rightNav = QRect(width() - st::mediaviewControlSize, navSkip, st::mediaviewControlSize, height() - 2 * navSkip);
_rightNavOver = style::centerrect(_rightNav, overRect);
_rightNavIcon = style::centerrect(_rightNav, st::mediaviewRight);
_saveMsg.moveTo((width() - _saveMsg.width()) / 2, (height() - _saveMsg.height()) / 2);
@ -1055,6 +1060,9 @@ void OverlayWidget::updateControls() {
updateThemePreviewGeometry();
const auto overRect = QRect(
QPoint(),
QSize(st::mediaviewIconOver, st::mediaviewIconOver));
_saveVisible = contentCanBeSaved();
_rotateVisible = !_themePreviewShown;
const auto navRect = [&](int i) {
@ -1064,10 +1072,13 @@ void OverlayWidget::updateControls() {
st::mediaviewIconSize.height());
};
_saveNav = navRect(_rotateVisible ? 3 : 2);
_saveNavOver = style::centerrect(_saveNav, overRect);
_saveNavIcon = style::centerrect(_saveNav, st::mediaviewSave);
_rotateNav = navRect(2);
_rotateNavOver = style::centerrect(_rotateNav, overRect);
_rotateNavIcon = style::centerrect(_rotateNav, st::mediaviewRotate);
_moreNav = navRect(1);
_moreNavOver = style::centerrect(_moreNav, overRect);
_moreNavIcon = style::centerrect(_moreNav, st::mediaviewMore);
const auto dNow = QDateTime::currentDateTime();
@ -1374,11 +1385,11 @@ bool OverlayWidget::updateControlsAnimation(crl::time now) {
_helper->setControlsOpacity(_controlsOpacity.current());
const auto content = finalContentRect();
const auto toUpdate = QRegion()
+ (_over == OverLeftNav ? _leftNav : _leftNavIcon)
+ (_over == OverRightNav ? _rightNav : _rightNavIcon)
+ _saveNavIcon
+ _rotateNavIcon
+ _moreNavIcon
+ (_over == OverLeftNav ? _leftNavOver : _leftNavIcon)
+ (_over == OverRightNav ? _rightNavOver : _rightNavIcon)
+ (_over == OverSave ? _saveNavOver : _saveNavIcon)
+ (_over == OverRotate ? _rotateNavOver : _rotateNavIcon)
+ (_over == OverMore ? _moreNavOver : _moreNavIcon)
+ _headerNav
+ _nameNav
+ _dateNav
@ -4159,7 +4170,7 @@ void OverlayWidget::paintControls(
struct Control {
OverState state = OverNone;
bool visible = false;
const QRect &outer;
const QRect &over;
const QRect &inner;
const style::icon &icon;
bool nonbright = false;
@ -4170,33 +4181,33 @@ void OverlayWidget::paintControls(
{
OverLeftNav,
_leftNavVisible,
_leftNav,
_leftNavOver,
_leftNavIcon,
st::mediaviewLeft,
true },
{
OverRightNav,
_rightNavVisible,
_rightNav,
_rightNavOver,
_rightNavIcon,
st::mediaviewRight,
true },
{
OverSave,
_saveVisible,
kEmpty,
_saveNavOver,
_saveNavIcon,
st::mediaviewSave },
{
OverRotate,
_rotateVisible,
kEmpty,
_rotateNavOver,
_rotateNavIcon,
st::mediaviewRotate },
{
OverMore,
true,
kEmpty,
_moreNavOver,
_moreNavIcon,
st::mediaviewMore },
};
@ -4211,7 +4222,7 @@ void OverlayWidget::paintControls(
const auto icon = controlOpacity(progress, control.nonbright);
renderer->paintControl(
control.state,
control.outer,
control.over,
bg * opacity,
control.inner,
icon * opacity,

View File

@ -495,9 +495,12 @@ private:
std::unique_ptr<Collage> _collage;
std::optional<WebPageCollage> _collageData;
QRect _leftNav, _leftNavIcon, _rightNav, _rightNavIcon;
QRect _leftNav, _leftNavOver, _leftNavIcon;
QRect _rightNav, _rightNavOver, _rightNavIcon;
QRect _headerNav, _nameNav, _dateNav;
QRect _rotateNav, _rotateNavIcon, _saveNav, _saveNavIcon, _moreNav, _moreNavIcon;
QRect _rotateNav, _rotateNavOver, _rotateNavIcon;
QRect _saveNav, _saveNavOver, _saveNavIcon;
QRect _moreNav, _moreNavOver, _moreNavIcon;
bool _leftNavVisible = false;
bool _rightNavVisible = false;
bool _saveVisible = false;

View File

@ -13,6 +13,24 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/abstract_button.h"
#include "styles/style_media_view.h"
namespace Media::View {
QColor OverBackgroundColor() {
auto c1 = st::mediaviewBg->c;
auto c2 = QColor(255, 255, 255);
const auto mix = [&](int a, int b) {
constexpr auto k1 = 0.15 * 0.85 / (1. - 0.85 * 0.85);
constexpr auto k2 = 0.15 / (1. - 0.85 * 0.85);
return int(a * k1 + b * k2);
};
return QColor(
mix(c1.red(), c2.red()),
mix(c1.green(), c2.green()),
mix(c1.blue(), c2.blue()));
}
} // namespace Media::View
namespace Platform {
namespace {
@ -120,8 +138,9 @@ object_ptr<Ui::AbstractButton> DefaultOverlayWidgetHelper::Buttons::create(
current = maximized ? &st::mediaviewTitleRestore : icon;
}
const auto alpha = progress * kOverBackgroundOpacity;
const auto ialpha = anim::interpolate(0, 255, alpha);
state->frame.fill(QColor(255, 255, 255, ialpha));
auto color = OverBackgroundColor();
color.setAlpha(anim::interpolate(0, 255, alpha));
state->frame.fill(color);
auto q = QPainter(&state->frame);
const auto normal = maximized

View File

@ -19,7 +19,8 @@ namespace Media::View {
inline constexpr auto kMaximizedIconOpacity = 0.6;
inline constexpr auto kNormalIconOpacity = 0.9;
inline constexpr auto kOverBackgroundOpacity = 0.15;
inline constexpr auto kOverBackgroundOpacity = 0.2775;
[[nodiscard]] QColor OverBackgroundColor();
} // namespace Media::View