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; mediaviewControlSize: 90px;
mediaviewIconSize: size(46px, 54px); mediaviewIconSize: size(46px, 54px);
mediaviewIconOver: 36px;
mediaviewWaitHide: 2000; mediaviewWaitHide: 2000;
mediaviewHideDuration: 1000; mediaviewHideDuration: 1000;

View file

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

View file

@ -60,8 +60,8 @@ private:
void paintControlsStart() override; void paintControlsStart() override;
void paintControl( void paintControl(
OverState control, OverState control,
QRect outer, QRect over,
float64 outerOpacity, float64 overOpacity,
QRect inner, QRect inner,
float64 innerOpacity, float64 innerOpacity,
const style::icon &icon) override; const style::icon &icon) override;
@ -135,7 +135,9 @@ private:
static constexpr auto kControlsCount = 5; static constexpr auto kControlsCount = 5;
[[nodiscard]] static Control ControlMeta(OverState control); [[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 _shadowTopTexture;
QRect _shadowBottomTexture; QRect _shadowBottomTexture;

View file

@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/painter.h" #include "ui/painter.h"
#include "media/view/media_view_pip.h" #include "media/view/media_view_pip.h"
#include "platform/platform_overlay_widget.h"
#include "styles/style_media_view.h" #include "styles/style_media_view.h"
namespace Media::View { namespace Media::View {
@ -173,22 +174,20 @@ void OverlayWidget::RendererSW::paintControlsStart() {
void OverlayWidget::RendererSW::paintControl( void OverlayWidget::RendererSW::paintControl(
OverState control, OverState control,
QRect outer, QRect over,
float64 outerOpacity, float64 overOpacity,
QRect inner, QRect inner,
float64 innerOpacity, float64 innerOpacity,
const style::icon &icon) { const style::icon &icon) {
if (!outer.isEmpty() && !outer.intersects(_clipOuter)) { if (!over.isEmpty() && !over.intersects(_clipOuter)) {
return; return;
} }
if (!outer.isEmpty() && outerOpacity > 0) { if (!over.isEmpty() && overOpacity > 0) {
_p->setOpacity(outerOpacity); if (_overControlImage.isNull()) {
for (const auto &rect : *_clip) { validateOverControlImage();
const auto fill = outer.intersected(rect);
if (!fill.isEmpty()) {
_p->fillRect(fill, st::mediaviewControlBg);
}
} }
_p->setOpacity(overOpacity);
_p->drawImage(over.topLeft(), _overControlImage);
} }
if (inner.intersects(_clipOuter)) { if (inner.intersects(_clipOuter)) {
_p->setOpacity(innerOpacity); _p->setOpacity(innerOpacity);
@ -220,4 +219,22 @@ void OverlayWidget::RendererSW::paintRoundedCorners(int radius) {
// The RpWindow rounding overlay will do the job. // 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 } // namespace Media::View

View file

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

View file

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

View file

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

View file

@ -495,9 +495,12 @@ private:
std::unique_ptr<Collage> _collage; std::unique_ptr<Collage> _collage;
std::optional<WebPageCollage> _collageData; std::optional<WebPageCollage> _collageData;
QRect _leftNav, _leftNavIcon, _rightNav, _rightNavIcon; QRect _leftNav, _leftNavOver, _leftNavIcon;
QRect _rightNav, _rightNavOver, _rightNavIcon;
QRect _headerNav, _nameNav, _dateNav; 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 _leftNavVisible = false;
bool _rightNavVisible = false; bool _rightNavVisible = false;
bool _saveVisible = false; bool _saveVisible = false;

View file

@ -13,6 +13,24 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/abstract_button.h" #include "ui/abstract_button.h"
#include "styles/style_media_view.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 Platform {
namespace { namespace {
@ -120,8 +138,9 @@ object_ptr<Ui::AbstractButton> DefaultOverlayWidgetHelper::Buttons::create(
current = maximized ? &st::mediaviewTitleRestore : icon; current = maximized ? &st::mediaviewTitleRestore : icon;
} }
const auto alpha = progress * kOverBackgroundOpacity; const auto alpha = progress * kOverBackgroundOpacity;
const auto ialpha = anim::interpolate(0, 255, alpha); auto color = OverBackgroundColor();
state->frame.fill(QColor(255, 255, 255, ialpha)); color.setAlpha(anim::interpolate(0, 255, alpha));
state->frame.fill(color);
auto q = QPainter(&state->frame); auto q = QPainter(&state->frame);
const auto normal = maximized const auto normal = maximized

View file

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