diff --git a/Telegram/SourceFiles/codegen/scheme/codegen_scheme.py b/Telegram/SourceFiles/codegen/scheme/codegen_scheme.py index c2199331d..81a595d76 100644 --- a/Telegram/SourceFiles/codegen/scheme/codegen_scheme.py +++ b/Telegram/SourceFiles/codegen/scheme/codegen_scheme.py @@ -675,7 +675,9 @@ for restype in typesList: forwards += 'class MTPD' + name + ';\n'; # data class forward declaration - dataText += ', '.join(prmsStr) + ') : ' + ', '.join(prmsInit) + ' {\n\t}\n'; + dataText += ', '.join(prmsStr) + ');\n'; + + constructsBodies += 'MTPD' + name + '::MTPD' + name + '(' + ', '.join(prmsStr) + ') : ' + ', '.join(prmsInit) + ' {\n}\n'; dataText += '\n'; for paramName in prmsList: # fields declaration @@ -746,20 +748,11 @@ for restype in typesList: typesText += ' {\n'; typesText += 'public:\n'; typesText += '\tMTP' + restype + '()'; # default constructor - inits = []; - if not (withType): - if (withData): - inits.append('TypeDataOwner(' + newFast + ')'); if (withData and not withType): typesText += ';\n'; - methods += '\nMTP' + restype + '::MTP' + restype + '()'; - if (inits): - methods += ' : ' + ', '.join(inits); - methods += ' {\n}\n'; + methods += '\nMTP' + restype + '::MTP' + restype + '() : TypeDataOwner(' + newFast + ') {\n}\n'; else: - if (inits): - typesText += ' : ' + ', '.join(inits); - typesText += ' {\n\t}\n'; + typesText += ' = default;\n'; if (withData): typesText += getters; diff --git a/Telegram/SourceFiles/info/info.style b/Telegram/SourceFiles/info/info.style index 8756f2c79..4c67885a0 100644 --- a/Telegram/SourceFiles/info/info.style +++ b/Telegram/SourceFiles/info/info.style @@ -713,3 +713,33 @@ topBarConnectingAnimation: InfiniteRadialAnimation(defaultInfiniteRadialAnimatio } infoFeedLeaveIconMargins: margins(10px, 12px, 20px, 10px); + +separatePanelBorderCacheSize: 60px; +separatePanelTitleHeight: 62px; +separatePanelClose: IconButton(boxTitleClose) { + width: 60px; + height: 60px; + + rippleAreaPosition: point(8px, 8px); + rippleAreaSize: 44px; + ripple: RippleAnimation(defaultRippleAnimation) { + color: windowBgOver; + } +} +separatePanelTitleFont: font(18px semibold); +separatePanelTitle: FlatLabel(defaultFlatLabel) { + textFg: boxTitleFg; + maxHeight: 26px; + style: TextStyle(defaultTextStyle) { + font: separatePanelTitleFont; + linkFont: separatePanelTitleFont; + linkFontOver: font(18px semibold underline); + } +} +separatePanelTitleTop: 18px; +separatePanelTitleLeft: 22px; +separatePanelTitleSkip: 0px; +separatePanelBack: IconButton(separatePanelClose) { + icon: infoTopBarBackIcon; + iconOver: infoTopBarBackIconOver; +} diff --git a/Telegram/SourceFiles/passport/passport.style b/Telegram/SourceFiles/passport/passport.style index 625a01ed7..ec0980e7c 100644 --- a/Telegram/SourceFiles/passport/passport.style +++ b/Telegram/SourceFiles/passport/passport.style @@ -37,37 +37,7 @@ passportVerifyErrorLabel: FlatLabel(passportErrorLabel) { align: align(topleft); } -passportPanelWidth: 392px; -passportPanelHeight: 600px; -passportPanelBorderCacheSize: 60px; -passportPanelTitleHeight: 62px; -passportPanelClose: IconButton(boxTitleClose) { - width: 60px; - height: 60px; - - rippleAreaPosition: point(8px, 8px); - rippleAreaSize: 44px; - ripple: RippleAnimation(defaultRippleAnimation) { - color: windowBgOver; - } -} -passportPanelTitleFont: font(18px semibold); -passportPanelTitle: FlatLabel(defaultFlatLabel) { - textFg: boxTitleFg; - maxHeight: 26px; - style: TextStyle(defaultTextStyle) { - font: passportPanelTitleFont; - linkFont: passportPanelTitleFont; - linkFontOver: font(18px semibold underline); - } -} -passportPanelTitleTop: 18px; -passportPanelTitleLeft: 22px; -passportPanelTitleSkip: 0px; -passportPanelBack: IconButton(passportPanelClose) { - icon: infoTopBarBackIcon; - iconOver: infoTopBarBackIconOver; -} +passportPanelSize: size(392px, 600px); passportPasswordFieldBottom: 306px; passportPasswordFieldSkip: 29px; diff --git a/Telegram/SourceFiles/passport/passport_panel.cpp b/Telegram/SourceFiles/passport/passport_panel.cpp index 68a5893c5..0848c9c66 100644 --- a/Telegram/SourceFiles/passport/passport_panel.cpp +++ b/Telegram/SourceFiles/passport/passport_panel.cpp @@ -10,16 +10,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "passport/passport_panel_controller.h" #include "passport/passport_panel_form.h" #include "passport/passport_panel_password.h" -#include "window/main_window.h" -#include "platform/platform_specific.h" -#include "ui/widgets/shadow.h" -#include "ui/widgets/buttons.h" +#include "ui/widgets/separate_panel.h" #include "ui/widgets/labels.h" #include "ui/wrap/padding_wrap.h" -#include "ui/wrap/fade_wrap.h" #include "lang/lang_keys.h" -#include "window/layer_widget.h" -#include "messenger.h" #include "styles/style_passport.h" #include "styles/style_widgets.h" #include "styles/style_calls.h" @@ -28,515 +22,85 @@ namespace Passport { Panel::Panel(not_null controller) : _controller(controller) -, _close(this, st::passportPanelClose) -, _title( - this, - lang(lng_passport_title), - Ui::FlatLabel::InitType::Simple, - st::passportPanelTitle) -, _back(this, object_ptr(this, st::passportPanelBack)) -, _body(this) { - setMouseTracking(true); - setWindowIcon(Window::CreateIcon()); - initControls(); - initLayout(); -} +, _widget(std::make_unique()) { + _widget->setTitle(Lang::Viewer(lng_passport_title)); + _widget->setInnerSize(st::passportPanelSize); -void Panel::initControls() { - widthValue( - ) | rpl::start_with_next([=](int width) { - _back->moveToLeft(_padding.left(), _padding.top()); - _close->moveToRight(_padding.right(), _padding.top()); - _title->resizeToWidth(width - - _padding.left() - _back->width() - - _padding.right() - _close->width()); - updateTitlePosition(); - }, lifetime()); - - _close->addClickHandler([=] { + rpl::merge( + _widget->closeRequests(), + _widget->destroyRequests() + ) | rpl::start_with_next([=] { _controller->cancelAuth(); - }); - - _back->toggledValue( - ) | rpl::start_with_next([=](bool toggled) { - _titleLeft.start( - [=] { updateTitlePosition(); }, - toggled ? 0. : 1., - toggled ? 1. : 0., - st::fadeWrapDuration); - }, _title->lifetime()); - _back->hide(anim::type::instant); - _titleLeft.finish(); - - _title->setAttribute(Qt::WA_TransparentForMouseEvents); -} - -void Panel::updateTitlePosition() { - const auto progress = _titleLeft.current(_back->toggled() ? 1. : 0.); - const auto left = anim::interpolate( - st::passportPanelTitleLeft, - _back->width() + st::passportPanelTitleSkip, - progress); - _title->moveToLeft( - _padding.left() + left, - _padding.top() + st::passportPanelTitleTop); - + }, _widget->lifetime()); } rpl::producer<> Panel::backRequests() const { - return rpl::merge( - _back->entity()->clicks(), - _synteticBackRequests.events()); + return _widget->backRequests(); } void Panel::setBackAllowed(bool allowed) { - if (allowed != _back->toggled()) { - _back->toggle(allowed, anim::type::normal); - } + _widget->setBackAllowed(allowed); } -void Panel::showAndActivate() { - toggleOpacityAnimation(true); - raise(); - setWindowState(windowState() | Qt::WindowActive); - activateWindow(); - setFocus(); -} - -void Panel::keyPressEvent(QKeyEvent *e) { - if (e->key() == Qt::Key_Escape && _back->toggled()) { - _synteticBackRequests.fire({}); - } - return RpWidget::keyPressEvent(e); -} - -void Panel::initLayout() { - setWindowFlags(Qt::WindowFlags(Qt::FramelessWindowHint) - | Qt::WindowStaysOnTopHint - | Qt::BypassWindowManagerHint - | Qt::NoDropShadowWindowHint - | Qt::Dialog); - setAttribute(Qt::WA_MacAlwaysShowToolWindow); - setAttribute(Qt::WA_NoSystemBackground, true); - setAttribute(Qt::WA_TranslucentBackground, true); - - initGeometry(); - createBorderImage(); - - Platform::InitOnTopPanel(this); -} - -void Panel::createBorderImage() { - if (!_useTransparency || !_borderParts.isNull()) { - return; - } - const auto cacheSize = st::passportPanelBorderCacheSize; - auto cache = QImage( - cacheSize * cIntRetinaFactor(), - cacheSize * cIntRetinaFactor(), - QImage::Format_ARGB32_Premultiplied); - cache.setDevicePixelRatio(cRetinaFactor()); - cache.fill(Qt::transparent); - { - Painter p(&cache); - auto inner = QRect(0, 0, cacheSize, cacheSize).marginsRemoved( - _padding); - Ui::Shadow::paint(p, inner, width(), st::callShadow); - p.setCompositionMode(QPainter::CompositionMode_Source); - p.setBrush(st::windowBg); - p.setPen(Qt::NoPen); - PainterHighQualityEnabler hq(p); - p.drawRoundedRect( - myrtlrect(inner), - st::callRadius, - st::callRadius); - } - _borderParts = App::pixmapFromImageInPlace(std::move(cache)); -} - -void Panel::toggleOpacityAnimation(bool visible) { - if (_visible == visible) { - return; - } - - _visible = visible; - if (_useTransparency) { - if (_animationCache.isNull()) { - showControls(); - _animationCache = Ui::GrabWidget(this); - hideChildren(); - } - _opacityAnimation.start( - [this] { opacityCallback(); }, - _visible ? 0. : 1., - _visible ? 1. : 0., - st::callPanelDuration, - _visible ? anim::easeOutCirc : anim::easeInCirc); - } - if (isHidden() && _visible) { - show(); - } -} - -void Panel::opacityCallback() { - update(); - if (!_visible && !_opacityAnimation.animating()) { - finishAnimating(); - } -} - -void Panel::finishAnimating() { - _animationCache = QPixmap(); - if (_visible) { - showControls(); - _inner->setFocus(); - } else { - destroyDelayed(); - } -} - -void Panel::showControls() { - showChildren(); - if (!_back->toggled()) { - _back->setVisible(false); - } -} - -void Panel::destroyDelayed() { - hide(); - _controller->cancelAuth(); +not_null Panel::widget() const { + return _widget.get(); } int Panel::hideAndDestroyGetDuration() { - toggleOpacityAnimation(false); - if (_animationCache.isNull()) { - destroyDelayed(); - return 0; - } - return st::callPanelDuration; + return _widget->hideAndDestroyGetDuration(); } void Panel::showAskPassword() { - showInner(base::make_unique_q(_body, _controller)); + _widget->showInner( + base::make_unique_q(_widget.get(), _controller)); setBackAllowed(false); } void Panel::showNoPassword() { - showInner(base::make_unique_q(_body, _controller)); + _widget->showInner( + base::make_unique_q(_widget.get(), _controller)); setBackAllowed(false); } void Panel::showCriticalError(const QString &error) { auto container = base::make_unique_q>( - _body, + _widget.get(), object_ptr( - _body, + _widget.get(), error, Ui::FlatLabel::InitType::Simple, st::passportErrorLabel), - style::margins(0, st::passportPanelHeight / 3, 0, 0)); + style::margins(0, st::passportPanelSize.height() / 3, 0, 0)); container->widthValue( ) | rpl::start_with_next([label = container->entity()](int width) { label->resize(width, label->height()); }, container->lifetime()); - showInner(std::move(container)); + + _widget->showInner(std::move(container)); setBackAllowed(false); } void Panel::showForm() { - showInner(base::make_unique_q(_body, _controller)); + _widget->showInner( + base::make_unique_q(_widget.get(), _controller)); setBackAllowed(false); } void Panel::showEditValue(object_ptr from) { - showInner(base::unique_qptr(from.data())); + _widget->showInner(base::unique_qptr(from.data())); } void Panel::showBox( object_ptr box, LayerOptions options, anim::type animated) { - ensureLayerCreated(); - _layer->showBox(std::move(box), options, animated); + _widget->showBox(std::move(box), options, animated); } -void Panel::ensureLayerCreated() { - if (_layer) { - return; - } - _layer.create(_body); - _layer->setHideByBackgroundClick(false); - _layer->move(0, 0); - _body->sizeValue( - ) | rpl::start_with_next([=](QSize size) { - _layer->resize(size); - }, _layer->lifetime()); - _layer->hideFinishEvents( - ) | rpl::start_with_next([=, pointer = _layer.data()]{ - if (_layer != pointer) { - return; - } - auto saved = std::exchange(_layer, nullptr); - if (Ui::InFocusChain(saved)) { - setFocus(); - } - saved.destroyDelayed(); - }, _layer->lifetime()); +void Panel::showToast(const QString &text) { + _widget->showToast(text); } -void Panel::showInner(base::unique_qptr inner) { - _inner = std::move(inner); - _inner->setParent(_body); - _inner->move(0, 0); - _body->sizeValue( - ) | rpl::start_with_next([=](QSize size) { - _inner->resize(size); - }, _inner->lifetime()); - _inner->show(); - - if (_layer) { - _layer->raise(); - } - - showAndActivate(); -} - -void Panel::focusInEvent(QFocusEvent *e) { - crl::on_main(this, [=] { - if (_layer) { - _layer->setInnerFocus(); - } else if (!_inner->isHidden()) { - _inner->setFocus(); - } - }); -} - -void Panel::initGeometry() { - const auto center = Messenger::Instance().getPointForCallPanelCenter(); - _useTransparency = Platform::TranslucentWindowsSupported(center); - setAttribute(Qt::WA_OpaquePaintEvent, !_useTransparency); - _padding = _useTransparency - ? st::callShadow.extend - : style::margins( - st::lineWidth, - st::lineWidth, - st::lineWidth, - st::lineWidth); - const auto screen = QApplication::desktop()->screenGeometry(center); - const auto rect = QRect( - 0, - 0, - st::passportPanelWidth, - st::passportPanelHeight); - setGeometry( - rect.translated(center - rect.center()).marginsAdded(_padding)); - updateControlsGeometry(); -} - -void Panel::resizeEvent(QResizeEvent *e) { - updateControlsGeometry(); -} - -void Panel::updateControlsGeometry() { - const auto top = _padding.top() + st::passportPanelTitleHeight; - _body->setGeometry( - _padding.left(), - top, - width() - _padding.left() - _padding.right(), - height() - top - _padding.bottom()); -} - -void Panel::paintEvent(QPaintEvent *e) { - Painter p(this); - if (!_animationCache.isNull()) { - auto opacity = _opacityAnimation.current( - getms(), - _visible ? 1. : 0.); - if (!_opacityAnimation.animating()) { - finishAnimating(); - if (isHidden()) return; - } else { - Platform::StartTranslucentPaint(p, e); - p.setOpacity(opacity); - - PainterHighQualityEnabler hq(p); - auto marginRatio = (1. - opacity) / 5; - auto marginWidth = qRound(width() * marginRatio); - auto marginHeight = qRound(height() * marginRatio); - p.drawPixmap( - rect().marginsRemoved( - QMargins( - marginWidth, - marginHeight, - marginWidth, - marginHeight)), - _animationCache, - QRect(QPoint(0, 0), _animationCache.size())); - return; - } - } - - if (_useTransparency) { - Platform::StartTranslucentPaint(p, e); - paintShadowBorder(p); - } else { - paintOpaqueBorder(p); - } -} - -void Panel::paintShadowBorder(Painter &p) const { - const auto factor = cIntRetinaFactor(); - const auto size = st::passportPanelBorderCacheSize; - const auto part1 = size / 3; - const auto part2 = size - part1; - const auto corner = QSize(part1, part1) * factor; - - const auto topleft = QRect(QPoint(0, 0), corner); - p.drawPixmap(QRect(0, 0, part1, part1), _borderParts, topleft); - - const auto topright = QRect(QPoint(part2, 0) * factor, corner); - p.drawPixmap( - QRect(width() - part1, 0, part1, part1), - _borderParts, - topright); - - const auto bottomleft = QRect(QPoint(0, part2) * factor, corner); - p.drawPixmap( - QRect(0, height() - part1, part1, part1), - _borderParts, - bottomleft); - - const auto bottomright = QRect(QPoint(part2, part2) * factor, corner); - p.drawPixmap( - QRect(width() - part1, height() - part1, part1, part1), - _borderParts, - bottomright); - - const auto left = QRect( - QPoint(0, part1) * factor, - QSize(_padding.left(), part2 - part1) * factor); - p.drawPixmap( - QRect(0, part1, _padding.left(), height() - 2 * part1), - _borderParts, - left); - - const auto top = QRect( - QPoint(part1, 0) * factor, - QSize(part2 - part1, _padding.top() + st::callRadius) * factor); - p.drawPixmap( - QRect( - part1, - 0, - width() - 2 * part1, - _padding.top() + st::callRadius), - _borderParts, - top); - - const auto right = QRect( - QPoint(size - _padding.right(), part1) * factor, - QSize(_padding.right(), part2 - part1) * factor); - p.drawPixmap( - QRect( - width() - _padding.right(), - part1, - _padding.right(), - height() - 2 * part1), - _borderParts, - right); - - const auto bottom = QRect( - QPoint(part1, size - _padding.bottom() - st::callRadius) * factor, - QSize(part2 - part1, _padding.bottom() + st::callRadius) * factor); - p.drawPixmap( - QRect( - part1, - height() - _padding.bottom() - st::callRadius, - width() - 2 * part1, - _padding.bottom() + st::callRadius), - _borderParts, - bottom); - - p.fillRect( - _padding.left(), - _padding.top() + st::callRadius, - width() - _padding.left() - _padding.right(), - height() - _padding.top() - _padding.bottom() - 2 * st::callRadius, - st::windowBg); -} - -void Panel::paintOpaqueBorder(Painter &p) const { - const auto border = st::windowShadowFgFallback; - p.fillRect(0, 0, width(), _padding.top(), border); - p.fillRect( - myrtlrect( - 0, - _padding.top(), - _padding.left(), - height() - _padding.top()), - border); - p.fillRect( - myrtlrect( - width() - _padding.right(), - _padding.top(), - _padding.right(), - height() - _padding.top()), - border); - p.fillRect( - _padding.left(), - height() - _padding.bottom(), - width() - _padding.left() - _padding.right(), - _padding.bottom(), - border); - - p.fillRect( - _padding.left(), - _padding.top(), - width() - _padding.left() - _padding.right(), - height() - _padding.top() - _padding.bottom(), - st::windowBg); -} - -void Panel::closeEvent(QCloseEvent *e) { - // #TODO passport -} - -void Panel::mousePressEvent(QMouseEvent *e) { - auto dragArea = myrtlrect( - _padding.left(), - _padding.top(), - width() - _padding.left() - _padding.right(), - st::passportPanelTitleHeight); - if (e->button() == Qt::LeftButton) { - if (dragArea.contains(e->pos())) { - _dragging = true; - _dragStartMousePosition = e->globalPos(); - _dragStartMyPosition = QPoint(x(), y()); - } else if (!rect().contains(e->pos())) { - } - } -} - -void Panel::mouseMoveEvent(QMouseEvent *e) { - if (_dragging) { - if (!(e->buttons() & Qt::LeftButton)) { - _dragging = false; - } else { - move(_dragStartMyPosition - + (e->globalPos() - _dragStartMousePosition)); - } - } -} - -void Panel::mouseReleaseEvent(QMouseEvent *e) { - if (e->button() == Qt::LeftButton) { - _dragging = false; - } -} - -void Panel::leaveEventHook(QEvent *e) { -} - -void Panel::leaveToChildEvent(QEvent *e, QWidget *child) { -} +Panel::~Panel() = default; } // namespace Passport diff --git a/Telegram/SourceFiles/passport/passport_panel.h b/Telegram/SourceFiles/passport/passport_panel.h index 73ce87413..c9186327b 100644 --- a/Telegram/SourceFiles/passport/passport_panel.h +++ b/Telegram/SourceFiles/passport/passport_panel.h @@ -7,32 +7,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once -#include "ui/rp_widget.h" -#include "boxes/abstract_box.h" - namespace Ui { -class IconButton; -class FlatLabel; -template -class FadeWrapScaled; +class RpWidget; +class SeparatePanel; } // namespace Ui -namespace Window { -class LayerStackWidget; -} // namespace Window - namespace Passport { class PanelController; -class Panel - : public Ui::RpWidget - , private base::Subscriber { - +class Panel { public: Panel(not_null controller); - void showAndActivate(); int hideAndDestroyGetDuration(); void showAskPassword(); @@ -44,63 +31,18 @@ public: object_ptr box, LayerOptions options, anim::type animated); + void showToast(const QString &text); rpl::producer<> backRequests() const; void setBackAllowed(bool allowed); -protected: - void paintEvent(QPaintEvent *e) override; - void closeEvent(QCloseEvent *e) override; - void resizeEvent(QResizeEvent *e) override; - void focusInEvent(QFocusEvent *e) override; - void mousePressEvent(QMouseEvent *e) override; - void mouseReleaseEvent(QMouseEvent *e) override; - void mouseMoveEvent(QMouseEvent *e) override; - void leaveEventHook(QEvent *e) override; - void leaveToChildEvent(QEvent *e, QWidget *child) override; - void keyPressEvent(QKeyEvent *e) override; + not_null widget() const; + + ~Panel(); private: - void initControls(); - void initLayout(); - void initGeometry(); - void showControls(); - void updateControlsGeometry(); - void createBorderImage(); - void opacityCallback(); - void showInner(base::unique_qptr inner); - void ensureLayerCreated(); - - void updateTitlePosition(); - void paintShadowBorder(Painter &p) const; - void paintOpaqueBorder(Painter &p) const; - - void toggleOpacityAnimation(bool visible); - void finishAnimating(); - void destroyDelayed(); - not_null _controller; - object_ptr _close; - object_ptr _title; - object_ptr> _back; - object_ptr _body; - base::unique_qptr _inner; - object_ptr _layer = { nullptr }; - rpl::event_stream<> _synteticBackRequests; - - bool _useTransparency = true; - style::margins _padding; - - bool _dragging = false; - QPoint _dragStartMousePosition; - QPoint _dragStartMyPosition; - - Animation _titleLeft; - bool _visible = false; - - Animation _opacityAnimation; - QPixmap _animationCache; - QPixmap _borderParts; + std::unique_ptr _widget; }; diff --git a/Telegram/SourceFiles/passport/passport_panel_controller.cpp b/Telegram/SourceFiles/passport/passport_panel_controller.cpp index 72a2bc3c5..badea4c31 100644 --- a/Telegram/SourceFiles/passport/passport_panel_controller.cpp +++ b/Telegram/SourceFiles/passport/passport_panel_controller.cpp @@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/passcode_box.h" #include "boxes/confirm_box.h" #include "ui/toast/toast.h" +#include "ui/rp_widget.h" #include "ui/countryinput.h" #include "layout.h" #include "styles/style_boxes.h" @@ -492,7 +493,7 @@ void PanelController::setupPassword() { notEmptyPassport, hint, newSecureSecretSalt)); - box->connect(box, &PasscodeBox::reloadPassword, _panel.get(), [=] { + box->connect(box, &PasscodeBox::reloadPassword, [=] { _form->reloadPassword(); }); } @@ -894,7 +895,8 @@ void PanelController::editWithUpload(int index, int documentIndex) { Expects(documentIndex >= 0 && documentIndex < _scopes[index].documents.size()); - EditScans::ChooseScan(_panel.get(), [=](QByteArray &&content) { + const auto widget = _panel->widget(); + EditScans::ChooseScan(widget.get(), [=](QByteArray &&content) { base::take(_scopeDocumentTypeBox); editScope(index, documentIndex); if (_scopes[index].documents[documentIndex]->requiresSpecialScan( @@ -949,7 +951,7 @@ void PanelController::editScope(int index, int documentIndex) { case Scope::Type::Address: { auto result = _editDocument ? object_ptr( - _panel.get(), + _panel->widget(), this, GetDocumentScheme( _editScope->type, @@ -960,7 +962,7 @@ void PanelController::editScope(int index, int documentIndex) { valueFiles(*_editDocument), valueSpecialFiles(*_editDocument)) : object_ptr( - _panel.get(), + _panel->widget(), this, GetDocumentScheme(_editScope->type), _editValue->data.parsedInEdit); @@ -980,7 +982,7 @@ void PanelController::editScope(int index, int documentIndex) { const auto existing = getDefaultContactValue(_editScope->type); _panelHasUnsavedChanges = nullptr; return object_ptr( - _panel.get(), + _panel->widget(), this, GetContactScheme(_editScope->type), value, @@ -1206,11 +1208,7 @@ void PanelController::showBox( } void PanelController::showToast(const QString &text) { - Expects(_panel != nullptr); - - auto toast = Ui::Toast::Config(); - toast.text = text; - Ui::Toast::Show(_panel.get(), toast); + _panel->showToast(text); } rpl::lifetime &PanelController::lifetime() { diff --git a/Telegram/SourceFiles/ui/widgets/separate_panel.cpp b/Telegram/SourceFiles/ui/widgets/separate_panel.cpp new file mode 100644 index 000000000..f6e33d515 --- /dev/null +++ b/Telegram/SourceFiles/ui/widgets/separate_panel.cpp @@ -0,0 +1,532 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "ui/widgets/separate_panel.h" + +#include "window/main_window.h" +#include "platform/platform_specific.h" +#include "ui/widgets/shadow.h" +#include "ui/widgets/buttons.h" +#include "ui/widgets/labels.h" +#include "ui/wrap/padding_wrap.h" +#include "ui/wrap/fade_wrap.h" +#include "ui/toast/toast.h" +#include "ui/widgets/tooltip.h" +#include "window/layer_widget.h" +#include "messenger.h" +#include "styles/style_widgets.h" +#include "styles/style_info.h" +#include "styles/style_calls.h" + +namespace Ui { + +SeparatePanel::SeparatePanel() +: _close(this, st::separatePanelClose) +, _back(this, object_ptr(this, st::separatePanelBack)) +, _body(this) { + setMouseTracking(true); + setWindowIcon(Window::CreateIcon()); + initControls(); + initLayout(); +} + +void SeparatePanel::setTitle(rpl::producer title) { + _title.create(this, std::move(title), st::separatePanelTitle); + _title->setAttribute(Qt::WA_TransparentForMouseEvents); +} + +void SeparatePanel::initControls() { + widthValue( + ) | rpl::start_with_next([=](int width) { + _back->moveToLeft(_padding.left(), _padding.top()); + _close->moveToRight(_padding.right(), _padding.top()); + if (_title) { + _title->resizeToWidth(width + - _padding.left() - _back->width() + - _padding.right() - _close->width()); + updateTitlePosition(); + } + }, lifetime()); + + _back->toggledValue( + ) | rpl::start_with_next([=](bool toggled) { + _titleLeft.start( + [=] { updateTitlePosition(); }, + toggled ? 0. : 1., + toggled ? 1. : 0., + st::fadeWrapDuration); + }, _back->lifetime()); + _back->hide(anim::type::instant); + _titleLeft.finish(); +} + +void SeparatePanel::updateTitlePosition() { + if (!_title) { + return; + } + const auto progress = _titleLeft.current(_back->toggled() ? 1. : 0.); + const auto left = anim::interpolate( + st::separatePanelTitleLeft, + _back->width() + st::separatePanelTitleSkip, + progress); + _title->moveToLeft( + _padding.left() + left, + _padding.top() + st::separatePanelTitleTop); +} + +rpl::producer<> SeparatePanel::backRequests() const { + return rpl::merge( + _back->entity()->clicks(), + _synteticBackRequests.events()); +} + +rpl::producer<> SeparatePanel::closeRequests() const { + return _close->clicks(); +} + +rpl::producer<> SeparatePanel::destroyRequests() const { + return _destroyRequests.events(); +} + +void SeparatePanel::setBackAllowed(bool allowed) { + if (allowed != _back->toggled()) { + _back->toggle(allowed, anim::type::normal); + } +} + +void SeparatePanel::showAndActivate() { + toggleOpacityAnimation(true); + raise(); + setWindowState(windowState() | Qt::WindowActive); + activateWindow(); + setFocus(); +} + +void SeparatePanel::keyPressEvent(QKeyEvent *e) { + if (e->key() == Qt::Key_Escape && _back->toggled()) { + _synteticBackRequests.fire({}); + } + return RpWidget::keyPressEvent(e); +} + +void SeparatePanel::initLayout() { + setWindowFlags(Qt::WindowFlags(Qt::FramelessWindowHint) + | Qt::WindowStaysOnTopHint + | Qt::BypassWindowManagerHint + | Qt::NoDropShadowWindowHint + | Qt::Dialog); + setAttribute(Qt::WA_MacAlwaysShowToolWindow); + setAttribute(Qt::WA_NoSystemBackground, true); + setAttribute(Qt::WA_TranslucentBackground, true); + + createBorderImage(); + + Platform::InitOnTopPanel(this); +} + +void SeparatePanel::createBorderImage() { + const auto shadowPadding = st::callShadow.extend; + const auto cacheSize = st::separatePanelBorderCacheSize; + auto cache = QImage( + cacheSize * cIntRetinaFactor(), + cacheSize * cIntRetinaFactor(), + QImage::Format_ARGB32_Premultiplied); + cache.setDevicePixelRatio(cRetinaFactor()); + cache.fill(Qt::transparent); + { + Painter p(&cache); + auto inner = QRect(0, 0, cacheSize, cacheSize).marginsRemoved( + shadowPadding); + Ui::Shadow::paint(p, inner, cacheSize, st::callShadow); + p.setCompositionMode(QPainter::CompositionMode_Source); + p.setBrush(st::windowBg); + p.setPen(Qt::NoPen); + PainterHighQualityEnabler hq(p); + p.drawRoundedRect( + myrtlrect(inner), + st::callRadius, + st::callRadius); + } + _borderParts = App::pixmapFromImageInPlace(std::move(cache)); +} + +void SeparatePanel::toggleOpacityAnimation(bool visible) { + if (_visible == visible) { + return; + } + + _visible = visible; + if (_useTransparency) { + if (_animationCache.isNull()) { + showControls(); + _animationCache = Ui::GrabWidget(this); + hideChildren(); + } + _opacityAnimation.start( + [this] { opacityCallback(); }, + _visible ? 0. : 1., + _visible ? 1. : 0., + st::callPanelDuration, + _visible ? anim::easeOutCirc : anim::easeInCirc); + } + if (isHidden() && _visible) { + show(); + } +} + +void SeparatePanel::opacityCallback() { + update(); + if (!_visible && !_opacityAnimation.animating()) { + finishAnimating(); + } +} + +void SeparatePanel::finishAnimating() { + _animationCache = QPixmap(); + if (_visible) { + showControls(); + _inner->setFocus(); + } else { + destroyDelayed(); + } +} + +void SeparatePanel::showControls() { + showChildren(); + if (!_back->toggled()) { + _back->setVisible(false); + } +} + +void SeparatePanel::destroyDelayed() { + hide(); + _destroyRequests.fire({}); +} + +int SeparatePanel::hideAndDestroyGetDuration() { + toggleOpacityAnimation(false); + if (_animationCache.isNull()) { + destroyDelayed(); + return 0; + } + return st::callPanelDuration; +} + +void SeparatePanel::showBox( + object_ptr box, + LayerOptions options, + anim::type animated) { + ensureLayerCreated(); + _layer->showBox(std::move(box), options, animated); +} + +void SeparatePanel::showToast(const QString &text) { + auto toast = Ui::Toast::Config(); + toast.text = text; + Ui::Toast::Show(this, toast); +} + +void SeparatePanel::ensureLayerCreated() { + if (_layer) { + return; + } + _layer.create(_body); + _layer->setHideByBackgroundClick(false); + _layer->move(0, 0); + _body->sizeValue( + ) | rpl::start_with_next([=](QSize size) { + _layer->resize(size); + }, _layer->lifetime()); + _layer->hideFinishEvents( + ) | rpl::start_with_next([=, pointer = _layer.data()]{ + if (_layer != pointer) { + return; + } + auto saved = std::exchange(_layer, nullptr); + if (Ui::InFocusChain(saved)) { + setFocus(); + } + saved.destroyDelayed(); + }, _layer->lifetime()); +} + +void SeparatePanel::showInner(base::unique_qptr inner) { + Expects(!size().isEmpty()); + + _inner = std::move(inner); + _inner->setParent(_body); + _inner->move(0, 0); + _body->sizeValue( + ) | rpl::start_with_next([=](QSize size) { + _inner->resize(size); + }, _inner->lifetime()); + _inner->show(); + + if (_layer) { + _layer->raise(); + } + + showAndActivate(); +} + +void SeparatePanel::focusInEvent(QFocusEvent *e) { + crl::on_main(this, [=] { + if (_layer) { + _layer->setInnerFocus(); + } else if (!_inner->isHidden()) { + _inner->setFocus(); + } + }); +} + +void SeparatePanel::setInnerSize(QSize size) { + Expects(!size.isEmpty()); + + if (rect().isEmpty()) { + initGeometry(size); + } else { + updateGeometry(size); + } +} + +void SeparatePanel::initGeometry(QSize size) { + const auto center = Messenger::Instance().getPointForCallPanelCenter(); + _useTransparency = Platform::TranslucentWindowsSupported(center); + _padding = _useTransparency + ? st::callShadow.extend + : style::margins( + st::lineWidth, + st::lineWidth, + st::lineWidth, + st::lineWidth); + setAttribute(Qt::WA_OpaquePaintEvent, !_useTransparency); + const auto screen = QApplication::desktop()->screenGeometry(center); + const auto rect = QRect(QPoint(), size); + setGeometry( + rect.translated(center - rect.center()).marginsAdded(_padding)); + updateControlsGeometry(); +} + +void SeparatePanel::updateGeometry(QSize size) { + setGeometry( + x(), + y(), + _padding.left() + size.width() + _padding.right(), + _padding.top() + size.height() + _padding.bottom()); + updateControlsGeometry(); + update(); +} + +void SeparatePanel::resizeEvent(QResizeEvent *e) { + updateControlsGeometry(); +} + +void SeparatePanel::updateControlsGeometry() { + const auto top = _padding.top() + st::separatePanelTitleHeight; + _body->setGeometry( + _padding.left(), + top, + width() - _padding.left() - _padding.right(), + height() - top - _padding.bottom()); +} + +void SeparatePanel::paintEvent(QPaintEvent *e) { + Painter p(this); + if (!_animationCache.isNull()) { + auto opacity = _opacityAnimation.current( + getms(), + _visible ? 1. : 0.); + if (!_opacityAnimation.animating()) { + finishAnimating(); + if (isHidden()) return; + } else { + Platform::StartTranslucentPaint(p, e); + p.setOpacity(opacity); + + PainterHighQualityEnabler hq(p); + auto marginRatio = (1. - opacity) / 5; + auto marginWidth = qRound(width() * marginRatio); + auto marginHeight = qRound(height() * marginRatio); + p.drawPixmap( + rect().marginsRemoved( + QMargins( + marginWidth, + marginHeight, + marginWidth, + marginHeight)), + _animationCache, + QRect(QPoint(0, 0), _animationCache.size())); + return; + } + } + + if (_useTransparency) { + Platform::StartTranslucentPaint(p, e); + paintShadowBorder(p); + } else { + paintOpaqueBorder(p); + } +} + +void SeparatePanel::paintShadowBorder(Painter &p) const { + const auto factor = cIntRetinaFactor(); + const auto size = st::separatePanelBorderCacheSize; + const auto part1 = size / 3; + const auto part2 = size - part1; + const auto corner = QSize(part1, part1) * factor; + + const auto topleft = QRect(QPoint(0, 0), corner); + p.drawPixmap(QRect(0, 0, part1, part1), _borderParts, topleft); + + const auto topright = QRect(QPoint(part2, 0) * factor, corner); + p.drawPixmap( + QRect(width() - part1, 0, part1, part1), + _borderParts, + topright); + + const auto bottomleft = QRect(QPoint(0, part2) * factor, corner); + p.drawPixmap( + QRect(0, height() - part1, part1, part1), + _borderParts, + bottomleft); + + const auto bottomright = QRect(QPoint(part2, part2) * factor, corner); + p.drawPixmap( + QRect(width() - part1, height() - part1, part1, part1), + _borderParts, + bottomright); + + const auto left = QRect( + QPoint(0, part1) * factor, + QSize(_padding.left(), part2 - part1) * factor); + p.drawPixmap( + QRect(0, part1, _padding.left(), height() - 2 * part1), + _borderParts, + left); + + const auto top = QRect( + QPoint(part1, 0) * factor, + QSize(part2 - part1, _padding.top() + st::callRadius) * factor); + p.drawPixmap( + QRect( + part1, + 0, + width() - 2 * part1, + _padding.top() + st::callRadius), + _borderParts, + top); + + const auto right = QRect( + QPoint(size - _padding.right(), part1) * factor, + QSize(_padding.right(), part2 - part1) * factor); + p.drawPixmap( + QRect( + width() - _padding.right(), + part1, + _padding.right(), + height() - 2 * part1), + _borderParts, + right); + + const auto bottom = QRect( + QPoint(part1, size - _padding.bottom() - st::callRadius) * factor, + QSize(part2 - part1, _padding.bottom() + st::callRadius) * factor); + p.drawPixmap( + QRect( + part1, + height() - _padding.bottom() - st::callRadius, + width() - 2 * part1, + _padding.bottom() + st::callRadius), + _borderParts, + bottom); + + p.fillRect( + _padding.left(), + _padding.top() + st::callRadius, + width() - _padding.left() - _padding.right(), + height() - _padding.top() - _padding.bottom() - 2 * st::callRadius, + st::windowBg); +} + +void SeparatePanel::paintOpaqueBorder(Painter &p) const { + const auto border = st::windowShadowFgFallback; + p.fillRect(0, 0, width(), _padding.top(), border); + p.fillRect( + myrtlrect( + 0, + _padding.top(), + _padding.left(), + height() - _padding.top()), + border); + p.fillRect( + myrtlrect( + width() - _padding.right(), + _padding.top(), + _padding.right(), + height() - _padding.top()), + border); + p.fillRect( + _padding.left(), + height() - _padding.bottom(), + width() - _padding.left() - _padding.right(), + _padding.bottom(), + border); + + p.fillRect( + _padding.left(), + _padding.top(), + width() - _padding.left() - _padding.right(), + height() - _padding.top() - _padding.bottom(), + st::windowBg); +} + +void SeparatePanel::closeEvent(QCloseEvent *e) { + // #TODO passport +} + +void SeparatePanel::mousePressEvent(QMouseEvent *e) { + auto dragArea = myrtlrect( + _padding.left(), + _padding.top(), + width() - _padding.left() - _padding.right(), + st::separatePanelTitleHeight); + if (e->button() == Qt::LeftButton) { + if (dragArea.contains(e->pos())) { + _dragging = true; + _dragStartMousePosition = e->globalPos(); + _dragStartMyPosition = QPoint(x(), y()); + } else if (!rect().contains(e->pos())) { + } + } +} + +void SeparatePanel::mouseMoveEvent(QMouseEvent *e) { + if (_dragging) { + if (!(e->buttons() & Qt::LeftButton)) { + _dragging = false; + } else { + move(_dragStartMyPosition + + (e->globalPos() - _dragStartMousePosition)); + } + } +} + +void SeparatePanel::mouseReleaseEvent(QMouseEvent *e) { + if (e->button() == Qt::LeftButton) { + _dragging = false; + } +} + +void SeparatePanel::leaveEventHook(QEvent *e) { + Ui::Tooltip::Hide(); +} + +void SeparatePanel::leaveToChildEvent(QEvent *e, QWidget *child) { + Ui::Tooltip::Hide(); +} + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/widgets/separate_panel.h b/Telegram/SourceFiles/ui/widgets/separate_panel.h new file mode 100644 index 000000000..6bcd6ce93 --- /dev/null +++ b/Telegram/SourceFiles/ui/widgets/separate_panel.h @@ -0,0 +1,104 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include "ui/rp_widget.h" +#include "boxes/abstract_box.h" + +namespace Ui { +class IconButton; +class FlatLabel; +template +class FadeWrapScaled; +} // namespace Ui + +namespace Window { +class LayerStackWidget; +} // namespace Window + +namespace Ui { + +class SeparatePanel : public Ui::RpWidget { +public: + SeparatePanel(); + + void setTitle(rpl::producer title); + void setInnerSize(QSize size); + + void showAndActivate(); + int hideAndDestroyGetDuration(); + + void showInner(base::unique_qptr inner); + void showBox( + object_ptr box, + LayerOptions options, + anim::type animated); + void showToast(const QString &text); + + rpl::producer<> backRequests() const; + rpl::producer<> closeRequests() const; + rpl::producer<> destroyRequests() const; + void setBackAllowed(bool allowed); + +protected: + void paintEvent(QPaintEvent *e) override; + void closeEvent(QCloseEvent *e) override; + void resizeEvent(QResizeEvent *e) override; + void focusInEvent(QFocusEvent *e) override; + void mousePressEvent(QMouseEvent *e) override; + void mouseReleaseEvent(QMouseEvent *e) override; + void mouseMoveEvent(QMouseEvent *e) override; + void leaveEventHook(QEvent *e) override; + void leaveToChildEvent(QEvent *e, QWidget *child) override; + void keyPressEvent(QKeyEvent *e) override; + +private: + void initControls(); + void initLayout(); + void initGeometry(QSize size); + void updateGeometry(QSize size); + void showControls(); + void updateControlsGeometry(); + void createBorderImage(); + void opacityCallback(); + void ensureLayerCreated(); + + void updateTitlePosition(); + void paintShadowBorder(Painter &p) const; + void paintOpaqueBorder(Painter &p) const; + + void toggleOpacityAnimation(bool visible); + void finishAnimating(); + void destroyDelayed(); + + object_ptr _close; + object_ptr _title = { nullptr }; + object_ptr> _back; + object_ptr _body; + base::unique_qptr _inner; + object_ptr _layer = { nullptr }; + rpl::event_stream<> _synteticBackRequests; + rpl::event_stream<> _destroyRequests; + + bool _useTransparency = true; + style::margins _padding; + + bool _dragging = false; + QPoint _dragStartMousePosition; + QPoint _dragStartMyPosition; + + Animation _titleLeft; + bool _visible = false; + + Animation _opacityAnimation; + QPixmap _animationCache; + QPixmap _borderParts; + +}; + +} // namespace Ui diff --git a/Telegram/gyp/telegram_sources.txt b/Telegram/gyp/telegram_sources.txt index 6e97a3c4a..04b1ca041 100644 --- a/Telegram/gyp/telegram_sources.txt +++ b/Telegram/gyp/telegram_sources.txt @@ -667,6 +667,8 @@ <(src_loc)/ui/widgets/multi_select.h <(src_loc)/ui/widgets/popup_menu.cpp <(src_loc)/ui/widgets/popup_menu.h +<(src_loc)/ui/widgets/separate_panel.cpp +<(src_loc)/ui/widgets/separate_panel.h <(src_loc)/ui/widgets/scroll_area.cpp <(src_loc)/ui/widgets/scroll_area.h <(src_loc)/ui/widgets/shadow.cpp