Show shadow below controls in media viewer.
Before Width: | Height: | Size: 513 B After Width: | Height: | Size: 366 B |
Before Width: | Height: | Size: 1 KiB After Width: | Height: | Size: 617 B |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 957 B |
Before Width: | Height: | Size: 296 B After Width: | Height: | Size: 318 B |
Before Width: | Height: | Size: 497 B After Width: | Height: | Size: 591 B |
Before Width: | Height: | Size: 729 B After Width: | Height: | Size: 880 B |
BIN
Telegram/Resources/icons/mediaview/next_shadow.png
Normal file
After Width: | Height: | Size: 435 B |
BIN
Telegram/Resources/icons/mediaview/next_shadow@2x.png
Normal file
After Width: | Height: | Size: 769 B |
BIN
Telegram/Resources/icons/mediaview/next_shadow@3x.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 912 B After Width: | Height: | Size: 680 B |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 210 B After Width: | Height: | Size: 215 B |
Before Width: | Height: | Size: 304 B After Width: | Height: | Size: 312 B |
Before Width: | Height: | Size: 461 B After Width: | Height: | Size: 425 B |
Before Width: | Height: | Size: 300 B After Width: | Height: | Size: 338 B |
Before Width: | Height: | Size: 388 B After Width: | Height: | Size: 485 B |
Before Width: | Height: | Size: 606 B After Width: | Height: | Size: 695 B |
Before Width: | Height: | Size: 360 B After Width: | Height: | Size: 358 B |
Before Width: | Height: | Size: 554 B After Width: | Height: | Size: 572 B |
Before Width: | Height: | Size: 722 B After Width: | Height: | Size: 808 B |
|
@ -101,11 +101,17 @@ mediaviewVolumeToggle: IconButton(mediaviewControlsButton) {
|
||||||
}
|
}
|
||||||
mediaviewVolumeSkip: 4px;
|
mediaviewVolumeSkip: 4px;
|
||||||
|
|
||||||
mediaviewLeft: icon {{ "mediaview/next-flip_horizontal", mediaviewControlFg }};
|
mediaviewLeft: icon {
|
||||||
mediaviewRight: icon {{ "mediaview/next", mediaviewControlFg }};
|
{ "mediaview/next_shadow-flip_horizontal", windowShadowFg }
|
||||||
|
{ "mediaview/next-flip_horizontal", mediaviewControlFg }
|
||||||
|
};
|
||||||
|
mediaviewRight: icon {
|
||||||
|
{ "mediaview/next_shadow", windowShadowFg }
|
||||||
|
{ "mediaview/next", mediaviewControlFg }
|
||||||
|
};
|
||||||
mediaviewSave: icon {{ "mediaview/download", mediaviewControlFg }};
|
mediaviewSave: icon {{ "mediaview/download", mediaviewControlFg }};
|
||||||
mediaviewRotate: icon {{ "mediaview/rotate", mediaviewControlFg }};
|
mediaviewRotate: icon {{ "mediaview/rotate", mediaviewControlFg }};
|
||||||
mediaviewMore: icon {{ "mediaview/more", mediaviewControlFg }};
|
mediaviewMore: icon {{ "title_menu_dots", mediaviewControlFg }};
|
||||||
|
|
||||||
mediaviewFileRed: icon {
|
mediaviewFileRed: icon {
|
||||||
{ size(25px, 25px), mediaviewFileBg },
|
{ size(25px, 25px), mediaviewFileBg },
|
||||||
|
@ -232,15 +238,13 @@ mediaviewThickFont: semiboldFont;
|
||||||
mediaviewFont: normalFont;
|
mediaviewFont: normalFont;
|
||||||
mediaviewTextStyle: defaultTextStyle;
|
mediaviewTextStyle: defaultTextStyle;
|
||||||
|
|
||||||
mediaviewTextLeft: 16px;
|
mediaviewTextLeft: 14px;
|
||||||
mediaviewTextSkip: 10px;
|
mediaviewTextSkip: 10px;
|
||||||
mediaviewHeaderTop: 48px;
|
mediaviewHeaderTop: 47px;
|
||||||
mediaviewTextTop: 24px;
|
mediaviewTextTop: 26px;
|
||||||
|
|
||||||
mediaviewIconOpacity: 0.45;
|
|
||||||
mediaviewIconOverOpacity: 1.;
|
|
||||||
mediaviewControlSize: 90px;
|
mediaviewControlSize: 90px;
|
||||||
mediaviewIconSize: size(60px, 56px);
|
mediaviewIconSize: size(46px, 54px);
|
||||||
|
|
||||||
mediaviewWaitHide: 2000;
|
mediaviewWaitHide: 2000;
|
||||||
mediaviewHideDuration: 1000;
|
mediaviewHideDuration: 1000;
|
||||||
|
@ -250,9 +254,9 @@ mediaviewFadeDuration: 150;
|
||||||
mediaviewDeltaFromLastAction: 5px;
|
mediaviewDeltaFromLastAction: 5px;
|
||||||
mediaviewSwipeDistance: 80px;
|
mediaviewSwipeDistance: 80px;
|
||||||
|
|
||||||
mediaviewCaptionPadding: margins(18px, 10px, 18px, 10px);
|
mediaviewCaptionPadding: margins(11px, 6px, 11px, 6px);
|
||||||
mediaviewCaptionMargin: size(11px, 11px);
|
mediaviewCaptionMargin: size(11px, 11px);
|
||||||
mediaviewCaptionRadius: 2px;
|
mediaviewCaptionRadius: 6px;
|
||||||
|
|
||||||
mediaviewGroupPadding: margins(0px, 14px, 0px, 14px);
|
mediaviewGroupPadding: margins(0px, 14px, 0px, 14px);
|
||||||
mediaviewGroupHeight: 80px;
|
mediaviewGroupHeight: 80px;
|
||||||
|
@ -317,6 +321,8 @@ mediaviewTitle: WindowTitle(defaultWindowTitle) {
|
||||||
closeIconActive: mediaviewTitleClose;
|
closeIconActive: mediaviewTitleClose;
|
||||||
closeIconActiveOver: mediaviewTitleClose;
|
closeIconActiveOver: mediaviewTitleClose;
|
||||||
}
|
}
|
||||||
|
mediaviewShadowTop: icon{{ "mediaview/shadow_top", windowShadowFg }};
|
||||||
|
mediaviewShadowBottom: icon{{ "mediaview/shadow_bottom", windowShadowFg }};
|
||||||
|
|
||||||
themePreviewSize: size(903px, 584px);
|
themePreviewSize: size(903px, 584px);
|
||||||
themePreviewBg: windowBg;
|
themePreviewBg: windowBg;
|
||||||
|
|
|
@ -29,6 +29,31 @@ 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 = 2 * 4 + 4 * 4;
|
||||||
|
|
||||||
|
[[nodiscard]] ShaderPart FragmentApplyControlsFade() {
|
||||||
|
return {
|
||||||
|
.header = R"(
|
||||||
|
uniform sampler2D f_texture;
|
||||||
|
uniform vec4 shadowTopRect;
|
||||||
|
uniform vec2 shadowBottomAndOpacity;
|
||||||
|
)",
|
||||||
|
.body = R"(
|
||||||
|
float topHeight = shadowTopRect.w;
|
||||||
|
float bottomHeight = shadowBottomAndOpacity.x;
|
||||||
|
float opacity = shadowBottomAndOpacity.y;
|
||||||
|
float viewportHeight = shadowTopRect.y + topHeight;
|
||||||
|
float fullHeight = topHeight + bottomHeight;
|
||||||
|
float topY = min(
|
||||||
|
(viewportHeight - gl_FragCoord.y) / fullHeight,
|
||||||
|
topHeight / fullHeight);
|
||||||
|
float topX = (gl_FragCoord.x - shadowTopRect.x) / shadowTopRect.z;
|
||||||
|
vec4 fadeTop = texture2D(f_texture, vec2(topX, topY)) * opacity;
|
||||||
|
float bottomY = max(fullHeight - gl_FragCoord.y, topHeight) / fullHeight;
|
||||||
|
vec4 fadeBottom = texture2D(f_texture, vec2(0.5, bottomY)) * opacity;
|
||||||
|
result.rgb = result.rgb * (1. - fadeTop.a) * (1. - fadeBottom.a);
|
||||||
|
)",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] ShaderPart FragmentPlaceOnTransparentBackground() {
|
[[nodiscard]] ShaderPart FragmentPlaceOnTransparentBackground() {
|
||||||
return {
|
return {
|
||||||
.header = R"(
|
.header = R"(
|
||||||
|
@ -77,6 +102,7 @@ OverlayWidget::RendererGL::RendererGL(not_null<OverlayWidget*> owner)
|
||||||
: _owner(owner) {
|
: _owner(owner) {
|
||||||
style::PaletteChanged(
|
style::PaletteChanged(
|
||||||
) | rpl::start_with_next([=] {
|
) | rpl::start_with_next([=] {
|
||||||
|
_controlsFadeImage.invalidate();
|
||||||
_radialImage.invalidate();
|
_radialImage.invalidate();
|
||||||
_documentBubbleImage.invalidate();
|
_documentBubbleImage.invalidate();
|
||||||
_themePreviewImage.invalidate();
|
_themePreviewImage.invalidate();
|
||||||
|
@ -118,6 +144,15 @@ void OverlayWidget::RendererGL::init(
|
||||||
FragmentSampleARGB32Texture(),
|
FragmentSampleARGB32Texture(),
|
||||||
})).vertex;
|
})).vertex;
|
||||||
|
|
||||||
|
_staticContentProgram.emplace();
|
||||||
|
LinkProgram(
|
||||||
|
&*_staticContentProgram,
|
||||||
|
_texturedVertexShader,
|
||||||
|
FragmentShader({
|
||||||
|
FragmentSampleARGB32Texture(),
|
||||||
|
FragmentApplyControlsFade()
|
||||||
|
}));
|
||||||
|
|
||||||
_withTransparencyProgram.emplace();
|
_withTransparencyProgram.emplace();
|
||||||
LinkProgram(
|
LinkProgram(
|
||||||
&*_withTransparencyProgram,
|
&*_withTransparencyProgram,
|
||||||
|
@ -125,6 +160,7 @@ void OverlayWidget::RendererGL::init(
|
||||||
FragmentShader({
|
FragmentShader({
|
||||||
FragmentSampleARGB32Texture(),
|
FragmentSampleARGB32Texture(),
|
||||||
FragmentPlaceOnTransparentBackground(),
|
FragmentPlaceOnTransparentBackground(),
|
||||||
|
FragmentApplyControlsFade()
|
||||||
}));
|
}));
|
||||||
|
|
||||||
_yuv420Program.emplace();
|
_yuv420Program.emplace();
|
||||||
|
@ -133,6 +169,7 @@ void OverlayWidget::RendererGL::init(
|
||||||
_texturedVertexShader,
|
_texturedVertexShader,
|
||||||
FragmentShader({
|
FragmentShader({
|
||||||
FragmentSampleYUV420Texture(),
|
FragmentSampleYUV420Texture(),
|
||||||
|
FragmentApplyControlsFade()
|
||||||
}));
|
}));
|
||||||
|
|
||||||
_nv12Program.emplace();
|
_nv12Program.emplace();
|
||||||
|
@ -141,6 +178,7 @@ void OverlayWidget::RendererGL::init(
|
||||||
_texturedVertexShader,
|
_texturedVertexShader,
|
||||||
FragmentShader({
|
FragmentShader({
|
||||||
FragmentSampleNV12Texture(),
|
FragmentSampleNV12Texture(),
|
||||||
|
FragmentApplyControlsFade()
|
||||||
}));
|
}));
|
||||||
|
|
||||||
_fillProgram.emplace();
|
_fillProgram.emplace();
|
||||||
|
@ -195,6 +233,7 @@ void OverlayWidget::RendererGL::paint(
|
||||||
if (_factor != factor) {
|
if (_factor != factor) {
|
||||||
_factor = factor;
|
_factor = factor;
|
||||||
_controlsImage.invalidate();
|
_controlsImage.invalidate();
|
||||||
|
_controlsFadeImage.invalidate();
|
||||||
}
|
}
|
||||||
_blendingEnabled = false;
|
_blendingEnabled = false;
|
||||||
_viewport = widget->size();
|
_viewport = widget->size();
|
||||||
|
@ -283,7 +322,12 @@ void OverlayWidget::RendererGL::paintTransformedVideoFrame(
|
||||||
}
|
}
|
||||||
_chromaNV12 = nv12;
|
_chromaNV12 = nv12;
|
||||||
}
|
}
|
||||||
if (!nv12) {
|
|
||||||
|
validateControlsFade();
|
||||||
|
if (nv12) {
|
||||||
|
_f->glActiveTexture(GL_TEXTURE2);
|
||||||
|
_controlsFadeImage.bind(*_f);
|
||||||
|
} else {
|
||||||
_f->glActiveTexture(GL_TEXTURE2);
|
_f->glActiveTexture(GL_TEXTURE2);
|
||||||
_textures.bind(*_f, 3);
|
_textures.bind(*_f, 3);
|
||||||
if (upload) {
|
if (upload) {
|
||||||
|
@ -297,6 +341,9 @@ void OverlayWidget::RendererGL::paintTransformedVideoFrame(
|
||||||
_chromaSize = yuv->chromaSize;
|
_chromaSize = yuv->chromaSize;
|
||||||
_f->glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
|
_f->glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_f->glActiveTexture(GL_TEXTURE3);
|
||||||
|
_controlsFadeImage.bind(*_f);
|
||||||
}
|
}
|
||||||
program->setUniformValue("y_texture", GLint(0));
|
program->setUniformValue("y_texture", GLint(0));
|
||||||
if (nv12) {
|
if (nv12) {
|
||||||
|
@ -305,6 +352,7 @@ void OverlayWidget::RendererGL::paintTransformedVideoFrame(
|
||||||
program->setUniformValue("u_texture", GLint(1));
|
program->setUniformValue("u_texture", GLint(1));
|
||||||
program->setUniformValue("v_texture", GLint(2));
|
program->setUniformValue("v_texture", GLint(2));
|
||||||
}
|
}
|
||||||
|
program->setUniformValue("f_texture", GLint(nv12 ? 2 : 3));
|
||||||
|
|
||||||
toggleBlending(false);
|
toggleBlending(false);
|
||||||
paintTransformedContent(program, geometry);
|
paintTransformedContent(program, geometry);
|
||||||
|
@ -325,7 +373,7 @@ void OverlayWidget::RendererGL::paintTransformedStaticContent(
|
||||||
|
|
||||||
auto &program = fillTransparentBackground
|
auto &program = fillTransparentBackground
|
||||||
? _withTransparencyProgram
|
? _withTransparencyProgram
|
||||||
: _imageProgram;
|
: _staticContentProgram;
|
||||||
program->bind();
|
program->bind();
|
||||||
if (fillTransparentBackground) {
|
if (fillTransparentBackground) {
|
||||||
program->setUniformValue(
|
program->setUniformValue(
|
||||||
|
@ -369,7 +417,13 @@ void OverlayWidget::RendererGL::paintTransformedStaticContent(
|
||||||
_rgbaSize = image.size();
|
_rgbaSize = image.size();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
validateControlsFade();
|
||||||
|
_f->glActiveTexture(GL_TEXTURE1);
|
||||||
|
_controlsFadeImage.bind(*_f);
|
||||||
|
|
||||||
program->setUniformValue("s_texture", GLint(0));
|
program->setUniformValue("s_texture", GLint(0));
|
||||||
|
program->setUniformValue("f_texture", GLint(1));
|
||||||
|
|
||||||
toggleBlending(semiTransparent && !fillTransparentBackground);
|
toggleBlending(semiTransparent && !fillTransparentBackground);
|
||||||
paintTransformedContent(&*program, geometry);
|
paintTransformedContent(&*program, geometry);
|
||||||
|
@ -412,6 +466,15 @@ void OverlayWidget::RendererGL::paintTransformedContent(
|
||||||
_contentBuffer->write(0, coords, sizeof(coords));
|
_contentBuffer->write(0, coords, sizeof(coords));
|
||||||
|
|
||||||
program->setUniformValue("viewport", _uniformViewport);
|
program->setUniformValue("viewport", _uniformViewport);
|
||||||
|
const auto &top = st::mediaviewShadowTop.size();
|
||||||
|
program->setUniformValue(
|
||||||
|
"shadowTopRect",
|
||||||
|
Uniform(transformRect(
|
||||||
|
QRect(QPoint(_viewport.width() - top.width(), 0), top))));
|
||||||
|
const auto &bottom = st::mediaviewShadowBottom;
|
||||||
|
program->setUniformValue(
|
||||||
|
"shadowBottomAndOpacity",
|
||||||
|
QVector2D(bottom.height() * _factor, geometry.controlsOpacity));
|
||||||
|
|
||||||
FillTexturedRectangle(*_f, &*program);
|
FillTexturedRectangle(*_f, &*program);
|
||||||
}
|
}
|
||||||
|
@ -607,6 +670,36 @@ void OverlayWidget::RendererGL::invalidateControls() {
|
||||||
ranges::fill(_controlsTextures, QRect());
|
ranges::fill(_controlsTextures, QRect());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OverlayWidget::RendererGL::validateControlsFade() {
|
||||||
|
if (!_controlsFadeImage.image().isNull()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto width = st::mediaviewShadowTop.width();
|
||||||
|
const auto bottomTop = st::mediaviewShadowTop.height();
|
||||||
|
const auto height = bottomTop + st::mediaviewShadowBottom.height();
|
||||||
|
|
||||||
|
auto image = QImage(
|
||||||
|
QSize(width, height) * _factor,
|
||||||
|
QImage::Format_ARGB32_Premultiplied);
|
||||||
|
image.fill(Qt::transparent);
|
||||||
|
image.setDevicePixelRatio(_factor);
|
||||||
|
|
||||||
|
auto p = QPainter(&image);
|
||||||
|
st::mediaviewShadowTop.paint(p, 0, 0, width);
|
||||||
|
st::mediaviewShadowBottom.fill(
|
||||||
|
p,
|
||||||
|
QRect(0, bottomTop, width, st::mediaviewShadowBottom.height()));
|
||||||
|
p.end();
|
||||||
|
|
||||||
|
_controlsFadeImage.setImage(std::move(image));
|
||||||
|
_shadowTopTexture = QRect(
|
||||||
|
QPoint(),
|
||||||
|
QSize(width, st::mediaviewShadowTop.height()) * _factor);
|
||||||
|
_shadowBottomTexture = QRect(
|
||||||
|
QPoint(0, bottomTop) * _factor,
|
||||||
|
QSize(width, st::mediaviewShadowBottom.height()) * _factor);
|
||||||
|
}
|
||||||
|
|
||||||
void OverlayWidget::RendererGL::paintFooter(QRect outer, float64 opacity) {
|
void OverlayWidget::RendererGL::paintFooter(QRect outer, float64 opacity) {
|
||||||
paintUsingRaster(_footerImage, outer, [&](Painter &&p) {
|
paintUsingRaster(_footerImage, outer, [&](Painter &&p) {
|
||||||
const auto newOuter = QRect(QPoint(), outer.size());
|
const auto newOuter = QRect(QPoint(), outer.size());
|
||||||
|
@ -699,25 +792,25 @@ void OverlayWidget::RendererGL::paintRoundedCorners(int radius) {
|
||||||
|
|
||||||
_f->glDisableVertexAttribArray(position);
|
_f->glDisableVertexAttribArray(position);
|
||||||
}
|
}
|
||||||
|
//
|
||||||
void OverlayWidget::RendererGL::invalidate() {
|
//void OverlayWidget::RendererGL::invalidate() {
|
||||||
_trackFrameIndex = -1;
|
// _trackFrameIndex = -1;
|
||||||
_streamedIndex = -1;
|
// _streamedIndex = -1;
|
||||||
const auto images = {
|
// const auto images = {
|
||||||
&_radialImage,
|
// &_radialImage,
|
||||||
&_documentBubbleImage,
|
// &_documentBubbleImage,
|
||||||
&_themePreviewImage,
|
// &_themePreviewImage,
|
||||||
&_saveMsgImage,
|
// &_saveMsgImage,
|
||||||
&_footerImage,
|
// &_footerImage,
|
||||||
&_captionImage,
|
// &_captionImage,
|
||||||
&_groupThumbsImage,
|
// &_groupThumbsImage,
|
||||||
&_controlsImage,
|
// &_controlsImage,
|
||||||
};
|
// };
|
||||||
for (const auto image : images) {
|
// for (const auto image : images) {
|
||||||
image->setImage(QImage());
|
// image->setImage(QImage());
|
||||||
}
|
// }
|
||||||
invalidateControls();
|
// invalidateControls();
|
||||||
}
|
//}
|
||||||
|
|
||||||
void OverlayWidget::RendererGL::paintUsingRaster(
|
void OverlayWidget::RendererGL::paintUsingRaster(
|
||||||
Ui::GL::Image &image,
|
Ui::GL::Image &image,
|
||||||
|
|
|
@ -70,7 +70,7 @@ 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 invalidate();
|
//void invalidate();
|
||||||
|
|
||||||
void paintUsingRaster(
|
void paintUsingRaster(
|
||||||
Ui::GL::Image &image,
|
Ui::GL::Image &image,
|
||||||
|
@ -79,6 +79,7 @@ private:
|
||||||
int bufferOffset,
|
int bufferOffset,
|
||||||
bool transparent = false);
|
bool transparent = false);
|
||||||
|
|
||||||
|
void validateControlsFade();
|
||||||
void validateControls();
|
void validateControls();
|
||||||
void invalidateControls();
|
void invalidateControls();
|
||||||
void toggleBlending(bool enabled);
|
void toggleBlending(bool enabled);
|
||||||
|
@ -105,6 +106,7 @@ private:
|
||||||
|
|
||||||
std::optional<QOpenGLBuffer> _contentBuffer;
|
std::optional<QOpenGLBuffer> _contentBuffer;
|
||||||
std::optional<QOpenGLShaderProgram> _imageProgram;
|
std::optional<QOpenGLShaderProgram> _imageProgram;
|
||||||
|
std::optional<QOpenGLShaderProgram> _staticContentProgram;
|
||||||
QOpenGLShader *_texturedVertexShader = nullptr;
|
QOpenGLShader *_texturedVertexShader = nullptr;
|
||||||
std::optional<QOpenGLShaderProgram> _withTransparencyProgram;
|
std::optional<QOpenGLShaderProgram> _withTransparencyProgram;
|
||||||
std::optional<QOpenGLShaderProgram> _yuv420Program;
|
std::optional<QOpenGLShaderProgram> _yuv420Program;
|
||||||
|
@ -121,6 +123,7 @@ private:
|
||||||
int _streamedIndex = 0;
|
int _streamedIndex = 0;
|
||||||
bool _chromaNV12 = false;
|
bool _chromaNV12 = false;
|
||||||
|
|
||||||
|
Ui::GL::Image _controlsFadeImage;
|
||||||
Ui::GL::Image _radialImage;
|
Ui::GL::Image _radialImage;
|
||||||
Ui::GL::Image _documentBubbleImage;
|
Ui::GL::Image _documentBubbleImage;
|
||||||
Ui::GL::Image _themePreviewImage;
|
Ui::GL::Image _themePreviewImage;
|
||||||
|
@ -134,6 +137,9 @@ private:
|
||||||
[[nodiscard]] static Control ControlMeta(OverState control);
|
[[nodiscard]] static Control ControlMeta(OverState control);
|
||||||
std::array<QRect, kControlsCount> _controlsTextures;
|
std::array<QRect, kControlsCount> _controlsTextures;
|
||||||
|
|
||||||
|
QRect _shadowTopTexture;
|
||||||
|
QRect _shadowBottomTexture;
|
||||||
|
|
||||||
bool _blendingEnabled = false;
|
bool _blendingEnabled = false;
|
||||||
|
|
||||||
rpl::lifetime _lifetime;
|
rpl::lifetime _lifetime;
|
||||||
|
|
|
@ -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 "styles/style_media_view.h"
|
||||||
|
|
||||||
namespace Media::View {
|
namespace Media::View {
|
||||||
|
|
||||||
|
@ -73,6 +74,7 @@ void OverlayWidget::RendererSW::paintTransformedVideoFrame(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
paintTransformedImage(_owner->videoFrame(), rect, rotation);
|
paintTransformedImage(_owner->videoFrame(), rect, rotation);
|
||||||
|
paintControlsFade(rect, geometry.controlsOpacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OverlayWidget::RendererSW::paintTransformedStaticContent(
|
void OverlayWidget::RendererSW::paintTransformedStaticContent(
|
||||||
|
@ -89,10 +91,34 @@ void OverlayWidget::RendererSW::paintTransformedStaticContent(
|
||||||
if (fillTransparentBackground) {
|
if (fillTransparentBackground) {
|
||||||
_p->fillRect(rect, _transparentBrush);
|
_p->fillRect(rect, _transparentBrush);
|
||||||
}
|
}
|
||||||
if (image.isNull()) {
|
if (!image.isNull()) {
|
||||||
return;
|
paintTransformedImage(image, rect, rotation);
|
||||||
}
|
}
|
||||||
paintTransformedImage(image, rect, rotation);
|
paintControlsFade(rect, geometry.controlsOpacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OverlayWidget::RendererSW::paintControlsFade(
|
||||||
|
QRect geometry,
|
||||||
|
float64 opacity) {
|
||||||
|
_p->setOpacity(opacity);
|
||||||
|
_p->setClipRect(geometry);
|
||||||
|
const auto width = _owner->width();
|
||||||
|
const auto &top = st::mediaviewShadowTop;
|
||||||
|
const auto topShadow = QRect(
|
||||||
|
QPoint(width - top.width(), 0),
|
||||||
|
top.size());
|
||||||
|
if (topShadow.intersected(geometry).intersects(_clipOuter)) {
|
||||||
|
top.paint(*_p, topShadow.topLeft(), width);
|
||||||
|
}
|
||||||
|
const auto &bottom = st::mediaviewShadowBottom;
|
||||||
|
const auto bottomShadow = QRect(
|
||||||
|
QPoint(0, _owner->height() - bottom.height()),
|
||||||
|
QSize(width, bottom.height()));
|
||||||
|
if (bottomShadow.intersected(geometry).intersects(_clipOuter)) {
|
||||||
|
bottom.fill(*_p, bottomShadow);
|
||||||
|
}
|
||||||
|
_p->setClipping(false);
|
||||||
|
_p->setOpacity(1.);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OverlayWidget::RendererSW::paintTransformedImage(
|
void OverlayWidget::RendererSW::paintTransformedImage(
|
||||||
|
|
|
@ -32,6 +32,7 @@ private:
|
||||||
const QImage &image,
|
const QImage &image,
|
||||||
QRect rect,
|
QRect rect,
|
||||||
int rotation);
|
int rotation);
|
||||||
|
void paintControlsFade(QRect geometry, float64 opacity);
|
||||||
void paintRadialLoading(
|
void paintRadialLoading(
|
||||||
QRect inner,
|
QRect inner,
|
||||||
bool radial,
|
bool radial,
|
||||||
|
|
|
@ -790,6 +790,11 @@ void OverlayWidget::updateControlsGeometry() {
|
||||||
_saveMsg.moveTo((width() - _saveMsg.width()) / 2, (height() - _saveMsg.height()) / 2);
|
_saveMsg.moveTo((width() - _saveMsg.width()) / 2, (height() - _saveMsg.height()) / 2);
|
||||||
_photoRadialRect = QRect(QPoint((width() - st::radialSize.width()) / 2, (height() - st::radialSize.height()) / 2), st::radialSize);
|
_photoRadialRect = QRect(QPoint((width() - st::radialSize.width()) / 2, (height() - st::radialSize.height()) / 2), st::radialSize);
|
||||||
|
|
||||||
|
const auto bottom = st::mediaviewShadowBottom.height();
|
||||||
|
const auto top = st::mediaviewShadowTop.size();
|
||||||
|
_bottomShadowRect = QRect(0, height() - bottom, width(), bottom);
|
||||||
|
_topShadowRect = QRect(QPoint(width() - top.width(), 0), top);
|
||||||
|
|
||||||
updateControls();
|
updateControls();
|
||||||
resizeContentByScreenSize();
|
resizeContentByScreenSize();
|
||||||
update();
|
update();
|
||||||
|
@ -1367,6 +1372,7 @@ bool OverlayWidget::updateControlsAnimation(crl::time now) {
|
||||||
_controlsOpacity.update(dt, anim::linear);
|
_controlsOpacity.update(dt, anim::linear);
|
||||||
}
|
}
|
||||||
_helper->setControlsOpacity(_controlsOpacity.current());
|
_helper->setControlsOpacity(_controlsOpacity.current());
|
||||||
|
const auto content = finalContentRect();
|
||||||
const auto toUpdate = QRegion()
|
const auto toUpdate = QRegion()
|
||||||
+ (_over == OverLeftNav ? _leftNav : _leftNavIcon)
|
+ (_over == OverLeftNav ? _leftNav : _leftNavIcon)
|
||||||
+ (_over == OverRightNav ? _rightNav : _rightNavIcon)
|
+ (_over == OverRightNav ? _rightNav : _rightNavIcon)
|
||||||
|
@ -1377,7 +1383,9 @@ bool OverlayWidget::updateControlsAnimation(crl::time now) {
|
||||||
+ _nameNav
|
+ _nameNav
|
||||||
+ _dateNav
|
+ _dateNav
|
||||||
+ _captionRect.marginsAdded(st::mediaviewCaptionPadding)
|
+ _captionRect.marginsAdded(st::mediaviewCaptionPadding)
|
||||||
+ _groupThumbsRect;
|
+ _groupThumbsRect
|
||||||
|
+ content.intersected(_bottomShadowRect)
|
||||||
|
+ content.intersected(_topShadowRect);
|
||||||
update(toUpdate);
|
update(toUpdate);
|
||||||
return (dt < 1);
|
return (dt < 1);
|
||||||
}
|
}
|
||||||
|
@ -1407,6 +1415,7 @@ QRect OverlayWidget::finalContentRect() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
OverlayWidget::ContentGeometry OverlayWidget::contentGeometry() const {
|
OverlayWidget::ContentGeometry OverlayWidget::contentGeometry() const {
|
||||||
|
const auto controlsOpacity = _controlsOpacity.current();
|
||||||
const auto toRotation = qreal(finalContentRotation());
|
const auto toRotation = qreal(finalContentRotation());
|
||||||
const auto toRectRotated = QRectF(finalContentRect());
|
const auto toRectRotated = QRectF(finalContentRect());
|
||||||
const auto toRectCenter = toRectRotated.center();
|
const auto toRectCenter = toRectRotated.center();
|
||||||
|
@ -1418,7 +1427,7 @@ OverlayWidget::ContentGeometry OverlayWidget::contentGeometry() const {
|
||||||
toRectRotated.width())
|
toRectRotated.width())
|
||||||
: toRectRotated;
|
: toRectRotated;
|
||||||
if (!_geometryAnimation.animating()) {
|
if (!_geometryAnimation.animating()) {
|
||||||
return { toRect, toRotation };
|
return { toRect, toRotation, controlsOpacity };
|
||||||
}
|
}
|
||||||
const auto fromRect = _oldGeometry.rect;
|
const auto fromRect = _oldGeometry.rect;
|
||||||
const auto fromRotation = _oldGeometry.rotation;
|
const auto fromRotation = _oldGeometry.rotation;
|
||||||
|
@ -1441,7 +1450,7 @@ OverlayWidget::ContentGeometry OverlayWidget::contentGeometry() const {
|
||||||
fromRect.width() + (toRect.width() - fromRect.width()) * progress,
|
fromRect.width() + (toRect.width() - fromRect.width()) * progress,
|
||||||
fromRect.height() + (toRect.height() - fromRect.height()) * progress
|
fromRect.height() + (toRect.height() - fromRect.height()) * progress
|
||||||
);
|
);
|
||||||
return { useRect, useRotation };
|
return { useRect, useRotation, controlsOpacity };
|
||||||
}
|
}
|
||||||
|
|
||||||
void OverlayWidget::updateContentRect() {
|
void OverlayWidget::updateContentRect() {
|
||||||
|
@ -4152,6 +4161,7 @@ void OverlayWidget::paintControls(
|
||||||
const QRect &outer;
|
const QRect &outer;
|
||||||
const QRect &inner;
|
const QRect &inner;
|
||||||
const style::icon &icon;
|
const style::icon &icon;
|
||||||
|
bool nonbright = false;
|
||||||
};
|
};
|
||||||
const QRect kEmpty;
|
const QRect kEmpty;
|
||||||
// When adding / removing controls please update RendererGL.
|
// When adding / removing controls please update RendererGL.
|
||||||
|
@ -4161,13 +4171,15 @@ void OverlayWidget::paintControls(
|
||||||
_leftNavVisible,
|
_leftNavVisible,
|
||||||
_leftNav,
|
_leftNav,
|
||||||
_leftNavIcon,
|
_leftNavIcon,
|
||||||
st::mediaviewLeft },
|
st::mediaviewLeft,
|
||||||
|
true },
|
||||||
{
|
{
|
||||||
OverRightNav,
|
OverRightNav,
|
||||||
_rightNavVisible,
|
_rightNavVisible,
|
||||||
_rightNav,
|
_rightNav,
|
||||||
_rightNavIcon,
|
_rightNavIcon,
|
||||||
st::mediaviewRight },
|
st::mediaviewRight,
|
||||||
|
true },
|
||||||
{
|
{
|
||||||
OverSave,
|
OverSave,
|
||||||
_saveVisible,
|
_saveVisible,
|
||||||
|
@ -4193,9 +4205,9 @@ void OverlayWidget::paintControls(
|
||||||
if (!control.visible) {
|
if (!control.visible) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const auto bg = overLevel(control.state);
|
const auto progress = overLevel(control.state);
|
||||||
const auto icon = bg * st::mediaviewIconOverOpacity
|
const auto bg = progress;
|
||||||
+ (1 - bg) * st::mediaviewIconOpacity;
|
const auto icon = controlOpacity(progress, control.nonbright);
|
||||||
renderer->paintControl(
|
renderer->paintControl(
|
||||||
control.state,
|
control.state,
|
||||||
control.outer,
|
control.outer,
|
||||||
|
@ -4206,6 +4218,15 @@ void OverlayWidget::paintControls(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float64 OverlayWidget::controlOpacity(
|
||||||
|
float64 progress,
|
||||||
|
bool nonbright) const {
|
||||||
|
const auto normal = _windowed
|
||||||
|
? kNormalIconOpacity
|
||||||
|
: kMaximizedIconOpacity;
|
||||||
|
return progress + (1. - progress) * normal;
|
||||||
|
}
|
||||||
|
|
||||||
void OverlayWidget::paintFooterContent(
|
void OverlayWidget::paintFooterContent(
|
||||||
Painter &p,
|
Painter &p,
|
||||||
QRect outer,
|
QRect outer,
|
||||||
|
@ -4221,7 +4242,7 @@ void OverlayWidget::paintFooterContent(
|
||||||
const auto date = _dateNav.translated(shift);
|
const auto date = _dateNav.translated(shift);
|
||||||
if (header.intersects(clip)) {
|
if (header.intersects(clip)) {
|
||||||
auto o = _headerHasLink ? overLevel(OverHeader) : 0;
|
auto o = _headerHasLink ? overLevel(OverHeader) : 0;
|
||||||
p.setOpacity((o * st::mediaviewIconOverOpacity + (1 - o) * st::mediaviewIconOpacity) * opacity);
|
p.setOpacity(controlOpacity(o) * opacity);
|
||||||
p.drawText(header.left(), header.top() + st::mediaviewThickFont->ascent, _headerText);
|
p.drawText(header.left(), header.top() + st::mediaviewThickFont->ascent, _headerText);
|
||||||
|
|
||||||
if (o > 0) {
|
if (o > 0) {
|
||||||
|
@ -4235,7 +4256,7 @@ void OverlayWidget::paintFooterContent(
|
||||||
// name
|
// name
|
||||||
if (_nameNav.isValid() && name.intersects(clip)) {
|
if (_nameNav.isValid() && name.intersects(clip)) {
|
||||||
float64 o = _from ? overLevel(OverName) : 0.;
|
float64 o = _from ? overLevel(OverName) : 0.;
|
||||||
p.setOpacity((o * st::mediaviewIconOverOpacity + (1 - o) * st::mediaviewIconOpacity) * opacity);
|
p.setOpacity(controlOpacity(o) * opacity);
|
||||||
_fromNameLabel.drawElided(p, name.left(), name.top(), name.width());
|
_fromNameLabel.drawElided(p, name.left(), name.top(), name.width());
|
||||||
|
|
||||||
if (o > 0) {
|
if (o > 0) {
|
||||||
|
@ -4247,7 +4268,7 @@ void OverlayWidget::paintFooterContent(
|
||||||
// date
|
// date
|
||||||
if (date.intersects(clip)) {
|
if (date.intersects(clip)) {
|
||||||
float64 o = overLevel(OverDate);
|
float64 o = overLevel(OverDate);
|
||||||
p.setOpacity((o * st::mediaviewIconOverOpacity + (1 - o) * st::mediaviewIconOpacity) * opacity);
|
p.setOpacity(controlOpacity(o) * opacity);
|
||||||
p.drawText(date.left(), date.top() + st::mediaviewFont->ascent, _dateText);
|
p.drawText(date.left(), date.top() + st::mediaviewFont->ascent, _dateText);
|
||||||
|
|
||||||
if (o > 0) {
|
if (o > 0) {
|
||||||
|
@ -4323,6 +4344,7 @@ void OverlayWidget::handleKeyPress(not_null<QKeyEvent*> e) {
|
||||||
} else if (_fullScreenVideo) {
|
} else if (_fullScreenVideo) {
|
||||||
if (key == Qt::Key_Escape) {
|
if (key == Qt::Key_Escape) {
|
||||||
playbackToggleFullScreen();
|
playbackToggleFullScreen();
|
||||||
|
} else if (ctrl) {
|
||||||
} else if (key == Qt::Key_0) {
|
} else if (key == Qt::Key_0) {
|
||||||
activateControls();
|
activateControls();
|
||||||
restartAtSeekPosition(0);
|
restartAtSeekPosition(0);
|
||||||
|
@ -4337,7 +4359,6 @@ void OverlayWidget::handleKeyPress(not_null<QKeyEvent*> e) {
|
||||||
activateControls();
|
activateControls();
|
||||||
seekRelativeTime(kSeekTimeMs);
|
seekRelativeTime(kSeekTimeMs);
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4379,8 +4400,6 @@ void OverlayWidget::handleKeyPress(not_null<QKeyEvent*> e) {
|
||||||
zoomIn();
|
zoomIn();
|
||||||
} else if (key == Qt::Key_Minus || key == Qt::Key_Underscore) {
|
} else if (key == Qt::Key_Minus || key == Qt::Key_Underscore) {
|
||||||
zoomOut();
|
zoomOut();
|
||||||
} else if (key == Qt::Key_0) {
|
|
||||||
zoomReset();
|
|
||||||
} else if (key == Qt::Key_I) {
|
} else if (key == Qt::Key_I) {
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
@ -5090,10 +5109,13 @@ bool OverlayWidget::filterApplicationEvent(
|
||||||
not_null<QEvent*> e) {
|
not_null<QEvent*> e) {
|
||||||
const auto type = e->type();
|
const auto type = e->type();
|
||||||
if (type == QEvent::ShortcutOverride) {
|
if (type == QEvent::ShortcutOverride) {
|
||||||
const auto keyEvent = static_cast<QKeyEvent*>(e.get());
|
const auto event = static_cast<QKeyEvent*>(e.get());
|
||||||
const auto ctrl = keyEvent->modifiers().testFlag(Qt::ControlModifier);
|
const auto key = event->key();
|
||||||
if (keyEvent->key() == Qt::Key_F && ctrl && _streamed) {
|
const auto ctrl = event->modifiers().testFlag(Qt::ControlModifier);
|
||||||
|
if (key == Qt::Key_F && ctrl && _streamed) {
|
||||||
playbackToggleFullScreen();
|
playbackToggleFullScreen();
|
||||||
|
} else if (key == Qt::Key_0 && ctrl) {
|
||||||
|
zoomReset();
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} else if (type == QEvent::MouseMove
|
} else if (type == QEvent::MouseMove
|
||||||
|
|
|
@ -154,6 +154,7 @@ private:
|
||||||
struct ContentGeometry {
|
struct ContentGeometry {
|
||||||
QRectF rect;
|
QRectF rect;
|
||||||
qreal rotation = 0.;
|
qreal rotation = 0.;
|
||||||
|
qreal controlsOpacity = 0.;
|
||||||
};
|
};
|
||||||
struct StartStreaming {
|
struct StartStreaming {
|
||||||
StartStreaming() : continueStreaming(false), startTime(0) {
|
StartStreaming() : continueStreaming(false), startTime(0) {
|
||||||
|
@ -417,6 +418,9 @@ private:
|
||||||
QRect clip,
|
QRect clip,
|
||||||
float64 opacity);
|
float64 opacity);
|
||||||
|
|
||||||
|
[[nodiscard]] float64 controlOpacity(
|
||||||
|
float64 progress,
|
||||||
|
bool nonbright = false) const;
|
||||||
[[nodiscard]] bool isSaveMsgShown() const;
|
[[nodiscard]] bool isSaveMsgShown() const;
|
||||||
|
|
||||||
void updateOverRect(OverState state);
|
void updateOverRect(OverState state);
|
||||||
|
@ -552,6 +556,9 @@ private:
|
||||||
object_ptr<Ui::LinkButton> _docSaveAs;
|
object_ptr<Ui::LinkButton> _docSaveAs;
|
||||||
object_ptr<Ui::LinkButton> _docCancel;
|
object_ptr<Ui::LinkButton> _docCancel;
|
||||||
|
|
||||||
|
QRect _bottomShadowRect;
|
||||||
|
QRect _topShadowRect;
|
||||||
|
|
||||||
QRect _photoRadialRect;
|
QRect _photoRadialRect;
|
||||||
Ui::RadialAnimation _radial;
|
Ui::RadialAnimation _radial;
|
||||||
|
|
||||||
|
|