Rich dropdown animations.
|
@ -29,6 +29,7 @@ windowOverBg: #f0f0f0; // light gray: fallback for over background
|
|||
windowSubTextFgOver: #7c99b2; // gray over light blue: fallback for subtext over color
|
||||
windowActiveTextFg: #2687bf; // online blue: fallback for active color
|
||||
windowShadowFg: #000000; // black: fallback for shadow color
|
||||
windowShadowFgFallback: #cdcdcd; // gray: fallback for shadow without
|
||||
|
||||
imageBg: #000000;
|
||||
imageBgTransparent: #ffffff;
|
||||
|
@ -46,6 +47,8 @@ lightButtonBgOver: #edf7ff;
|
|||
lightButtonFg: #2b99d5;
|
||||
lightButtonFgOver: lightButtonFg;
|
||||
|
||||
menuBg: windowBg;
|
||||
menuBgOver: windowOverBg;
|
||||
menuIconFg: #a8a8a8;
|
||||
menuIconFgOver: #999999;
|
||||
|
||||
|
|
After Width: | Height: | Size: 86 B |
After Width: | Height: | Size: 107 B |
After Width: | Height: | Size: 179 B |
After Width: | Height: | Size: 262 B |
After Width: | Height: | Size: 78 B |
After Width: | Height: | Size: 93 B |
After Width: | Height: | Size: 75 B |
After Width: | Height: | Size: 90 B |
After Width: | Height: | Size: 141 B |
After Width: | Height: | Size: 195 B |
|
@ -30,6 +30,7 @@ windowOverBg: #f0f0f0;
|
|||
windowSubTextFgOver: #7c99b2;
|
||||
windowActiveTextFg: #2687bf;
|
||||
windowShadowFg: #000000;
|
||||
windowShadowFgFallback: #cdcdcd;
|
||||
imageBg: #000000;
|
||||
imageBgTransparent: #ffffff;
|
||||
activeButtonBg: windowActiveBg;
|
||||
|
@ -42,6 +43,8 @@ lightButtonBg: windowBg;
|
|||
lightButtonBgOver: #edf7ff;
|
||||
lightButtonFg: #2b99d5;
|
||||
lightButtonFgOver: lightButtonFg;
|
||||
menuBg: windowBg;
|
||||
menuBgOver: windowOverBg;
|
||||
menuIconFg: #a8a8a8;
|
||||
menuIconFgOver: #999999;
|
||||
titleBg: windowOverBg;
|
||||
|
|
|
@ -2235,6 +2235,7 @@ namespace {
|
|||
::cornersMaskSmall[i] = new QImage(mask[i].convertToFormat(QImage::Format_ARGB32_Premultiplied));
|
||||
::cornersMaskSmall[i]->setDevicePixelRatio(cRetinaFactor());
|
||||
}
|
||||
prepareCorners(MenuCorners, st::buttonRadius, st::menuBg);
|
||||
prepareCorners(BotKbOverCorners, st::dateRadius, st::msgBotKbOverBg);
|
||||
prepareCorners(StickerCorners, st::dateRadius, st::msgServiceBg);
|
||||
prepareCorners(StickerSelectedCorners, st::dateRadius, st::msgServiceSelectBg);
|
||||
|
|
|
@ -147,8 +147,9 @@ struct lambda_wrap_helper_move_impl<Lambda, std_::false_type, Return, Args...> :
|
|||
static void construct_move_lambda_method(void *lambda, void *source) {
|
||||
static_assert(alignof(JustLambda) <= alignof(typename Parent::alignment), "Bad lambda alignment.");
|
||||
auto space = sizeof(JustLambda);
|
||||
auto aligned = std_::align(alignof(JustLambda), space, lambda, space);
|
||||
t_assert(aligned == lambda);
|
||||
// We want to be able to pass lambda by value in 32bit Windows version.
|
||||
//auto aligned = std_::align(alignof(JustLambda), space, lambda, space);
|
||||
//t_assert(aligned == lambda);
|
||||
auto source_lambda = static_cast<JustLambda*>(source);
|
||||
new (lambda) JustLambda(static_cast<JustLambda&&>(*source_lambda));
|
||||
}
|
||||
|
@ -222,8 +223,9 @@ struct lambda_wrap_helper_copy_impl<Lambda, std_::false_type, Return, Args...> :
|
|||
static void construct_copy_lambda_method(void *lambda, const void *source) {
|
||||
static_assert(alignof(JustLambda) <= alignof(typename Parent::alignment), "Bad lambda alignment.");
|
||||
auto space = sizeof(JustLambda);
|
||||
auto aligned = std_::align(alignof(JustLambda), space, lambda, space);
|
||||
t_assert(aligned == lambda);
|
||||
// We want to be able to pass lambda by value in 32bit Windows version.
|
||||
//auto aligned = std_::align(alignof(JustLambda), space, lambda, space);
|
||||
//t_assert(aligned == lambda);
|
||||
auto source_lambda = static_cast<const JustLambda*>(source);
|
||||
new (lambda) JustLambda(static_cast<const JustLambda &>(*source_lambda));
|
||||
}
|
||||
|
@ -319,11 +321,9 @@ protected:
|
|||
lambda_unique(const BaseHelper *helper, const Private &) : helper_(helper) {
|
||||
}
|
||||
|
||||
static_assert(BaseHelper::kStorageSize % sizeof(void*) == 0, "Bad pointer size.");
|
||||
union {
|
||||
void *(storage_[BaseHelper::kStorageSize / sizeof(void*)]);
|
||||
typename BaseHelper::alignment alignment_;
|
||||
};
|
||||
using alignment = typename BaseHelper::alignment;
|
||||
static_assert(BaseHelper::kStorageSize % sizeof(alignment) == 0, "Bad storage size.");
|
||||
alignment storage_[BaseHelper::kStorageSize / sizeof(alignment)];
|
||||
const BaseHelper *helper_;
|
||||
|
||||
};
|
||||
|
|
|
@ -2031,7 +2031,7 @@ void DialogsWidget::showMainMenu() {
|
|||
}, &st::dialogsMenuHelp, &st::dialogsMenuHelpOver);
|
||||
}
|
||||
updateMainMenuGeometry();
|
||||
_mainMenu->showAnimated();
|
||||
_mainMenu->showAnimated(Ui::PanelAnimation::Origin::TopLeft);
|
||||
}
|
||||
|
||||
void DialogsWidget::searchMessages(const QString &query, PeerData *inPeer) {
|
||||
|
|
|
@ -3128,6 +3128,7 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent)
|
|||
_silent->hide();
|
||||
_botCommandStart->hide();
|
||||
|
||||
_attachType->setOrigin(Ui::PanelAnimation::Origin::BottomLeft);
|
||||
_attachToggle->installEventFilter(_attachType);
|
||||
_attachEmoji->installEventFilter(_emojiPan);
|
||||
|
||||
|
|
|
@ -36,6 +36,7 @@ enum RoundCorners {
|
|||
SmallMaskCorners = 0x00, // for images
|
||||
LargeMaskCorners,
|
||||
|
||||
MenuCorners,
|
||||
BotKbOverCorners,
|
||||
StickerCorners,
|
||||
StickerSelectedCorners,
|
||||
|
|
|
@ -160,6 +160,7 @@ MainWidget::MainWidget(QWidget *parent) : TWidget(parent)
|
|||
MTP::setGlobalFailHandler(rpcFail(&MainWidget::updateFail));
|
||||
|
||||
_mediaType->hide();
|
||||
_mediaType->setOrigin(Ui::PanelAnimation::Origin::TopRight);
|
||||
_topBar->mediaTypeButton()->installEventFilter(_mediaType);
|
||||
|
||||
show();
|
||||
|
|
|
@ -34,7 +34,7 @@ MediaPlayerButton {
|
|||
cancelStroke: pixels;
|
||||
}
|
||||
|
||||
mediaPlayerActiveFg: #54b5ed;
|
||||
mediaPlayerActiveFg: windowActiveBg;
|
||||
mediaPlayerInactiveFg: #dfebf2;
|
||||
|
||||
mediaPlayerButton: MediaPlayerButton {
|
||||
|
|
|
@ -172,7 +172,7 @@ void Panel::paintEvent(QPaintEvent *e) {
|
|||
if (_layout != Layout::Full) {
|
||||
shadowedSides |= (rtl() ? Side::Left : Side::Right) | Side::Top;
|
||||
}
|
||||
_shadow.paint(p, shadowedRect, st::defaultInnerDropdown.shadowShift, shadowedSides);
|
||||
_shadow.paint(p, shadowedRect, st::defaultDropdownShadowShift, shadowedSides);
|
||||
p.fillRect(shadowedRect, st::windowBg);
|
||||
}
|
||||
|
||||
|
|
|
@ -81,7 +81,7 @@ void VolumeController::applyVolumeChange(float64 volume) {
|
|||
}
|
||||
|
||||
VolumeWidget::VolumeWidget(QWidget *parent) : TWidget(parent)
|
||||
, _shadow(st::defaultInnerDropdown.shadow)
|
||||
, _shadow(st::defaultDropdownShadow)
|
||||
, _controller(this) {
|
||||
hide();
|
||||
_controller->setIsVertical(true);
|
||||
|
@ -145,7 +145,7 @@ void VolumeWidget::paintEvent(QPaintEvent *e) {
|
|||
auto shadowedRect = rect().marginsRemoved(getMargin());
|
||||
using ShadowSide = Ui::RectShadow::Side;
|
||||
auto shadowedSides = ShadowSide::Left | ShadowSide::Right | ShadowSide::Bottom;
|
||||
_shadow.paint(p, shadowedRect, st::defaultInnerDropdown.shadowShift, shadowedSides);
|
||||
_shadow.paint(p, shadowedRect, st::defaultDropdownShadowShift, shadowedSides);
|
||||
p.fillRect(shadowedRect.x(), 0, shadowedRect.width(), shadowedRect.y() + shadowedRect.height(), st::windowBg);
|
||||
}
|
||||
|
||||
|
|
|
@ -137,10 +137,24 @@ mediaviewMenu: Menu(defaultMenu) {
|
|||
|
||||
separatorFg: mediaviewMenuFg;
|
||||
}
|
||||
mediaviewMenuShadow: Shadow(defaultEmptyShadow) {
|
||||
fallback: mediaviewMenuBg;
|
||||
}
|
||||
mediaviewPanelAnimation: PanelAnimation(defaultPanelAnimation) {
|
||||
fadeBg: mediaviewMenuBg;
|
||||
shadow: mediaviewMenuShadow;
|
||||
}
|
||||
mediaviewPopupMenu: PopupMenu(defaultPopupMenu) {
|
||||
shadow: icon {};
|
||||
shadow: mediaviewMenuShadow;
|
||||
menu: mediaviewMenu;
|
||||
animation: mediaviewPanelAnimation;
|
||||
}
|
||||
mediaviewDropdownMenu: DropdownMenu(defaultDropdownMenu) {
|
||||
menu: mediaviewMenu;
|
||||
wrap: InnerDropdown(defaultInnerDropdown) {
|
||||
bg: mediaviewMenuBg;
|
||||
animation: mediaviewPanelAnimation;
|
||||
scrollPadding: margins(0px, 8px, 0px, 8px);
|
||||
shadow: mediaviewMenuShadow;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2542,7 +2542,7 @@ void MediaView::onDropdown() {
|
|||
_dropdown->addAction(action.text, this, action.member);
|
||||
}
|
||||
_dropdown->moveToRight(0, height() - _dropdown->height());
|
||||
_dropdown->showAnimated();
|
||||
_dropdown->showAnimated(Ui::PanelAnimation::Origin::BottomRight);
|
||||
_dropdown->setFocus();
|
||||
}
|
||||
|
||||
|
|
|
@ -213,6 +213,25 @@ void startManager();
|
|||
void stopManager();
|
||||
void registerClipManager(Media::Clip::Manager *manager);
|
||||
|
||||
inline uint64 shifted(uint32 components) {
|
||||
auto wide = static_cast<uint64>(components);
|
||||
return (wide & 0x00000000000000FFULL)
|
||||
| ((wide & 0x000000000000FF00ULL) << 8)
|
||||
| ((wide & 0x0000000000FF0000ULL) << 16)
|
||||
| ((wide & 0x00000000FF000000ULL) << 24);
|
||||
}
|
||||
|
||||
inline uint32 unshifted(uint64 components) {
|
||||
return static_cast<uint32>((components & 0x000000000000FF00ULL) >> 8)
|
||||
| static_cast<uint32>((components & 0x00000000FF000000ULL) >> 16)
|
||||
| static_cast<uint32>((components & 0x0000FF0000000000ULL) >> 24)
|
||||
| static_cast<uint32>((components & 0xFF00000000000000ULL) >> 32);
|
||||
}
|
||||
|
||||
inline uint64 reshifted(uint64 components) {
|
||||
return (components >> 8) & 0x00FF00FF00FF00FFULL;
|
||||
}
|
||||
|
||||
inline int interpolate(int a, int b, float64 b_ratio) {
|
||||
return qRound(a + float64(b - a) * b_ratio);
|
||||
}
|
||||
|
@ -220,11 +239,20 @@ inline int interpolate(int a, int b, float64 b_ratio) {
|
|||
inline QColor color(QColor a, QColor b, float64 b_ratio) {
|
||||
auto bOpacity = snap(interpolate(0, 255, b_ratio), 0, 255) + 1;
|
||||
auto aOpacity = (256 - bOpacity);
|
||||
auto bBits = static_cast<uint64>(b.alpha() & 0xFF)
|
||||
| (static_cast<uint64>(b.red() & 0xFF) << 16)
|
||||
| (static_cast<uint64>(b.green() & 0xFF) << 32)
|
||||
| (static_cast<uint64>(b.blue() & 0xFF) << 48);
|
||||
auto aBits = static_cast<uint64>(a.alpha() & 0xFF)
|
||||
| (static_cast<uint64>(a.red() & 0xFF) << 16)
|
||||
| (static_cast<uint64>(a.green() & 0xFF) << 32)
|
||||
| (static_cast<uint64>(a.blue() & 0xFF) << 48);
|
||||
auto resultBits = (aBits * aOpacity + bBits * bOpacity) >> 8;
|
||||
return {
|
||||
(a.red() * aOpacity + b.red() * bOpacity) >> 8,
|
||||
(a.green() * aOpacity + b.green() * bOpacity) >> 8,
|
||||
(a.blue() * aOpacity + b.blue() * bOpacity) >> 8,
|
||||
(a.alpha() * aOpacity + b.alpha() * bOpacity) >> 8
|
||||
static_cast<int>((resultBits >> 16) & 0xFF),
|
||||
static_cast<int>((resultBits >> 32) & 0xFF),
|
||||
static_cast<int>((resultBits >> 48) & 0xFF),
|
||||
static_cast<int>(resultBits & 0xFF),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,492 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
It is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
In addition, as a special exception, the copyright holders give permission
|
||||
to link the code of portions of this program with the OpenSSL library.
|
||||
|
||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#include "stdafx.h"
|
||||
#include "ui/effects/panel_animation.h"
|
||||
|
||||
namespace Ui {
|
||||
|
||||
void PanelAnimation::setFinalImage(QImage &&finalImage, QRect inner) {
|
||||
t_assert(!started());
|
||||
_finalImage = std_::move(finalImage).convertToFormat(QImage::Format_ARGB32_Premultiplied);
|
||||
|
||||
t_assert(!_finalImage.isNull());
|
||||
_finalWidth = _finalImage.width();
|
||||
_finalHeight = _finalImage.height();
|
||||
|
||||
setStartWidth();
|
||||
setStartHeight();
|
||||
setStartAlpha();
|
||||
setStartFadeTop();
|
||||
createFadeMask();
|
||||
setWidthDuration();
|
||||
setHeightDuration();
|
||||
setAlphaDuration();
|
||||
setShadow();
|
||||
|
||||
auto checkCorner = [this, inner](Corner &corner) {
|
||||
if (!corner.valid()) return;
|
||||
if ((_startWidth >= 0 && _startWidth < _finalWidth)
|
||||
|| (_startHeight >= 0 && _startHeight < _finalHeight)) {
|
||||
t_assert(corner.width <= inner.width());
|
||||
t_assert(corner.height <= inner.height());
|
||||
}
|
||||
};
|
||||
checkCorner(_topLeft);
|
||||
checkCorner(_topRight);
|
||||
checkCorner(_bottomLeft);
|
||||
checkCorner(_bottomRight);
|
||||
_finalInts = reinterpret_cast<const uint32*>(_finalImage.constBits());
|
||||
_finalIntsPerLine = (_finalImage.bytesPerLine() >> 2);
|
||||
_finalIntsPerLineAdded = _finalIntsPerLine - _finalWidth;
|
||||
t_assert(_finalImage.depth() == static_cast<int>(sizeof(uint32) << 3));
|
||||
t_assert(_finalImage.bytesPerLine() == (_finalIntsPerLine << 2));
|
||||
t_assert(_finalIntsPerLineAdded >= 0);
|
||||
|
||||
_finalInnerLeft = inner.x();
|
||||
_finalInnerTop = inner.y();
|
||||
_finalInnerWidth = inner.width();
|
||||
_finalInnerHeight = inner.height();
|
||||
_finalInnerRight = _finalInnerLeft + _finalInnerWidth;
|
||||
_finalInnerBottom = _finalInnerTop + _finalInnerHeight;
|
||||
t_assert(QRect(0, 0, _finalWidth, _finalHeight).contains(inner));
|
||||
}
|
||||
|
||||
void PanelAnimation::setShadow() {
|
||||
if (_skipShadow) return;
|
||||
|
||||
_shadow.extend = _st.shadow.extend;
|
||||
_shadow.left = cloneImage(_st.shadow.left);
|
||||
if (_shadow.valid()) {
|
||||
_shadow.topLeft = cloneImage(_st.shadow.topLeft);
|
||||
_shadow.top = cloneImage(_st.shadow.top);
|
||||
_shadow.topRight = cloneImage(_st.shadow.topRight);
|
||||
_shadow.right = cloneImage(_st.shadow.right);
|
||||
_shadow.bottomRight = cloneImage(_st.shadow.bottomRight);
|
||||
_shadow.bottom = cloneImage(_st.shadow.bottom);
|
||||
_shadow.bottomLeft = cloneImage(_st.shadow.bottomLeft);
|
||||
t_assert(!_shadow.topLeft.isNull()
|
||||
&& !_shadow.top.isNull()
|
||||
&& !_shadow.topRight.isNull()
|
||||
&& !_shadow.right.isNull()
|
||||
&& !_shadow.bottomRight.isNull()
|
||||
&& !_shadow.bottom.isNull()
|
||||
&& !_shadow.bottomLeft.isNull());
|
||||
} else {
|
||||
_shadow.topLeft =
|
||||
_shadow.top =
|
||||
_shadow.topRight =
|
||||
_shadow.right =
|
||||
_shadow.bottomRight =
|
||||
_shadow.bottom =
|
||||
_shadow.bottomLeft = QImage();
|
||||
}
|
||||
}
|
||||
|
||||
void PanelAnimation::setStartWidth() {
|
||||
_startWidth = qRound(_st.startWidth * _finalImage.width());
|
||||
if (_startWidth >= 0) t_assert(_startWidth <= _finalWidth);
|
||||
}
|
||||
|
||||
void PanelAnimation::setStartHeight() {
|
||||
_startHeight = qRound(_st.startHeight * _finalImage.height());
|
||||
if (_startHeight >= 0) t_assert(_startHeight <= _finalHeight);
|
||||
}
|
||||
|
||||
void PanelAnimation::setStartAlpha() {
|
||||
_startAlpha = qRound(_st.startOpacity * 255);
|
||||
t_assert(_startAlpha >= 0 && _startAlpha < 256);
|
||||
}
|
||||
|
||||
void PanelAnimation::setStartFadeTop() {
|
||||
_startFadeTop = qRound(_st.startFadeTop * _finalImage.height());
|
||||
}
|
||||
|
||||
void PanelAnimation::createFadeMask() {
|
||||
auto resultHeight = qRound(_finalImage.height() * _st.fadeHeight);
|
||||
auto finalAlpha = qRound(_st.fadeOpacity * 255);
|
||||
t_assert(finalAlpha >= 0 && finalAlpha < 256);
|
||||
auto result = QImage(1, resultHeight, QImage::Format_ARGB32_Premultiplied);
|
||||
auto ints = reinterpret_cast<uint32*>(result.bits());
|
||||
auto intsPerLine = (result.bytesPerLine() >> 2);
|
||||
auto up = (_origin == PanelAnimation::Origin::BottomLeft || _origin == PanelAnimation::Origin::BottomRight);
|
||||
auto from = up ? resultHeight : 0, to = resultHeight - from, delta = up ? -1 : 1;
|
||||
for (auto y = from; y != to; y += delta) {
|
||||
auto alpha = static_cast<uint32>(finalAlpha * y) / resultHeight;
|
||||
*ints = (0xFFU << 24) | (alpha << 16) | (alpha << 8) | alpha;
|
||||
ints += intsPerLine;
|
||||
}
|
||||
_fadeMask = style::colorizeImage(result, _st.fadeBg);
|
||||
_fadeHeight = _fadeMask.height();
|
||||
_fadeInts = reinterpret_cast<const uint32*>(_fadeMask.constBits());
|
||||
_fadeIntsPerLine = (_fadeMask.bytesPerLine() >> 2);
|
||||
t_assert(_fadeMask.bytesPerLine() == (_fadeIntsPerLine << 2));
|
||||
}
|
||||
|
||||
void PanelAnimation::setCornerMasks(QImage &&topLeft, QImage &&topRight, QImage &&bottomLeft, QImage &&bottomRight) {
|
||||
setCornerMask(_topLeft, std_::move(topLeft));
|
||||
setCornerMask(_topRight, std_::move(topRight));
|
||||
setCornerMask(_bottomLeft, std_::move(bottomLeft));
|
||||
setCornerMask(_bottomRight, std_::move(bottomRight));
|
||||
}
|
||||
|
||||
void PanelAnimation::setSkipShadow(bool skipShadow) {
|
||||
t_assert(!started());
|
||||
_skipShadow = skipShadow;
|
||||
}
|
||||
|
||||
void PanelAnimation::setCornerMask(Corner &corner, QImage &&image) {
|
||||
t_assert(!started());
|
||||
corner.image = std_::move(image);
|
||||
if (corner.valid()) {
|
||||
corner.width = corner.image.width();
|
||||
corner.height = corner.image.height();
|
||||
corner.bytes = corner.image.constBits();
|
||||
corner.bytesPerPixel = (corner.image.depth() >> 3);
|
||||
corner.bytesPerLineAdded = corner.image.bytesPerLine() - corner.width * corner.bytesPerPixel;
|
||||
t_assert(corner.image.depth() == (corner.bytesPerPixel << 3));
|
||||
t_assert(corner.bytesPerLineAdded >= 0);
|
||||
if (_startWidth >= 0) t_assert(corner.width <= _startWidth);
|
||||
if (_startHeight >= 0) t_assert(corner.height <= _startHeight);
|
||||
if (!_finalImage.isNull()) {
|
||||
t_assert(corner.width <= _finalInnerWidth);
|
||||
t_assert(corner.height <= _finalInnerHeight);
|
||||
}
|
||||
} else {
|
||||
corner.width = corner.height = 0;
|
||||
}
|
||||
}
|
||||
|
||||
QImage PanelAnimation::cloneImage(const style::icon &source) {
|
||||
if (source.empty()) return QImage();
|
||||
|
||||
auto result = QImage(source.width(), source.height(), QImage::Format_ARGB32_Premultiplied);
|
||||
result.fill(Qt::transparent);
|
||||
{
|
||||
Painter p(&result);
|
||||
source.paint(p, 0, 0, source.width());
|
||||
}
|
||||
return std_::move(result);
|
||||
}
|
||||
|
||||
void PanelAnimation::setWidthDuration() {
|
||||
_widthDuration = _st.widthDuration;
|
||||
t_assert(_widthDuration >= 0.);
|
||||
t_assert(_widthDuration <= 1.);
|
||||
}
|
||||
|
||||
void PanelAnimation::setHeightDuration() {
|
||||
t_assert(!started());
|
||||
_heightDuration = _st.heightDuration;
|
||||
t_assert(_heightDuration >= 0.);
|
||||
t_assert(_heightDuration <= 1.);
|
||||
}
|
||||
|
||||
void PanelAnimation::setAlphaDuration() {
|
||||
t_assert(!started());
|
||||
_alphaDuration = _st.opacityDuration;
|
||||
t_assert(_alphaDuration >= 0.);
|
||||
t_assert(_alphaDuration <= 1.);
|
||||
}
|
||||
|
||||
void PanelAnimation::start() {
|
||||
t_assert(!started());
|
||||
t_assert(!_finalImage.isNull());
|
||||
_frame = QImage(_finalWidth, _finalHeight, QImage::Format_ARGB32_Premultiplied);
|
||||
_frame.setDevicePixelRatio(_finalImage.devicePixelRatio());
|
||||
_frameIntsPerLine = (_frame.bytesPerLine() >> 2);
|
||||
_frameInts = reinterpret_cast<uint32*>(_frame.bits());
|
||||
_frameIntsPerLineAdded = _frameIntsPerLine - _finalWidth;
|
||||
t_assert(_frame.depth() == static_cast<int>(sizeof(uint32) << 3));
|
||||
t_assert(_frame.bytesPerLine() == (_frameIntsPerLine << 2));
|
||||
t_assert(_frameIntsPerLineAdded >= 0);
|
||||
}
|
||||
|
||||
const QImage &PanelAnimation::getFrame(float64 dt, float64 opacity) {
|
||||
t_assert(started());
|
||||
t_assert(dt >= 0.);
|
||||
|
||||
auto &transition = anim::easeOutCirc;
|
||||
constexpr auto finalAlpha = 256;
|
||||
auto alpha = (dt >= _alphaDuration) ? finalAlpha : anim::interpolate(_startAlpha + 1, finalAlpha, transition(1., dt / _alphaDuration));
|
||||
_frameAlpha = anim::interpolate(0, alpha, opacity);
|
||||
|
||||
auto frameWidth = (_startWidth < 0 || dt >= _widthDuration) ? _finalInnerWidth : anim::interpolate(_startWidth, _finalInnerWidth, transition(1., dt / _widthDuration));
|
||||
auto frameHeight = (_startHeight < 0 || dt >= _heightDuration) ? _finalInnerHeight : anim::interpolate(_startHeight, _finalInnerHeight, transition(1., dt / _heightDuration));
|
||||
auto frameLeft = (_origin == Origin::TopLeft || _origin == Origin::BottomLeft) ? _finalInnerLeft : (_finalInnerRight - frameWidth);
|
||||
auto frameTop = (_origin == Origin::TopLeft || _origin == Origin::TopRight) ? _finalInnerTop : (_finalInnerBottom - frameHeight);
|
||||
auto frameRight = frameLeft + frameWidth;
|
||||
auto frameBottom = frameTop + frameHeight;
|
||||
|
||||
auto fadeTop = (_fadeHeight > 0) ? snap(anim::interpolate(_startFadeTop, _finalInnerHeight, transition(1., dt)), 0, frameHeight) : frameHeight;
|
||||
auto fadeBottom = (fadeTop < frameHeight) ? qMin(fadeTop + _fadeHeight, frameHeight) : frameHeight;
|
||||
auto fadeSkipLines = 0;
|
||||
if (_origin == Origin::BottomLeft || _origin == Origin::BottomRight) {
|
||||
fadeTop = frameHeight - fadeTop;
|
||||
fadeBottom = frameHeight - fadeBottom;
|
||||
qSwap(fadeTop, fadeBottom);
|
||||
fadeSkipLines = fadeTop + _fadeHeight - fadeBottom;
|
||||
}
|
||||
fadeTop += frameTop;
|
||||
fadeBottom += frameTop;
|
||||
|
||||
auto finalInts = _finalInts + frameLeft + frameTop * _finalIntsPerLine;
|
||||
auto frameInts = _frameInts + frameLeft + frameTop * _frameIntsPerLine;
|
||||
auto finalIntsPerLineAdd = (_finalWidth - frameWidth) + _finalIntsPerLineAdded;
|
||||
auto frameIntsPerLineAdd = (_finalWidth - frameWidth) + _frameIntsPerLineAdded;
|
||||
|
||||
// Draw frameWidth x fadeTop with fade first color.
|
||||
auto fadeInts = _fadeInts + fadeSkipLines * _fadeIntsPerLine;
|
||||
auto fadeWithMasterAlpha = [this](uint32 fade) {
|
||||
auto fadeAlphaAddition = (256 - (fade >> 24));
|
||||
auto fadePattern = anim::shifted(fade);
|
||||
return [this, fadeAlphaAddition, fadePattern](uint32 source) {
|
||||
auto sourceAlpha = (source >> 24) + 1;
|
||||
auto sourcePattern = anim::shifted(source);
|
||||
auto mixedPattern = anim::reshifted(fadePattern * sourceAlpha + sourcePattern * fadeAlphaAddition);
|
||||
return anim::unshifted(mixedPattern * _frameAlpha);
|
||||
};
|
||||
};
|
||||
if (frameTop != fadeTop) {
|
||||
// Take the fade components from the first line of the fade mask.
|
||||
auto withMasterAlpha = fadeWithMasterAlpha(_fadeInts ? *_fadeInts : 0);
|
||||
for (auto y = frameTop; y != fadeTop; ++y) {
|
||||
for (auto x = frameLeft; x != frameRight; ++x) {
|
||||
*frameInts++ = withMasterAlpha(*finalInts++);
|
||||
}
|
||||
finalInts += finalIntsPerLineAdd;
|
||||
frameInts += frameIntsPerLineAdd;
|
||||
}
|
||||
}
|
||||
|
||||
// Draw frameWidth x (fadeBottom - fadeTop) with fade gradient.
|
||||
for (auto y = fadeTop; y != fadeBottom; ++y) {
|
||||
auto withMasterAlpha = fadeWithMasterAlpha(*fadeInts);
|
||||
for (auto x = frameLeft; x != frameRight; ++x) {
|
||||
*frameInts++ = withMasterAlpha(*finalInts++);
|
||||
}
|
||||
finalInts += finalIntsPerLineAdd;
|
||||
frameInts += frameIntsPerLineAdd;
|
||||
fadeInts += _fadeIntsPerLine;
|
||||
}
|
||||
|
||||
// Draw frameWidth x (frameBottom - fadeBottom) with fade final color.
|
||||
if (fadeBottom != frameBottom) {
|
||||
// Take the fade components from the last line of the fade mask.
|
||||
auto withMasterAlpha = fadeWithMasterAlpha(*(fadeInts - _fadeIntsPerLine));
|
||||
for (auto y = fadeBottom; y != frameBottom; ++y) {
|
||||
for (auto x = frameLeft; x != frameRight; ++x) {
|
||||
*frameInts++ = withMasterAlpha(*finalInts++);
|
||||
}
|
||||
finalInts += finalIntsPerLineAdd;
|
||||
frameInts += frameIntsPerLineAdd;
|
||||
}
|
||||
}
|
||||
|
||||
// Draw corners
|
||||
auto innerLeft = qMax(_finalInnerLeft, frameLeft);
|
||||
auto innerTop = qMax(_finalInnerTop, frameTop);
|
||||
auto innerRight = qMin(_finalInnerRight, frameRight);
|
||||
auto innerBottom = qMin(_finalInnerBottom, frameBottom);
|
||||
if (innerLeft != _finalInnerLeft || innerTop != _finalInnerTop) {
|
||||
paintCorner(_topLeft, innerLeft, innerTop);
|
||||
}
|
||||
if (innerRight != _finalInnerRight || innerTop != _finalInnerTop) {
|
||||
paintCorner(_topRight, innerRight - _topRight.width, innerTop);
|
||||
}
|
||||
if (innerLeft != _finalInnerLeft || innerBottom != _finalInnerBottom) {
|
||||
paintCorner(_bottomLeft, innerLeft, innerBottom - _bottomLeft.height);
|
||||
}
|
||||
if (innerRight != _finalInnerRight || innerBottom != _finalInnerBottom) {
|
||||
paintCorner(_bottomRight, innerRight - _bottomRight.width, innerBottom - _bottomRight.height);
|
||||
}
|
||||
|
||||
// Fill the rest with transparent
|
||||
if (frameTop) {
|
||||
memset(_frameInts, 0, _frameIntsPerLine * frameTop * sizeof(uint32));
|
||||
}
|
||||
auto widthLeft = (_finalWidth - frameRight);
|
||||
if (frameLeft || widthLeft) {
|
||||
auto frameInts = _frameInts + frameTop * _frameIntsPerLine;
|
||||
for (auto y = frameTop; y != frameBottom; ++y) {
|
||||
memset(frameInts, 0, frameLeft * sizeof(uint32));
|
||||
memset(frameInts + frameLeft + frameWidth, 0, widthLeft * sizeof(uint32));
|
||||
frameInts += _frameIntsPerLine;
|
||||
}
|
||||
}
|
||||
if (auto heightLeft = (_finalHeight - frameBottom)) {
|
||||
memset(_frameInts + frameBottom * _frameIntsPerLine, 0, _frameIntsPerLine * heightLeft * sizeof(uint32));
|
||||
}
|
||||
|
||||
// Draw shadow
|
||||
if (_shadow.valid()) {
|
||||
paintShadow(innerLeft, innerTop, innerRight, innerBottom);
|
||||
}
|
||||
|
||||
// Debug
|
||||
//frameInts = _frameInts;
|
||||
//auto pattern = anim::shifted((static_cast<uint32>(0xFF) << 24) | (static_cast<uint32>(0xFF) << 16) | (static_cast<uint32>(0xFF) << 8) | static_cast<uint32>(0xFF));
|
||||
//for (auto y = 0; y != _finalHeight; ++y) {
|
||||
// for (auto x = 0; x != _finalWidth; ++x) {
|
||||
// auto source = *frameInts;
|
||||
// auto sourceAlpha = (source >> 24);
|
||||
// *frameInts = anim::unshifted(anim::shifted(source) * 256 + pattern * (256 - sourceAlpha));
|
||||
// ++frameInts;
|
||||
// }
|
||||
// frameInts += _frameIntsPerLineAdded;
|
||||
//}
|
||||
|
||||
return _frame;
|
||||
}
|
||||
|
||||
void PanelAnimation::paintCorner(Corner &corner, int left, int top) {
|
||||
auto mask = corner.bytes;
|
||||
auto bytesPerPixel = corner.bytesPerPixel;
|
||||
auto bytesPerLineAdded = corner.bytesPerLineAdded;
|
||||
auto frameInts = _frameInts + top * _frameIntsPerLine + left;
|
||||
auto frameIntsPerLineAdd = _frameIntsPerLine - corner.width;
|
||||
for (auto y = 0; y != corner.height; ++y) {
|
||||
for (auto x = 0; x != corner.width; ++x) {
|
||||
auto alpha = static_cast<uint32>(*mask) + 1;
|
||||
*frameInts = anim::unshifted(anim::shifted(*frameInts) * alpha);
|
||||
++frameInts;
|
||||
mask += bytesPerPixel;
|
||||
}
|
||||
frameInts += frameIntsPerLineAdd;
|
||||
mask += bytesPerLineAdded;
|
||||
}
|
||||
}
|
||||
|
||||
void PanelAnimation::paintShadow(int left, int top, int right, int bottom) {
|
||||
left -= _shadow.extend.left();
|
||||
top -= _shadow.extend.top();
|
||||
right += _shadow.extend.right();
|
||||
bottom += _shadow.extend.bottom();
|
||||
paintShadowCorner(left, top, _shadow.topLeft);
|
||||
paintShadowCorner(right - _shadow.topRight.width(), top, _shadow.topRight);
|
||||
paintShadowCorner(right - _shadow.bottomRight.width(), bottom - _shadow.bottomRight.height(), _shadow.bottomRight);
|
||||
paintShadowCorner(left, bottom - _shadow.bottomLeft.height(), _shadow.bottomLeft);
|
||||
paintShadowVertical(left, top + _shadow.topLeft.height(), bottom - _shadow.bottomLeft.height(), _shadow.left);
|
||||
paintShadowVertical(right - _shadow.right.width(), top + _shadow.topRight.height(), bottom - _shadow.bottomRight.height(), _shadow.right);
|
||||
paintShadowHorizontal(left + _shadow.topLeft.width(), right - _shadow.topRight.width(), top, _shadow.top);
|
||||
paintShadowHorizontal(left + _shadow.bottomLeft.width(), right - _shadow.bottomRight.width(), bottom - _shadow.bottom.height(), _shadow.bottom);
|
||||
}
|
||||
|
||||
void PanelAnimation::paintShadowCorner(int left, int top, const QImage &image) {
|
||||
auto imageWidth = image.width();
|
||||
auto imageHeight = image.height();
|
||||
auto imageInts = reinterpret_cast<const uint32*>(image.constBits());
|
||||
auto imageIntsPerLine = (image.bytesPerLine() >> 2);
|
||||
auto imageIntsPerLineAdded = imageIntsPerLine - imageWidth;
|
||||
if (left < 0) {
|
||||
auto shift = -base::take(left);
|
||||
imageWidth -= shift;
|
||||
imageInts += shift;
|
||||
}
|
||||
if (top < 0) {
|
||||
auto shift = -base::take(top);
|
||||
imageHeight -= shift;
|
||||
imageInts += shift * imageIntsPerLine;
|
||||
}
|
||||
if (left + imageWidth > _finalWidth) {
|
||||
imageWidth = _finalWidth - left;
|
||||
}
|
||||
if (top + imageHeight > _finalHeight) {
|
||||
imageHeight = _finalHeight - top;
|
||||
}
|
||||
if (imageWidth < 0 || imageHeight < 0) return;
|
||||
|
||||
auto frameInts = _frameInts + top * _frameIntsPerLine + left;
|
||||
auto frameIntsPerLineAdd = _frameIntsPerLine - imageWidth;
|
||||
for (auto y = 0; y != imageHeight; ++y) {
|
||||
for (auto x = 0; x != imageWidth; ++x) {
|
||||
auto source = *frameInts;
|
||||
auto shadowAlpha = _frameAlpha - (source >> 24);
|
||||
*frameInts = anim::unshifted(anim::shifted(source) * 256 + anim::shifted(*imageInts) * shadowAlpha);
|
||||
++frameInts;
|
||||
++imageInts;
|
||||
}
|
||||
frameInts += frameIntsPerLineAdd;
|
||||
imageInts += imageIntsPerLineAdded;
|
||||
}
|
||||
}
|
||||
|
||||
void PanelAnimation::paintShadowVertical(int left, int top, int bottom, const QImage &image) {
|
||||
auto imageWidth = image.width();
|
||||
auto imageInts = reinterpret_cast<const uint32*>(image.constBits());
|
||||
if (left < 0) {
|
||||
auto shift = -base::take(left);
|
||||
imageWidth -= shift;
|
||||
imageInts += shift;
|
||||
}
|
||||
if (top < 0) top = 0;
|
||||
if (bottom > _finalHeight) bottom = _finalHeight;
|
||||
if (left + imageWidth > _finalWidth) {
|
||||
imageWidth = _finalWidth - left;
|
||||
}
|
||||
if (imageWidth < 0 || bottom <= top) return;
|
||||
|
||||
auto frameInts = _frameInts + top * _frameIntsPerLine + left;
|
||||
auto frameIntsPerLineAdd = _frameIntsPerLine - imageWidth;
|
||||
for (auto y = top; y != bottom; ++y) {
|
||||
for (auto x = 0; x != imageWidth; ++x) {
|
||||
auto source = *frameInts;
|
||||
auto shadowAlpha = _frameAlpha - (source >> 24);
|
||||
*frameInts = anim::unshifted(anim::shifted(source) * 256 + anim::shifted(*imageInts) * shadowAlpha);
|
||||
++frameInts;
|
||||
++imageInts;
|
||||
}
|
||||
frameInts += frameIntsPerLineAdd;
|
||||
imageInts -= imageWidth;
|
||||
}
|
||||
}
|
||||
|
||||
void PanelAnimation::paintShadowHorizontal(int left, int right, int top, const QImage &image) {
|
||||
auto imageHeight = image.height();
|
||||
auto imageInts = reinterpret_cast<const uint32*>(image.constBits());
|
||||
auto imageIntsPerLine = (image.bytesPerLine() >> 2);
|
||||
if (top < 0) {
|
||||
auto shift = -base::take(top);
|
||||
imageHeight -= shift;
|
||||
imageInts += shift * imageIntsPerLine;
|
||||
}
|
||||
if (left < 0) left = 0;
|
||||
if (right > _finalWidth) right = _finalWidth;
|
||||
if (top + imageHeight > _finalHeight) {
|
||||
imageHeight = _finalHeight - top;
|
||||
}
|
||||
if (imageHeight < 0 || right <= left) return;
|
||||
|
||||
auto frameInts = _frameInts + top * _frameIntsPerLine + left;
|
||||
auto frameIntsPerLineAdd = _frameIntsPerLine - (right - left);
|
||||
for (auto y = 0; y != imageHeight; ++y) {
|
||||
auto imagePattern = anim::shifted(*imageInts);
|
||||
for (auto x = left; x != right; ++x) {
|
||||
auto source = *frameInts;
|
||||
auto shadowAlpha = _frameAlpha - (source >> 24);
|
||||
*frameInts = anim::unshifted(anim::shifted(source) * 256 + imagePattern * shadowAlpha);
|
||||
++frameInts;
|
||||
}
|
||||
frameInts += frameIntsPerLineAdd;
|
||||
imageInts += imageIntsPerLine;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Ui
|
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
It is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
In addition, as a special exception, the copyright holders give permission
|
||||
to link the code of portions of this program with the OpenSSL library.
|
||||
|
||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "styles/style_widgets.h"
|
||||
|
||||
namespace Ui {
|
||||
|
||||
class PanelAnimation {
|
||||
public:
|
||||
enum class Origin {
|
||||
TopLeft,
|
||||
TopRight,
|
||||
BottomLeft,
|
||||
BottomRight,
|
||||
};
|
||||
PanelAnimation(const style::PanelAnimation &st, Origin origin) : _st(st), _origin(origin) {
|
||||
}
|
||||
|
||||
void setFinalImage(QImage &&finalImage, QRect inner);
|
||||
void setCornerMasks(QImage &&topLeft, QImage &&topRight, QImage &&bottomLeft, QImage &&bottomRight);
|
||||
void setSkipShadow(bool skipShadow);
|
||||
|
||||
void start();
|
||||
const QImage &getFrame(float64 dt, float64 opacity);
|
||||
|
||||
private:
|
||||
void setStartWidth();
|
||||
void setStartHeight();
|
||||
void setStartAlpha();
|
||||
void setStartFadeTop();
|
||||
void createFadeMask();
|
||||
void setWidthDuration();
|
||||
void setHeightDuration();
|
||||
void setAlphaDuration();
|
||||
void setShadow();
|
||||
|
||||
bool started() const {
|
||||
return !_frame.isNull();
|
||||
}
|
||||
|
||||
struct Corner {
|
||||
QImage image;
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
const uchar *bytes = nullptr;
|
||||
int bytesPerPixel = 0;
|
||||
int bytesPerLineAdded = 0;
|
||||
|
||||
bool valid() const {
|
||||
return !image.isNull();
|
||||
}
|
||||
};
|
||||
void setCornerMask(Corner &corner, QImage &&image);
|
||||
void paintCorner(Corner &corner, int left, int top);
|
||||
|
||||
struct Shadow {
|
||||
style::margins extend;
|
||||
QImage left, topLeft, top, topRight, right, bottomRight, bottom, bottomLeft;
|
||||
|
||||
bool valid() const {
|
||||
return !left.isNull();
|
||||
}
|
||||
};
|
||||
QImage cloneImage(const style::icon &source);
|
||||
void paintShadow(int left, int top, int right, int bottom);
|
||||
void paintShadowCorner(int left, int top, const QImage &image);
|
||||
void paintShadowVertical(int left, int top, int bottom, const QImage &image);
|
||||
void paintShadowHorizontal(int left, int right, int top, const QImage &image);
|
||||
|
||||
const style::PanelAnimation &_st;
|
||||
Origin _origin = Origin::TopLeft;
|
||||
|
||||
QImage _finalImage;
|
||||
const uint32 *_finalInts = nullptr;
|
||||
int _finalIntsPerLine = 0;
|
||||
int _finalIntsPerLineAdded = 0;
|
||||
int _finalWidth = 0;
|
||||
int _finalHeight = 0;
|
||||
int _finalInnerLeft = 0;
|
||||
int _finalInnerTop = 0;
|
||||
int _finalInnerRight = 0;
|
||||
int _finalInnerBottom = 0;
|
||||
int _finalInnerWidth = 0;
|
||||
int _finalInnerHeight = 0;
|
||||
|
||||
Shadow _shadow;
|
||||
bool _skipShadow = false;
|
||||
int _startWidth = -1;
|
||||
int _startHeight = -1;
|
||||
int _startAlpha = 0;
|
||||
|
||||
int _startFadeTop = 0;
|
||||
QImage _fadeMask;
|
||||
int _fadeHeight = 0;
|
||||
const uint32 *_fadeInts = nullptr;
|
||||
int _fadeIntsPerLine = 0;
|
||||
|
||||
Corner _topLeft;
|
||||
Corner _topRight;
|
||||
Corner _bottomLeft;
|
||||
Corner _bottomRight;
|
||||
|
||||
float64 _widthDuration = 1.;
|
||||
float64 _heightDuration = 1.;
|
||||
float64 _alphaDuration = 1.;
|
||||
|
||||
QImage _frame;
|
||||
uint32 *_frameInts = nullptr;
|
||||
int _frameAlpha = 0; // recounted each getFrame()
|
||||
int _frameIntsPerLine = 0;
|
||||
int _frameIntsPerLineAdded = 0;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Ui
|
|
@ -20,6 +20,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
#include "styles/style_widgets.h"
|
||||
|
||||
namespace Ui {
|
||||
|
||||
class RectShadow {
|
||||
|
|
|
@ -240,11 +240,16 @@ void FlatInput::updatePlaceholderText() {
|
|||
|
||||
void FlatInput::contextMenuEvent(QContextMenuEvent *e) {
|
||||
if (auto menu = createStandardContextMenu()) {
|
||||
menu->addSeparator();
|
||||
auto action = menu->addAction(QString("test"));
|
||||
action->setMenu(new QMenu(this));
|
||||
action->menu()->addAction(QString("test123"));
|
||||
action->menu()->addAction(QString("test456"));
|
||||
//menu->addSeparator();
|
||||
//auto action = menu->addAction(QString("test"));
|
||||
//action->setMenu(new QMenu(this));
|
||||
//action->menu()->addAction(QString("test123"));
|
||||
//action->menu()->addAction(QString("test456"));
|
||||
//action->menu()->addAction(QString("test678"));
|
||||
//auto second = action->menu()->addAction(QString("test90"));
|
||||
//second->setMenu(new QMenu(this));
|
||||
//second->menu()->addAction(QString("testing111"));
|
||||
//second->menu()->addAction(QString("testing222"));
|
||||
(new Ui::PopupMenu(menu))->popup(e->globalPos());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -90,10 +90,10 @@ void colorizeImage(const QImage &src, QColor c, QImage *outResult, QRect srcRect
|
|||
auto green = (c.green() * initialAlpha) >> 8;
|
||||
auto blue = (c.blue() * initialAlpha) >> 8;
|
||||
auto alpha = (255 * initialAlpha) >> 8;
|
||||
auto pattern = static_cast<uint64>(alpha)
|
||||
| (static_cast<uint64>(red) << 16)
|
||||
| (static_cast<uint64>(green) << 32)
|
||||
| (static_cast<uint64>(blue) << 48);
|
||||
auto pattern = static_cast<uint64>(blue)
|
||||
| (static_cast<uint64>(green) << 16)
|
||||
| (static_cast<uint64>(red) << 32)
|
||||
| (static_cast<uint64>(alpha) << 48);
|
||||
|
||||
auto resultBytesPerPixel = (src.depth() >> 3);
|
||||
auto resultIntsPerPixel = 1;
|
||||
|
@ -113,12 +113,7 @@ void colorizeImage(const QImage &src, QColor c, QImage *outResult, QRect srcRect
|
|||
for (int y = 0; y != height; ++y) {
|
||||
for (int x = 0; x != width; ++x) {
|
||||
auto maskOpacity = static_cast<uint64>(*maskBytes) + 1;
|
||||
auto masked = (pattern * maskOpacity) >> 8;
|
||||
auto alpha = static_cast<uint32>(masked & 0xFF);
|
||||
auto red = static_cast<uint32>((masked >> 16) & 0xFF);
|
||||
auto green = static_cast<uint32>((masked >> 32) & 0xFF);
|
||||
auto blue = static_cast<uint32>((masked >> 48) & 0xFF);
|
||||
*resultInts = blue | (green << 8) | (red << 16) | (alpha << 24);
|
||||
*resultInts = anim::unshifted(pattern * maskOpacity);
|
||||
maskBytes += maskBytesPerPixel;
|
||||
resultInts += resultIntsPerPixel;
|
||||
}
|
||||
|
|
|
@ -72,7 +72,7 @@ QPixmap myGrab(TWidget *target, QRect rect) {
|
|||
result.fill(Qt::transparent);
|
||||
|
||||
target->grabStart();
|
||||
target->render(&result, QPoint(), QRegion(rect), QWidget::DrawChildren | QWidget::IgnoreMask);
|
||||
target->render(&result, QPoint(0, 0), rect, QWidget::DrawChildren | QWidget::IgnoreMask);
|
||||
target->grabFinish();
|
||||
|
||||
return result;
|
||||
|
|
|
@ -23,13 +23,21 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
|||
|
||||
#include "mainwindow.h"
|
||||
#include "ui/scrollarea.h"
|
||||
#include "ui/widgets/shadow.h"
|
||||
#include "profile/profile_members_widget.h"
|
||||
#include "ui/effects/panel_animation.h"
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr float64 kFadeHeight = 1. / 3;
|
||||
constexpr int kFadeAlphaMax = 160;
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace Ui {
|
||||
|
||||
InnerDropdown::InnerDropdown(QWidget *parent, const style::InnerDropdown &st) : TWidget(parent)
|
||||
, _st(st)
|
||||
, _shadow(_st.shadow)
|
||||
, _scroll(this, _st.scroll) {
|
||||
_hideTimer.setSingleShot(true);
|
||||
connect(&_hideTimer, SIGNAL(timeout()), this, SLOT(onHideAnimated()));
|
||||
|
@ -99,35 +107,39 @@ void InnerDropdown::onScroll() {
|
|||
void InnerDropdown::paintEvent(QPaintEvent *e) {
|
||||
Painter p(this);
|
||||
|
||||
if (!_cache.isNull()) {
|
||||
bool animating = _a_appearance.animating(getms());
|
||||
if (animating) {
|
||||
p.setOpacity(_a_appearance.current(_hiding ? 0. : 1.));
|
||||
} else if (_hiding || isHidden()) {
|
||||
hideFinished();
|
||||
return;
|
||||
auto ms = getms();
|
||||
if (_a_show.animating(ms)) {
|
||||
if (auto opacity = _a_opacity.current(ms, _hiding ? 0. : 1.)) {
|
||||
p.drawImage(0, 0, _showAnimation->getFrame(_a_show.current(1.), opacity));
|
||||
}
|
||||
return;
|
||||
} else if (_a_opacity.animating(ms)) {
|
||||
p.setOpacity(_a_opacity.current(0.));
|
||||
p.drawPixmap(0, 0, _cache);
|
||||
if (!animating) {
|
||||
showChildren();
|
||||
_cache = QPixmap();
|
||||
}
|
||||
return;
|
||||
} else if (_hiding || isHidden()) {
|
||||
hideFinished();
|
||||
return;
|
||||
} else if (_showAnimation) {
|
||||
p.drawImage(0, 0, _showAnimation->getFrame(1., 1.));
|
||||
_showAnimation.reset();
|
||||
showChildren();
|
||||
return;
|
||||
}
|
||||
|
||||
// draw shadow
|
||||
auto shadowedRect = rect().marginsRemoved(_st.padding);
|
||||
_shadow.paint(p, shadowedRect, _st.shadowShift);
|
||||
p.fillRect(shadowedRect, st::windowBg);
|
||||
auto inner = rect().marginsRemoved(_st.padding);
|
||||
Shadow::paint(p, inner, width(), _st.shadow);
|
||||
App::roundRect(p, inner, _st.bg, ImageRoundRadius::Small);
|
||||
}
|
||||
|
||||
void InnerDropdown::enterEvent(QEvent *e) {
|
||||
showAnimated();
|
||||
showAnimated(_origin);
|
||||
return TWidget::enterEvent(e);
|
||||
}
|
||||
|
||||
void InnerDropdown::leaveEvent(QEvent *e) {
|
||||
if (_a_appearance.animating(getms())) {
|
||||
auto ms = getms();
|
||||
if (_a_show.animating(ms) || _a_opacity.animating(ms)) {
|
||||
hideAnimated();
|
||||
} else {
|
||||
_hideTimer.start(300);
|
||||
|
@ -136,18 +148,24 @@ void InnerDropdown::leaveEvent(QEvent *e) {
|
|||
}
|
||||
|
||||
void InnerDropdown::otherEnter() {
|
||||
showAnimated();
|
||||
showAnimated(_origin);
|
||||
}
|
||||
|
||||
void InnerDropdown::otherLeave() {
|
||||
if (_a_appearance.animating(getms())) {
|
||||
auto ms = getms();
|
||||
if (_a_show.animating(ms) || _a_opacity.animating(ms)) {
|
||||
hideAnimated();
|
||||
} else {
|
||||
_hideTimer.start(0);
|
||||
}
|
||||
}
|
||||
|
||||
void InnerDropdown::showAnimated() {
|
||||
void InnerDropdown::setOrigin(PanelAnimation::Origin origin) {
|
||||
_origin = origin;
|
||||
}
|
||||
|
||||
void InnerDropdown::showAnimated(PanelAnimation::Origin origin) {
|
||||
setOrigin(origin);
|
||||
_hideTimer.stop();
|
||||
showStarted();
|
||||
}
|
||||
|
@ -160,8 +178,7 @@ void InnerDropdown::hideAnimated(HideOption option) {
|
|||
if (_hiding) return;
|
||||
|
||||
_hideTimer.stop();
|
||||
_hiding = true;
|
||||
startAnimation();
|
||||
startOpacityAnimation(true);
|
||||
}
|
||||
|
||||
void InnerDropdown::hideFast() {
|
||||
|
@ -169,22 +186,12 @@ void InnerDropdown::hideFast() {
|
|||
|
||||
_hideTimer.stop();
|
||||
_hiding = false;
|
||||
_a_appearance.finish();
|
||||
_a_opacity.finish();
|
||||
hideFinished();
|
||||
}
|
||||
|
||||
void InnerDropdown::startAnimation() {
|
||||
auto from = _hiding ? 1. : 0.;
|
||||
auto to = _hiding ? 0. : 1.;
|
||||
if (!_a_appearance.animating()) {
|
||||
showChildren();
|
||||
_cache = myGrab(this);
|
||||
}
|
||||
hideChildren();
|
||||
_a_appearance.start([this] { repaintCallback(); }, from, to, _st.duration);
|
||||
}
|
||||
|
||||
void InnerDropdown::hideFinished() {
|
||||
_a_show.finish();
|
||||
_cache = QPixmap();
|
||||
_ignoreShowEvents = false;
|
||||
if (!isHidden()) {
|
||||
|
@ -195,25 +202,83 @@ void InnerDropdown::hideFinished() {
|
|||
}
|
||||
}
|
||||
|
||||
void InnerDropdown::prepareCache() {
|
||||
if (_a_opacity.animating()) return;
|
||||
|
||||
auto showAnimation = base::take(_a_show);
|
||||
auto showAnimationData = base::take(_showAnimation);
|
||||
showChildren();
|
||||
_cache = myGrab(this);
|
||||
_showAnimation = base::take(showAnimationData);
|
||||
_a_show = base::take(showAnimation);
|
||||
}
|
||||
|
||||
void InnerDropdown::startOpacityAnimation(bool hiding) {
|
||||
_hiding = false;
|
||||
prepareCache();
|
||||
_hiding = hiding;
|
||||
hideChildren();
|
||||
_a_opacity.start([this] { opacityAnimationCallback(); }, _hiding ? 1. : 0., _hiding ? 0. : 1., _st.duration);
|
||||
}
|
||||
|
||||
void InnerDropdown::showStarted() {
|
||||
if (_ignoreShowEvents) return;
|
||||
if (isHidden()) {
|
||||
show();
|
||||
startShowAnimation();
|
||||
return;
|
||||
} else if (!_hiding) {
|
||||
return;
|
||||
}
|
||||
_hiding = false;
|
||||
startAnimation();
|
||||
startOpacityAnimation(false);
|
||||
}
|
||||
|
||||
void InnerDropdown::repaintCallback() {
|
||||
void InnerDropdown::startShowAnimation() {
|
||||
if (!_a_show.animating()) {
|
||||
auto opacityAnimation = base::take(_a_opacity);
|
||||
showChildren();
|
||||
auto cache = grabForPanelAnimation();
|
||||
_a_opacity = base::take(opacityAnimation);
|
||||
|
||||
_showAnimation = std_::make_unique<PanelAnimation>(_st.animation, _origin);
|
||||
_showAnimation->setFinalImage(std_::move(cache), rect().marginsRemoved(_st.padding));
|
||||
auto corners = App::cornersMask(ImageRoundRadius::Small);
|
||||
_showAnimation->setCornerMasks(QImage(*corners[0]), QImage(*corners[1]), QImage(*corners[2]), QImage(*corners[3]));
|
||||
_showAnimation->start();
|
||||
}
|
||||
hideChildren();
|
||||
_a_show.start([this] { showAnimationCallback(); }, 0., 1., _st.showDuration);
|
||||
}
|
||||
|
||||
QImage InnerDropdown::grabForPanelAnimation() {
|
||||
myEnsureResized(this);
|
||||
auto result = QImage(size() * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied);
|
||||
result.setDevicePixelRatio(cRetinaFactor());
|
||||
result.fill(Qt::transparent);
|
||||
{
|
||||
Painter p(&result);
|
||||
App::roundRect(p, rect().marginsRemoved(_st.padding), _st.bg, ImageRoundRadius::Small);
|
||||
for (auto child : children()) {
|
||||
if (auto widget = qobject_cast<QWidget*>(child)) {
|
||||
widget->render(&p, widget->pos(), widget->rect(), QWidget::DrawChildren | QWidget::IgnoreMask);
|
||||
}
|
||||
}
|
||||
}
|
||||
return std_::move(result);
|
||||
}
|
||||
|
||||
void InnerDropdown::opacityAnimationCallback() {
|
||||
update();
|
||||
if (!_a_appearance.animating() && _hiding) {
|
||||
if (_hiding && !_a_opacity.animating()) {
|
||||
_hiding = false;
|
||||
hideFinished();
|
||||
}
|
||||
}
|
||||
|
||||
void InnerDropdown::showAnimationCallback() {
|
||||
update();
|
||||
}
|
||||
|
||||
bool InnerDropdown::eventFilter(QObject *obj, QEvent *e) {
|
||||
if (e->type() == QEvent::Enter) {
|
||||
otherEnter();
|
||||
|
|
|
@ -20,8 +20,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
#include "ui/effects/rect_shadow.h"
|
||||
#include "styles/style_widgets.h"
|
||||
#include "ui/effects/panel_animation.h"
|
||||
|
||||
class ScrollArea;
|
||||
|
||||
|
@ -36,7 +36,7 @@ public:
|
|||
void setOwnedWidget(TWidget *widget);
|
||||
|
||||
bool overlaps(const QRect &globalRect) {
|
||||
if (isHidden() || _a_appearance.animating()) return false;
|
||||
if (isHidden() || _a_show.animating() || _a_opacity.animating()) return false;
|
||||
|
||||
return rect().marginsRemoved(_st.padding).contains(QRect(mapFromGlobal(globalRect.topLeft()), globalRect.size()));
|
||||
}
|
||||
|
@ -46,7 +46,6 @@ public:
|
|||
|
||||
void otherEnter();
|
||||
void otherLeave();
|
||||
void showFast();
|
||||
void hideFast();
|
||||
|
||||
void setHiddenCallback(base::lambda_unique<void()> callback) {
|
||||
|
@ -54,10 +53,11 @@ public:
|
|||
}
|
||||
|
||||
bool isHiding() const {
|
||||
return _hiding && _a_appearance.animating();
|
||||
return _hiding && _a_opacity.animating();
|
||||
}
|
||||
|
||||
void showAnimated();
|
||||
void setOrigin(PanelAnimation::Origin origin);
|
||||
void showAnimated(PanelAnimation::Origin origin);
|
||||
enum class HideOption {
|
||||
Default,
|
||||
IgnoreShow,
|
||||
|
@ -84,28 +84,34 @@ private slots:
|
|||
}
|
||||
|
||||
private:
|
||||
QImage grabForPanelAnimation();
|
||||
void startShowAnimation();
|
||||
void startOpacityAnimation(bool hiding);
|
||||
void prepareCache();
|
||||
|
||||
class Container;
|
||||
void repaintCallback();
|
||||
void showAnimationCallback();
|
||||
void opacityAnimationCallback();
|
||||
|
||||
void hideFinished();
|
||||
void showStarted();
|
||||
|
||||
void startAnimation();
|
||||
|
||||
void updateHeight();
|
||||
|
||||
const style::InnerDropdown &_st;
|
||||
|
||||
bool _hiding = false;
|
||||
PanelAnimation::Origin _origin = PanelAnimation::Origin::TopLeft;
|
||||
std_::unique_ptr<PanelAnimation> _showAnimation;
|
||||
FloatAnimation _a_show;
|
||||
|
||||
bool _hiding = false;
|
||||
QPixmap _cache;
|
||||
FloatAnimation _a_appearance;
|
||||
FloatAnimation _a_opacity;
|
||||
|
||||
QTimer _hideTimer;
|
||||
bool _ignoreShowEvents = false;
|
||||
base::lambda_unique<void()> _hiddenCallback;
|
||||
|
||||
RectShadow _shadow;
|
||||
ChildWidget<ScrollArea> _scroll;
|
||||
|
||||
int _maxHeight = 0;
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "stdafx.h"
|
||||
#include "ui/widgets/popup_menu.h"
|
||||
|
||||
#include "ui/widgets/shadow.h"
|
||||
#include "pspecific.h"
|
||||
#include "application.h"
|
||||
#include "lang.h"
|
||||
|
@ -26,19 +27,13 @@ namespace Ui {
|
|||
|
||||
PopupMenu::PopupMenu(const style::PopupMenu &st) : TWidget(nullptr)
|
||||
, _st(st)
|
||||
, _menu(this, _st.menu)
|
||||
, _shadow(_st.shadow)
|
||||
, a_opacity(1)
|
||||
, _a_hide(animation(this, &PopupMenu::step_hide)) {
|
||||
, _menu(this, _st.menu) {
|
||||
init();
|
||||
}
|
||||
|
||||
PopupMenu::PopupMenu(QMenu *menu, const style::PopupMenu &st) : TWidget(nullptr)
|
||||
, _st(st)
|
||||
, _menu(this, menu, _st.menu)
|
||||
, _shadow(_st.shadow)
|
||||
, a_opacity(1)
|
||||
, _a_hide(animation(this, &PopupMenu::step_hide)) {
|
||||
, _menu(this, menu, _st.menu) {
|
||||
init();
|
||||
|
||||
for (auto action : actions()) {
|
||||
|
@ -50,8 +45,6 @@ PopupMenu::PopupMenu(QMenu *menu, const style::PopupMenu &st) : TWidget(nullptr)
|
|||
}
|
||||
|
||||
void PopupMenu::init() {
|
||||
_padding = _shadow.getDimensions(_st.shadowShift);
|
||||
|
||||
_menu->setResizedCallback([this] { handleMenuResize(); });
|
||||
_menu->setActivatedCallback([this](QAction *action, int actionTop, TriggeredSource source) {
|
||||
handleActivated(action, actionTop, source);
|
||||
|
@ -63,8 +56,7 @@ void PopupMenu::init() {
|
|||
_menu->setMouseMoveDelegate([this](QPoint globalPosition) { handleMouseMove(globalPosition); });
|
||||
_menu->setMousePressDelegate([this](QPoint globalPosition) { handleMousePress(globalPosition); });
|
||||
|
||||
_menu->moveToLeft(_padding.left(), _padding.top());
|
||||
handleMenuResize();
|
||||
handleCompositingUpdate();
|
||||
|
||||
setWindowFlags(Qt::WindowFlags(Qt::FramelessWindowHint) | Qt::BypassWindowManagerHint | Qt::Popup | Qt::NoDropShadowWindowHint);
|
||||
setMouseTracking(true);
|
||||
|
@ -75,9 +67,17 @@ void PopupMenu::init() {
|
|||
setAttribute(Qt::WA_TranslucentBackground, true);
|
||||
}
|
||||
|
||||
void PopupMenu::handleCompositingUpdate() {
|
||||
_padding = _compositing ? _st.shadow.extend : style::margins(st::lineWidth, st::lineWidth, st::lineWidth, st::lineWidth);
|
||||
_menu->moveToLeft(_padding.left() + _st.scrollPadding.left(), _padding.top() + _st.scrollPadding.top());
|
||||
handleMenuResize();
|
||||
}
|
||||
|
||||
void PopupMenu::handleMenuResize() {
|
||||
resize(_padding.left() + _menu->width() + _padding.right(), _padding.top() + _menu->height() + _padding.bottom());
|
||||
_inner = QRect(_padding.left(), _padding.top(), width() - _padding.left() - _padding.right(), height() - _padding.top() - _padding.bottom());
|
||||
auto newWidth = _padding.left() + _st.scrollPadding.left() + _menu->width() + _st.scrollPadding.right() + _padding.right();
|
||||
auto newHeight = _padding.top() + _st.scrollPadding.top() + _menu->height() + _st.scrollPadding.bottom() + _padding.bottom();
|
||||
resize(newWidth, newHeight);
|
||||
_inner = rect().marginsRemoved(_padding);
|
||||
}
|
||||
|
||||
QAction *PopupMenu::addAction(const QString &text, const QObject *receiver, const char* member, const style::icon *icon, const style::icon *iconOver) {
|
||||
|
@ -106,21 +106,36 @@ PopupMenu::Actions &PopupMenu::actions() {
|
|||
void PopupMenu::paintEvent(QPaintEvent *e) {
|
||||
Painter p(this);
|
||||
|
||||
auto clip = e->rect();
|
||||
p.setClipRect(clip);
|
||||
auto compositionMode = p.compositionMode();
|
||||
p.setCompositionMode(QPainter::CompositionMode_Source);
|
||||
if (_a_hide.animating()) {
|
||||
p.setOpacity(a_opacity.current());
|
||||
auto ms = getms();
|
||||
if (_a_show.animating(ms)) {
|
||||
if (auto opacity = _a_opacity.current(ms, _hiding ? 0. : 1.)) {
|
||||
p.drawImage(0, 0, _showAnimation->getFrame(_a_show.current(1.), opacity));
|
||||
}
|
||||
} else if (_a_opacity.animating(ms)) {
|
||||
p.setOpacity(_a_opacity.current(0.));
|
||||
p.drawPixmap(0, 0, _cache);
|
||||
return;
|
||||
} else if (_hiding || isHidden()) {
|
||||
hideFinished();
|
||||
} else if (_showAnimation) {
|
||||
p.drawImage(0, 0, _showAnimation->getFrame(1., 1.));
|
||||
_showAnimation.reset();
|
||||
showChildren();
|
||||
} else {
|
||||
paintBg(p);
|
||||
}
|
||||
}
|
||||
|
||||
// This is the minimal alpha value that allowed mouse tracking in OS X.
|
||||
p.fillRect(clip, QColor(255, 255, 255, 13));
|
||||
p.setCompositionMode(compositionMode);
|
||||
|
||||
_shadow.paint(p, _inner, _st.shadowShift);
|
||||
void PopupMenu::paintBg(Painter &p) {
|
||||
if (_compositing) {
|
||||
Shadow::paint(p, _inner, width(), _st.shadow);
|
||||
App::roundRect(p, _inner, _st.menu.itemBg, ImageRoundRadius::Small);
|
||||
} else {
|
||||
p.fillRect(0, 0, width() - _padding.right(), _padding.top(), _st.shadow.fallback);
|
||||
p.fillRect(width() - _padding.right(), 0, _padding.right(), height() - _padding.bottom(), _st.shadow.fallback);
|
||||
p.fillRect(_padding.left(), height() - _padding.bottom(), width() - _padding.left(), _padding.bottom(), _st.shadow.fallback);
|
||||
p.fillRect(0, _padding.top(), _padding.left(), height() - _padding.top(), _st.shadow.fallback);
|
||||
p.fillRect(_inner, _st.menu.itemBg);
|
||||
}
|
||||
}
|
||||
|
||||
void PopupMenu::handleActivated(QAction *action, int actionTop, TriggeredSource source) {
|
||||
|
@ -225,21 +240,13 @@ void PopupMenu::hideEvent(QHideEvent *e) {
|
|||
|
||||
void PopupMenu::hideMenu(bool fast) {
|
||||
if (isHidden()) return;
|
||||
if (_parent && !_a_hide.animating()) {
|
||||
if (_parent && !_a_opacity.animating()) {
|
||||
_parent->childHiding(this);
|
||||
}
|
||||
if (fast) {
|
||||
if (_a_hide.animating()) {
|
||||
_a_hide.stop();
|
||||
}
|
||||
a_opacity = anim::fvalue(0, 0);
|
||||
hideFinish();
|
||||
hideFast();
|
||||
} else {
|
||||
if (!_a_hide.animating()) {
|
||||
_cache = myGrab(this);
|
||||
a_opacity.start(0);
|
||||
_a_hide.start();
|
||||
}
|
||||
hideAnimated();
|
||||
if (_parent) {
|
||||
_parent->hideMenu();
|
||||
}
|
||||
|
@ -255,20 +262,134 @@ void PopupMenu::childHiding(PopupMenu *child) {
|
|||
}
|
||||
}
|
||||
|
||||
void PopupMenu::hideFinish() {
|
||||
hide();
|
||||
void PopupMenu::setOrigin(PanelAnimation::Origin origin) {
|
||||
_origin = origin;
|
||||
}
|
||||
|
||||
void PopupMenu::step_hide(float64 ms, bool timer) {
|
||||
float64 dt = ms / _st.duration;
|
||||
if (dt >= 1) {
|
||||
_a_hide.stop();
|
||||
a_opacity.finish();
|
||||
hideFinish();
|
||||
} else {
|
||||
a_opacity.update(dt, anim::linear);
|
||||
void PopupMenu::showAnimated(PanelAnimation::Origin origin) {
|
||||
setOrigin(origin);
|
||||
showStarted();
|
||||
}
|
||||
|
||||
void PopupMenu::hideAnimated() {
|
||||
if (isHidden()) return;
|
||||
if (_hiding) return;
|
||||
|
||||
startOpacityAnimation(true);
|
||||
}
|
||||
|
||||
void PopupMenu::hideFast() {
|
||||
if (isHidden()) return;
|
||||
|
||||
_hiding = false;
|
||||
_a_opacity.finish();
|
||||
hideFinished();
|
||||
}
|
||||
|
||||
void PopupMenu::hideFinished() {
|
||||
_a_show.finish();
|
||||
_cache = QPixmap();
|
||||
if (!isHidden()) {
|
||||
hide();
|
||||
}
|
||||
if (timer) update();
|
||||
}
|
||||
|
||||
void PopupMenu::prepareCache() {
|
||||
if (_a_opacity.animating()) return;
|
||||
|
||||
auto showAnimation = base::take(_a_show);
|
||||
auto showAnimationData = base::take(_showAnimation);
|
||||
showChildren();
|
||||
_cache = myGrab(this);
|
||||
_showAnimation = base::take(showAnimationData);
|
||||
_a_show = base::take(showAnimation);
|
||||
}
|
||||
|
||||
void PopupMenu::startOpacityAnimation(bool hiding) {
|
||||
_hiding = false;
|
||||
if (!_compositing) {
|
||||
_a_opacity.finish();
|
||||
if (hiding) {
|
||||
hideFinished();
|
||||
} else {
|
||||
update();
|
||||
}
|
||||
return;
|
||||
}
|
||||
prepareCache();
|
||||
_hiding = hiding;
|
||||
hideChildren();
|
||||
_a_opacity.start([this] { opacityAnimationCallback(); }, _hiding ? 1. : 0., _hiding ? 0. : 1., _st.duration);
|
||||
}
|
||||
|
||||
void PopupMenu::showStarted() {
|
||||
if (isHidden()) {
|
||||
show();
|
||||
startShowAnimation();
|
||||
return;
|
||||
} else if (!_hiding) {
|
||||
return;
|
||||
}
|
||||
startOpacityAnimation(false);
|
||||
}
|
||||
|
||||
void PopupMenu::startShowAnimation() {
|
||||
if (!_compositing) {
|
||||
_a_show.finish();
|
||||
update();
|
||||
return;
|
||||
}
|
||||
if (!_a_show.animating()) {
|
||||
auto opacityAnimation = base::take(_a_opacity);
|
||||
showChildren();
|
||||
auto cache = grabForPanelAnimation();
|
||||
_a_opacity = base::take(opacityAnimation);
|
||||
|
||||
_showAnimation = std_::make_unique<PanelAnimation>(_st.animation, _origin);
|
||||
_showAnimation->setFinalImage(std_::move(cache), _inner);
|
||||
if (_compositing) {
|
||||
auto corners = App::cornersMask(ImageRoundRadius::Small);
|
||||
_showAnimation->setCornerMasks(QImage(*corners[0]), QImage(*corners[1]), QImage(*corners[2]), QImage(*corners[3]));
|
||||
} else {
|
||||
_showAnimation->setSkipShadow(true);
|
||||
}
|
||||
_showAnimation->start();
|
||||
}
|
||||
hideChildren();
|
||||
_a_show.start([this] { showAnimationCallback(); }, 0., 1., _st.showDuration);
|
||||
}
|
||||
|
||||
void PopupMenu::opacityAnimationCallback() {
|
||||
update();
|
||||
if (_hiding && !_a_opacity.animating()) {
|
||||
_hiding = false;
|
||||
hideFinished();
|
||||
}
|
||||
}
|
||||
|
||||
void PopupMenu::showAnimationCallback() {
|
||||
update();
|
||||
}
|
||||
|
||||
QImage PopupMenu::grabForPanelAnimation() {
|
||||
myEnsureResized(this);
|
||||
auto result = QImage(size() * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied);
|
||||
result.setDevicePixelRatio(cRetinaFactor());
|
||||
result.fill(Qt::transparent);
|
||||
{
|
||||
Painter p(&result);
|
||||
if (_compositing) {
|
||||
App::roundRect(p, _inner, _st.menu.itemBg, ImageRoundRadius::Small);
|
||||
} else {
|
||||
p.fillRect(_inner, _st.menu.itemBg);
|
||||
}
|
||||
for (auto child : children()) {
|
||||
if (auto widget = qobject_cast<QWidget*>(child)) {
|
||||
widget->render(&p, widget->pos(), widget->rect(), QWidget::DrawChildren | QWidget::IgnoreMask);
|
||||
}
|
||||
}
|
||||
}
|
||||
return std_::move(result);
|
||||
}
|
||||
|
||||
void PopupMenu::deleteOnHide(bool del) {
|
||||
|
@ -282,8 +403,15 @@ void PopupMenu::popup(const QPoint &p) {
|
|||
void PopupMenu::showMenu(const QPoint &p, PopupMenu *parent, TriggeredSource source) {
|
||||
_parent = parent;
|
||||
|
||||
QPoint w = p - QPoint(0, _padding.top());
|
||||
QRect r = Sandbox::screenGeometry(p);
|
||||
auto origin = PanelAnimation::Origin::TopLeft;
|
||||
auto w = p - QPoint(0, _padding.top());
|
||||
auto r = Sandbox::screenGeometry(p);
|
||||
#ifdef Q_OS_LINUX
|
||||
_compositing = QX11Info::isCompositingManagerRunning(QApplication::desktop()->screenNumber(p));
|
||||
#else // Q_OS_LINUX
|
||||
_compositing = true;
|
||||
#endif // Q_OS_LINUX
|
||||
handleCompositingUpdate();
|
||||
if (rtl()) {
|
||||
if (w.x() - width() < r.x() - _padding.left()) {
|
||||
if (_parent && w.x() + _parent->width() - _padding.left() - _padding.right() + width() - _padding.right() <= r.x() + r.width()) {
|
||||
|
@ -299,8 +427,9 @@ void PopupMenu::showMenu(const QPoint &p, PopupMenu *parent, TriggeredSource sou
|
|||
if (_parent && w.x() - _parent->width() + _padding.left() + _padding.right() - width() + _padding.right() >= r.x() - _padding.left()) {
|
||||
w.setX(w.x() + _padding.left() + _padding.right() - _parent->width() - width() + _padding.left() + _padding.right());
|
||||
} else {
|
||||
w.setX(r.x() + r.width() - width() + _padding.right());
|
||||
w.setX(p.x() - width() + _padding.right());
|
||||
}
|
||||
origin = PanelAnimation::Origin::TopRight;
|
||||
}
|
||||
}
|
||||
if (w.y() + height() - _padding.bottom() > r.y() + r.height()) {
|
||||
|
@ -308,13 +437,18 @@ void PopupMenu::showMenu(const QPoint &p, PopupMenu *parent, TriggeredSource sou
|
|||
w.setY(r.y() + r.height() - height() + _padding.bottom());
|
||||
} else {
|
||||
w.setY(p.y() - height() + _padding.bottom());
|
||||
origin = (origin == PanelAnimation::Origin::TopRight) ? PanelAnimation::Origin::BottomRight : PanelAnimation::Origin::BottomLeft;
|
||||
}
|
||||
}
|
||||
if (w.x() < r.x()) {
|
||||
w.setX(r.x());
|
||||
}
|
||||
if (w.y() < r.y()) {
|
||||
w.setY(r.y());
|
||||
}
|
||||
move(w);
|
||||
|
||||
setOrigin(origin);
|
||||
_menu->setShowSource(source);
|
||||
|
||||
psUpdateOverlayed(this);
|
||||
|
@ -323,11 +457,7 @@ void PopupMenu::showMenu(const QPoint &p, PopupMenu *parent, TriggeredSource sou
|
|||
windowHandle()->requestActivate();
|
||||
activateWindow();
|
||||
|
||||
if (_a_hide.animating()) {
|
||||
_a_hide.stop();
|
||||
_cache = QPixmap();
|
||||
}
|
||||
a_opacity = anim::fvalue(1, 1);
|
||||
startShowAnimation();
|
||||
}
|
||||
|
||||
PopupMenu::~PopupMenu() {
|
||||
|
|
|
@ -18,8 +18,8 @@
|
|||
#pragma once
|
||||
|
||||
#include "styles/style_widgets.h"
|
||||
#include "ui/effects/rect_shadow.h"
|
||||
#include "ui/widgets/menu.h"
|
||||
#include "ui/effects/panel_animation.h"
|
||||
|
||||
namespace Ui {
|
||||
|
||||
|
@ -58,14 +58,28 @@ protected:
|
|||
}
|
||||
|
||||
private:
|
||||
void paintBg(Painter &p);
|
||||
void hideFast();
|
||||
void setOrigin(PanelAnimation::Origin origin);
|
||||
void showAnimated(PanelAnimation::Origin origin);
|
||||
void hideAnimated();
|
||||
|
||||
QImage grabForPanelAnimation();
|
||||
void startShowAnimation();
|
||||
void startOpacityAnimation(bool hiding);
|
||||
void prepareCache();
|
||||
void childHiding(PopupMenu *child);
|
||||
|
||||
void step_hide(float64 ms, bool timer);
|
||||
void showAnimationCallback();
|
||||
void opacityAnimationCallback();
|
||||
|
||||
void init();
|
||||
void hideFinish();
|
||||
|
||||
void hideFinished();
|
||||
void showStarted();
|
||||
|
||||
using TriggeredSource = Ui::Menu::TriggeredSource;
|
||||
void handleCompositingUpdate();
|
||||
void handleMenuResize();
|
||||
void handleActivated(QAction *action, int actionTop, TriggeredSource source);
|
||||
void handleTriggered(QAction *action, int actionTop, TriggeredSource source);
|
||||
|
@ -97,12 +111,16 @@ private:
|
|||
QRect _inner;
|
||||
style::margins _padding;
|
||||
|
||||
Ui::RectShadow _shadow;
|
||||
SubmenuPointer _activeSubmenu;
|
||||
|
||||
PanelAnimation::Origin _origin = PanelAnimation::Origin::TopLeft;
|
||||
std_::unique_ptr<PanelAnimation> _showAnimation;
|
||||
FloatAnimation _a_show;
|
||||
|
||||
bool _compositing = true;
|
||||
bool _hiding = false;
|
||||
QPixmap _cache;
|
||||
anim::fvalue a_opacity;
|
||||
Animation _a_hide;
|
||||
FloatAnimation _a_opacity;
|
||||
|
||||
bool _deleteOnHide = true;
|
||||
bool _triggering = false;
|
||||
|
|
|
@ -53,4 +53,59 @@ void ToggleableShadow::paintEvent(QPaintEvent *e) {
|
|||
p.fillRect(e->rect(), _color);
|
||||
}
|
||||
|
||||
void Shadow::paint(Painter &p, const QRect &box, int outerWidth, const style::Shadow &st, Sides sides) {
|
||||
auto left = (sides & Side::Left);
|
||||
auto top = (sides & Side::Top);
|
||||
auto right = (sides & Side::Right);
|
||||
auto bottom = (sides & Side::Bottom);
|
||||
if (left) {
|
||||
auto from = box.y();
|
||||
auto to = from + box.height();
|
||||
if (top && !st.topLeft.empty()) {
|
||||
st.topLeft.paint(p, box.x() - st.extend.left(), box.y() - st.extend.top(), outerWidth);
|
||||
from += st.topLeft.height() - st.extend.top();
|
||||
}
|
||||
if (bottom && !st.bottomLeft.empty()) {
|
||||
st.bottomLeft.paint(p, box.x() - st.extend.left(), box.y() + box.height() + st.extend.bottom() - st.bottomLeft.height(), outerWidth);
|
||||
to -= st.bottomLeft.height() - st.extend.bottom();
|
||||
}
|
||||
if (to > from && !st.left.empty()) {
|
||||
st.left.fill(p, rtlrect(box.x() - st.extend.left(), from, st.left.width(), to - from, outerWidth));
|
||||
}
|
||||
}
|
||||
if (right) {
|
||||
auto from = box.y();
|
||||
auto to = from + box.height();
|
||||
if (top && !st.topRight.empty()) {
|
||||
st.topRight.paint(p, box.x() + box.width() + st.extend.right() - st.topRight.width(), box.y() - st.extend.top(), outerWidth);
|
||||
from += st.topRight.height() - st.extend.top();
|
||||
}
|
||||
if (bottom && !st.bottomRight.empty()) {
|
||||
st.bottomRight.paint(p, box.x() + box.width() + st.extend.right() - st.bottomRight.width(), box.y() + box.height() + st.extend.bottom() - st.bottomRight.height(), outerWidth);
|
||||
to -= st.bottomRight.height() - st.extend.bottom();
|
||||
}
|
||||
if (to > from && !st.right.empty()) {
|
||||
st.right.fill(p, rtlrect(box.x() + box.width() + st.extend.right() - st.right.width(), from, st.right.width(), to - from, outerWidth));
|
||||
}
|
||||
}
|
||||
if (top && !st.top.empty()) {
|
||||
auto from = box.x();
|
||||
auto to = from + box.width();
|
||||
if (left && !st.topLeft.empty()) from += st.topLeft.width() - st.extend.left();
|
||||
if (right && !st.topRight.empty()) to -= st.topRight.width() - st.extend.right();
|
||||
if (to > from) {
|
||||
st.top.fill(p, rtlrect(from, box.y() - st.extend.top(), to - from, st.top.height(), outerWidth));
|
||||
}
|
||||
}
|
||||
if (bottom && !st.bottom.empty()) {
|
||||
auto from = box.x();
|
||||
auto to = from + box.width();
|
||||
if (left && !st.bottomLeft.empty()) from += st.bottomLeft.width() - st.extend.left();
|
||||
if (right && !st.bottomRight.empty()) to -= st.bottomRight.width() - st.extend.right();
|
||||
if (to > from) {
|
||||
st.bottom.fill(p, rtlrect(from, box.y() + box.height() + st.extend.bottom() - st.bottom.height(), to - from, st.bottom.height(), outerWidth));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Ui
|
||||
|
|
|
@ -1,5 +1,24 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
It is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "styles/style_widgets.h"
|
||||
|
||||
namespace Ui {
|
||||
|
||||
class PlainShadow : public TWidget {
|
||||
|
@ -63,4 +82,35 @@ private:
|
|||
|
||||
};
|
||||
|
||||
class Shadow : public TWidget {
|
||||
public:
|
||||
enum class Side {
|
||||
Left = 0x01,
|
||||
Top = 0x02,
|
||||
Right = 0x04,
|
||||
Bottom = 0x08,
|
||||
};
|
||||
Q_DECLARE_FLAGS(Sides, Side);
|
||||
Q_DECLARE_FRIEND_OPERATORS_FOR_FLAGS(Sides);
|
||||
|
||||
Shadow(QWidget *parent, const style::Shadow &st, Sides sides = Side::Left | Side::Top | Side::Right | Side::Bottom) : TWidget(parent)
|
||||
, _st(st)
|
||||
, _sides(sides) {
|
||||
}
|
||||
|
||||
static void paint(Painter &p, const QRect &box, int outerWidth, const style::Shadow &st, Sides sides = Side::Left | Side::Top | Side::Right | Side::Bottom);
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *e) override {
|
||||
Painter p(this);
|
||||
paint(p, rect().marginsRemoved(_st.extend), width(), _st, _sides);
|
||||
}
|
||||
|
||||
private:
|
||||
const style::Shadow &_st;
|
||||
Sides _sides;
|
||||
|
||||
};
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(Shadow::Sides);
|
||||
|
||||
} // namespace Ui
|
||||
|
|
|
@ -40,6 +40,19 @@ IconButton {
|
|||
duration: int;
|
||||
}
|
||||
|
||||
Shadow {
|
||||
left: icon;
|
||||
topLeft: icon;
|
||||
top: icon;
|
||||
topRight: icon;
|
||||
right: icon;
|
||||
bottomRight: icon;
|
||||
bottom: icon;
|
||||
bottomLeft: icon;
|
||||
extend: margins;
|
||||
fallback: color;
|
||||
}
|
||||
|
||||
MaskButton {
|
||||
width: pixels;
|
||||
height: pixels;
|
||||
|
@ -148,23 +161,41 @@ Menu {
|
|||
widthMax: pixels;
|
||||
}
|
||||
|
||||
PanelAnimation {
|
||||
startWidth: double;
|
||||
widthDuration: double;
|
||||
startHeight: double;
|
||||
heightDuration: double;
|
||||
startOpacity: double;
|
||||
opacityDuration: double;
|
||||
startFadeTop: double;
|
||||
fadeHeight: double;
|
||||
fadeOpacity: double;
|
||||
fadeBg: color;
|
||||
shadow: Shadow;
|
||||
}
|
||||
|
||||
PopupMenu {
|
||||
shadow: icon;
|
||||
shadowShift: pixels;
|
||||
shadow: Shadow;
|
||||
scrollPadding: margins;
|
||||
animation: PanelAnimation;
|
||||
|
||||
menu: Menu;
|
||||
|
||||
duration: int;
|
||||
showDuration: int;
|
||||
}
|
||||
|
||||
InnerDropdown {
|
||||
padding: margins;
|
||||
shadow: icon;
|
||||
shadowShift: pixels;
|
||||
shadow: Shadow;
|
||||
animation: PanelAnimation;
|
||||
|
||||
duration: int;
|
||||
showDuration: int;
|
||||
width: pixels;
|
||||
|
||||
bg: color;
|
||||
scroll: flatScroll;
|
||||
scrollMargin: margins;
|
||||
scrollPadding: margins;
|
||||
|
@ -209,9 +240,39 @@ discreteSliderLabelFont: normalFont;
|
|||
discreteSliderLabelFg: #1485c2;
|
||||
discreteSliderDuration: 200;
|
||||
|
||||
defaultRoundShadow: Shadow {
|
||||
left: icon {{ "round_shadow_left", windowShadowFg }};
|
||||
topLeft: icon {{ "round_shadow_top_left", windowShadowFg }};
|
||||
top: icon {{ "round_shadow_top", windowShadowFg }};
|
||||
topRight: icon {{ "round_shadow_top_left-flip_horizontal", windowShadowFg }};
|
||||
right: icon {{ "round_shadow_left-flip_horizontal", windowShadowFg }};
|
||||
bottomRight: icon {{ "round_shadow_bottom_left-flip_horizontal", windowShadowFg }};
|
||||
bottom: icon {{ "round_shadow_bottom", windowShadowFg }};
|
||||
bottomLeft: icon {{ "round_shadow_bottom_left", windowShadowFg }};
|
||||
extend: margins(3px, 2px, 3px, 4px);
|
||||
fallback: windowShadowFgFallback;
|
||||
}
|
||||
defaultEmptyShadow: Shadow {
|
||||
fallback: windowBg;
|
||||
}
|
||||
|
||||
defaultPanelAnimation: PanelAnimation {
|
||||
startWidth: 0.5;
|
||||
widthDuration: 0.6;
|
||||
startHeight: 0.3;
|
||||
heightDuration: 0.9;
|
||||
startOpacity: 0.2;
|
||||
opacityDuration: 0.3;
|
||||
startFadeTop: 0.;
|
||||
fadeHeight: 0.2;
|
||||
fadeOpacity: 1.0;
|
||||
fadeBg: menuBg;
|
||||
shadow: defaultRoundShadow;
|
||||
}
|
||||
|
||||
defaultMenuArrow: icon {{ "dropdown_submenu_arrow", #373737 }};
|
||||
defaultMenu: Menu {
|
||||
skip: 8px;
|
||||
skip: 0px;
|
||||
|
||||
itemBg: windowBg;
|
||||
itemBgOver: windowOverBg;
|
||||
|
@ -235,23 +296,30 @@ defaultMenu: Menu {
|
|||
widthMax: 300px;
|
||||
}
|
||||
defaultPopupMenu: PopupMenu {
|
||||
shadow: defaultDropdownShadow;
|
||||
shadowShift: defaultDropdownShadowShift;
|
||||
shadow: defaultRoundShadow;
|
||||
animation: defaultPanelAnimation;
|
||||
|
||||
scrollPadding: margins(0px, 8px, 0px, 8px);
|
||||
|
||||
menu: defaultMenu;
|
||||
|
||||
duration: 120;
|
||||
duration: 150;
|
||||
showDuration: 200;
|
||||
}
|
||||
defaultInnerDropdown: InnerDropdown {
|
||||
padding: margins(10px, 10px, 10px, 10px);
|
||||
shadow: defaultDropdownShadow;
|
||||
shadowShift: defaultDropdownShadowShift;
|
||||
shadow: defaultRoundShadow;
|
||||
animation: defaultPanelAnimation;
|
||||
|
||||
duration: 150;
|
||||
showDuration: 200;
|
||||
|
||||
bg: menuBg;
|
||||
scroll: solidScroll;
|
||||
}
|
||||
defaultDropdownMenu: DropdownMenu {
|
||||
wrap: defaultInnerDropdown;
|
||||
wrap: InnerDropdown(defaultInnerDropdown) {
|
||||
scrollPadding: margins(0px, 8px, 0px, 8px);
|
||||
}
|
||||
menu: defaultMenu;
|
||||
}
|
||||
|
|
|
@ -105,7 +105,7 @@ void TopBarWidget::showMenu() {
|
|||
_menu.destroyDelayed();
|
||||
});
|
||||
_menu->moveToRight(0, 0);
|
||||
_menu->showAnimated();
|
||||
_menu->showAnimated(Ui::PanelAnimation::Origin::TopRight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -461,6 +461,8 @@
|
|||
'<(src_loc)/ui/buttons/peer_avatar_button.h',
|
||||
'<(src_loc)/ui/buttons/round_button.cpp',
|
||||
'<(src_loc)/ui/buttons/round_button.h',
|
||||
'<(src_loc)/ui/effects/panel_animation.cpp',
|
||||
'<(src_loc)/ui/effects/panel_animation.h',
|
||||
'<(src_loc)/ui/effects/radial_animation.cpp',
|
||||
'<(src_loc)/ui/effects/radial_animation.h',
|
||||
'<(src_loc)/ui/effects/rect_shadow.cpp',
|
||||
|
|