Add SeparatePanel and ShowMultilineToast.
This commit is contained in:
parent
49b4b35efa
commit
b77fb45083
11 changed files with 875 additions and 0 deletions
|
|
@ -161,6 +161,8 @@ PRIVATE
|
||||||
ui/toast/toast_manager.h
|
ui/toast/toast_manager.h
|
||||||
ui/toast/toast_widget.cpp
|
ui/toast/toast_widget.cpp
|
||||||
ui/toast/toast_widget.h
|
ui/toast/toast_widget.h
|
||||||
|
ui/toasts/common_toasts.cpp
|
||||||
|
ui/toasts/common_toasts.h
|
||||||
ui/widgets/box_content_divider.cpp
|
ui/widgets/box_content_divider.cpp
|
||||||
ui/widgets/box_content_divider.h
|
ui/widgets/box_content_divider.h
|
||||||
ui/widgets/buttons.cpp
|
ui/widgets/buttons.cpp
|
||||||
|
|
@ -197,6 +199,8 @@ PRIVATE
|
||||||
ui/widgets/popup_menu.h
|
ui/widgets/popup_menu.h
|
||||||
ui/widgets/rp_window.cpp
|
ui/widgets/rp_window.cpp
|
||||||
ui/widgets/rp_window.h
|
ui/widgets/rp_window.h
|
||||||
|
ui/widgets/separate_panel.cpp
|
||||||
|
ui/widgets/separate_panel.h
|
||||||
ui/widgets/scroll_area.cpp
|
ui/widgets/scroll_area.cpp
|
||||||
ui/widgets/scroll_area.h
|
ui/widgets/scroll_area.h
|
||||||
ui/widgets/side_bar_button.cpp
|
ui/widgets/side_bar_button.cpp
|
||||||
|
|
|
||||||
|
|
@ -143,4 +143,12 @@ QString Integration::phraseFormattingSpoiler() {
|
||||||
return "Spoiler";
|
return "Spoiler";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString Integration::phraseButtonOk() {
|
||||||
|
return "OK";
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Integration::phraseButtonCancel() {
|
||||||
|
return "Cancel";
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,8 @@
|
||||||
|
|
||||||
#include "base/basic_types.h"
|
#include "base/basic_types.h"
|
||||||
|
|
||||||
|
#include <rpl/producer.h>
|
||||||
|
|
||||||
#include <any>
|
#include <any>
|
||||||
|
|
||||||
// Methods that must be implemented outside lib_ui.
|
// Methods that must be implemented outside lib_ui.
|
||||||
|
|
@ -71,6 +73,8 @@ public:
|
||||||
[[nodiscard]] virtual QString phraseFormattingStrikeOut();
|
[[nodiscard]] virtual QString phraseFormattingStrikeOut();
|
||||||
[[nodiscard]] virtual QString phraseFormattingMonospace();
|
[[nodiscard]] virtual QString phraseFormattingMonospace();
|
||||||
[[nodiscard]] virtual QString phraseFormattingSpoiler();
|
[[nodiscard]] virtual QString phraseFormattingSpoiler();
|
||||||
|
[[nodiscard]] virtual QString phraseButtonOk();
|
||||||
|
[[nodiscard]] virtual QString phraseButtonCancel();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -157,3 +157,43 @@ boxLoadingSize: 20px;
|
||||||
boxDividerTop: icon {{ "box_divider_top", boxDividerFg }};
|
boxDividerTop: icon {{ "box_divider_top", boxDividerFg }};
|
||||||
boxDividerBottom: icon {{ "box_divider_bottom", boxDividerFg }};
|
boxDividerBottom: icon {{ "box_divider_bottom", boxDividerFg }};
|
||||||
boxDividerHeight: 10px;
|
boxDividerHeight: 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: icon {{ "box_button_back", boxTitleCloseFg }};
|
||||||
|
iconOver: icon {{ "box_button_back", boxTitleCloseFgOver }};
|
||||||
|
}
|
||||||
|
separatePanelDuration: 150;
|
||||||
|
|
||||||
|
webviewDialogButton: defaultBoxButton;
|
||||||
|
webviewDialogSubmit: RoundButton(defaultActiveButton) {
|
||||||
|
width: -48px;
|
||||||
|
height: 34px;
|
||||||
|
textTop: 7px;
|
||||||
|
font: font(14px semibold);
|
||||||
|
}
|
||||||
|
webviewDialogPadding: margins(8px, 12px, 15px, 12px);
|
||||||
|
|
|
||||||
30
ui/toasts/common_toasts.cpp
Normal file
30
ui/toasts/common_toasts.cpp
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
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/toasts/common_toasts.h"
|
||||||
|
|
||||||
|
#include "ui/toast/toast.h"
|
||||||
|
#include "styles/style_widgets.h"
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
|
||||||
|
base::weak_ptr<Toast::Instance> ShowMultilineToast(
|
||||||
|
MultilineToastArgs &&args) {
|
||||||
|
auto config = Ui::Toast::Config{
|
||||||
|
.text = std::move(args.text),
|
||||||
|
.st = &st::defaultMultilineToast,
|
||||||
|
.durationMs = (args.duration
|
||||||
|
? args.duration
|
||||||
|
: Ui::Toast::kDefaultDuration),
|
||||||
|
.multiline = true,
|
||||||
|
};
|
||||||
|
return args.parentOverride
|
||||||
|
? Ui::Toast::Show(args.parentOverride, std::move(config))
|
||||||
|
: Ui::Toast::Show(std::move(config));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Ui
|
||||||
27
ui/toasts/common_toasts.h
Normal file
27
ui/toasts/common_toasts.h
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
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/text/text_entity.h"
|
||||||
|
#include "base/weak_ptr.h"
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
namespace Toast {
|
||||||
|
class Instance;
|
||||||
|
} // namespace Toast
|
||||||
|
|
||||||
|
struct MultilineToastArgs {
|
||||||
|
QWidget *parentOverride = nullptr;
|
||||||
|
TextWithEntities text;
|
||||||
|
crl::time duration = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
base::weak_ptr<Toast::Instance> ShowMultilineToast(
|
||||||
|
MultilineToastArgs &&args);
|
||||||
|
|
||||||
|
} // namespace Ui
|
||||||
|
|
@ -10,7 +10,9 @@
|
||||||
#include "ui/rect_part.h"
|
#include "ui/rect_part.h"
|
||||||
#include "ui/integration.h"
|
#include "ui/integration.h"
|
||||||
|
|
||||||
|
#include <crl/crl.h>
|
||||||
#include <QtCore/QEvent>
|
#include <QtCore/QEvent>
|
||||||
|
#include <QtWidgets/QWidget>
|
||||||
|
|
||||||
class QPixmap;
|
class QPixmap;
|
||||||
class QImage;
|
class QImage;
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,8 @@
|
||||||
#include <QtWidgets/QTextEdit>
|
#include <QtWidgets/QTextEdit>
|
||||||
#include <QtCore/QTimer>
|
#include <QtCore/QTimer>
|
||||||
|
|
||||||
|
#include <rpl/variable.h>
|
||||||
|
|
||||||
class QTouchEvent;
|
class QTouchEvent;
|
||||||
class Painter;
|
class Painter;
|
||||||
|
|
||||||
|
|
|
||||||
641
ui/widgets/separate_panel.cpp
Normal file
641
ui/widgets/separate_panel.cpp
Normal file
|
|
@ -0,0 +1,641 @@
|
||||||
|
/*
|
||||||
|
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 "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/toasts/common_toasts.h"
|
||||||
|
#include "ui/widgets/tooltip.h"
|
||||||
|
#include "ui/platform/ui_platform_utility.h"
|
||||||
|
#include "ui/layers/layer_widget.h"
|
||||||
|
#include "styles/style_widgets.h"
|
||||||
|
#include "styles/style_layers.h"
|
||||||
|
#include "styles/palette.h"
|
||||||
|
#include "base/debug_log.h"
|
||||||
|
|
||||||
|
#include <QtGui/QWindow>
|
||||||
|
#include <QtGui/QScreen>
|
||||||
|
#include <QtWidgets/QApplication>
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
|
||||||
|
SeparatePanel::SeparatePanel()
|
||||||
|
: _close(this, st::separatePanelClose)
|
||||||
|
, _back(this, object_ptr<Ui::IconButton>(this, st::separatePanelBack))
|
||||||
|
, _body(this) {
|
||||||
|
setMouseTracking(true);
|
||||||
|
setWindowIcon(QGuiApplication::windowIcon());
|
||||||
|
initControls();
|
||||||
|
initLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SeparatePanel::setTitle(rpl::producer<QString> title) {
|
||||||
|
_title.create(this, std::move(title), st::separatePanelTitle);
|
||||||
|
_title->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||||
|
_title->show();
|
||||||
|
updateTitleGeometry(width());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SeparatePanel::initControls() {
|
||||||
|
widthValue(
|
||||||
|
) | rpl::start_with_next([=](int width) {
|
||||||
|
_back->moveToLeft(_padding.left(), _padding.top());
|
||||||
|
_close->moveToRight(_padding.right(), _padding.top());
|
||||||
|
if (_title) {
|
||||||
|
updateTitleGeometry(width);
|
||||||
|
}
|
||||||
|
}, 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.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SeparatePanel::updateTitleGeometry(int newWidth) {
|
||||||
|
_title->resizeToWidth(newWidth
|
||||||
|
- _padding.left() - _back->width()
|
||||||
|
- _padding.right() - _close->width());
|
||||||
|
updateTitlePosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SeparatePanel::updateTitlePosition() {
|
||||||
|
if (!_title) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto progress = _titleLeft.value(_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() | rpl::to_empty,
|
||||||
|
_synteticBackRequests.events());
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<> SeparatePanel::closeRequests() const {
|
||||||
|
return rpl::merge(
|
||||||
|
_close->clicks() | rpl::to_empty,
|
||||||
|
_userCloseRequests.events());
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<> SeparatePanel::closeEvents() const {
|
||||||
|
return _closeEvents.events();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SeparatePanel::setBackAllowed(bool allowed) {
|
||||||
|
if (allowed != _back->toggled()) {
|
||||||
|
_back->toggle(allowed, anim::type::normal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SeparatePanel::setHideOnDeactivate(bool hideOnDeactivate) {
|
||||||
|
_hideOnDeactivate = hideOnDeactivate;
|
||||||
|
if (!_hideOnDeactivate) {
|
||||||
|
showAndActivate();
|
||||||
|
} else if (!isActiveWindow()) {
|
||||||
|
LOG(("Export Info: Panel Hide On Inactive Change."));
|
||||||
|
hideGetDuration();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SeparatePanel::showAndActivate() {
|
||||||
|
if (isHidden()) {
|
||||||
|
while (const auto widget = QApplication::activePopupWidget()) {
|
||||||
|
if (!widget->close()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
toggleOpacityAnimation(true);
|
||||||
|
raise();
|
||||||
|
setWindowState(windowState() | Qt::WindowActive);
|
||||||
|
activateWindow();
|
||||||
|
setFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SeparatePanel::keyPressEvent(QKeyEvent *e) {
|
||||||
|
if (e->key() == Qt::Key_Escape) {
|
||||||
|
crl::on_main(this, [=] {
|
||||||
|
if (_back->toggled()) {
|
||||||
|
_synteticBackRequests.fire({});
|
||||||
|
} else {
|
||||||
|
_userCloseRequests.fire({});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return RpWidget::keyPressEvent(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SeparatePanel::eventHook(QEvent *e) {
|
||||||
|
if (e->type() == QEvent::WindowDeactivate && _hideOnDeactivate) {
|
||||||
|
LOG(("Export Info: Panel Hide On Inactive Window."));
|
||||||
|
hideGetDuration();
|
||||||
|
}
|
||||||
|
return RpWidget::eventHook(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SeparatePanel::initLayout() {
|
||||||
|
setWindowFlags(Qt::WindowFlags(Qt::FramelessWindowHint)
|
||||||
|
| Qt::WindowStaysOnTopHint
|
||||||
|
| Qt::NoDropShadowWindowHint
|
||||||
|
| Qt::Dialog);
|
||||||
|
setAttribute(Qt::WA_MacAlwaysShowToolWindow);
|
||||||
|
setAttribute(Qt::WA_NoSystemBackground, true);
|
||||||
|
setAttribute(Qt::WA_TranslucentBackground, true);
|
||||||
|
|
||||||
|
createBorderImage();
|
||||||
|
style::PaletteChanged(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
createBorderImage();
|
||||||
|
Ui::ForceFullRepaint(this);
|
||||||
|
}, lifetime());
|
||||||
|
|
||||||
|
Ui::Platform::InitOnTopPanel(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SeparatePanel::createBorderImage() {
|
||||||
|
const auto shadowPadding = st::callShadow.extend;
|
||||||
|
const auto cacheSize = st::separatePanelBorderCacheSize;
|
||||||
|
auto cache = QImage(
|
||||||
|
cacheSize * style::DevicePixelRatio(),
|
||||||
|
cacheSize * style::DevicePixelRatio(),
|
||||||
|
QImage::Format_ARGB32_Premultiplied);
|
||||||
|
cache.setDevicePixelRatio(style::DevicePixelRatio());
|
||||||
|
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 = Ui::PixmapFromImage(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::separatePanelDuration,
|
||||||
|
_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();
|
||||||
|
if (_inner) {
|
||||||
|
_inner->setFocus();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
finishClose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SeparatePanel::showControls() {
|
||||||
|
showChildren();
|
||||||
|
if (!_back->toggled()) {
|
||||||
|
_back->setVisible(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SeparatePanel::finishClose() {
|
||||||
|
hide();
|
||||||
|
crl::on_main(this, [=] {
|
||||||
|
if (isHidden() && !_visible && !_opacityAnimation.animating()) {
|
||||||
|
LOG(("Export Info: Panel Closed."));
|
||||||
|
_closeEvents.fire({});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
int SeparatePanel::hideGetDuration() {
|
||||||
|
LOG(("Export Info: Panel Hide Requested."));
|
||||||
|
toggleOpacityAnimation(false);
|
||||||
|
if (_animationCache.isNull()) {
|
||||||
|
finishClose();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return st::separatePanelDuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SeparatePanel::showBox(
|
||||||
|
object_ptr<Ui::BoxContent> box,
|
||||||
|
Ui::LayerOptions options,
|
||||||
|
anim::type animated) {
|
||||||
|
ensureLayerCreated();
|
||||||
|
_layer->showBox(std::move(box), options, animated);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SeparatePanel::showToast(const TextWithEntities &text) {
|
||||||
|
Ui::ShowMultilineToast({
|
||||||
|
.parentOverride = this,
|
||||||
|
.text = text,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void SeparatePanel::ensureLayerCreated() {
|
||||||
|
if (_layer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_layer = base::make_unique_q<Ui::LayerStackWidget>(_body);
|
||||||
|
_layer->setHideByBackgroundClick(false);
|
||||||
|
_layer->move(0, 0);
|
||||||
|
_body->sizeValue(
|
||||||
|
) | rpl::start_with_next([=](QSize size) {
|
||||||
|
_layer->resize(size);
|
||||||
|
}, _layer->lifetime());
|
||||||
|
_layer->hideFinishEvents(
|
||||||
|
) | rpl::filter([=] {
|
||||||
|
return _layer != nullptr; // Last hide finish is sent from destructor.
|
||||||
|
}) | rpl::start_with_next([=] {
|
||||||
|
destroyLayer();
|
||||||
|
}, _layer->lifetime());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SeparatePanel::destroyLayer() {
|
||||||
|
if (!_layer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto layer = base::take(_layer);
|
||||||
|
const auto resetFocus = Ui::InFocusChain(layer);
|
||||||
|
if (resetFocus) {
|
||||||
|
setFocus();
|
||||||
|
}
|
||||||
|
layer = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SeparatePanel::showInner(base::unique_qptr<Ui::RpWidget> 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 && !_inner->isHidden()) {
|
||||||
|
_inner->setFocus();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void SeparatePanel::setInnerSize(QSize size) {
|
||||||
|
Expects(!size.isEmpty());
|
||||||
|
|
||||||
|
if (rect().isEmpty()) {
|
||||||
|
initGeometry(size);
|
||||||
|
} else {
|
||||||
|
updateGeometry(size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QRect SeparatePanel::innerGeometry() const {
|
||||||
|
return _body->geometry();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SeparatePanel::initGeometry(QSize size) {
|
||||||
|
const auto active = QApplication::activeWindow();
|
||||||
|
const auto available = !active
|
||||||
|
? QGuiApplication::primaryScreen()->availableGeometry()
|
||||||
|
: active->screen()->availableGeometry();
|
||||||
|
const auto parentGeometry = (active
|
||||||
|
&& active->isVisible()
|
||||||
|
&& active->isActiveWindow())
|
||||||
|
? active->geometry()
|
||||||
|
: available;
|
||||||
|
|
||||||
|
auto center = parentGeometry.center();
|
||||||
|
if (size.height() > available.height()) {
|
||||||
|
size = QSize(size.width(), available.height());
|
||||||
|
}
|
||||||
|
if (center.x() + size.width() / 2
|
||||||
|
> available.x() + available.width()) {
|
||||||
|
center.setX(
|
||||||
|
available.x() + available.width() - size.width() / 2);
|
||||||
|
}
|
||||||
|
if (center.x() - size.width() / 2 < available.x()) {
|
||||||
|
center.setX(available.x() + size.width() / 2);
|
||||||
|
}
|
||||||
|
if (center.y() + size.height() / 2
|
||||||
|
> available.y() + available.height()) {
|
||||||
|
center.setY(
|
||||||
|
available.y() + available.height() - size.height() / 2);
|
||||||
|
}
|
||||||
|
if (center.y() - size.height() / 2 < available.y()) {
|
||||||
|
center.setY(available.y() + size.height() / 2);
|
||||||
|
}
|
||||||
|
_useTransparency = Ui::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 rect = [&] {
|
||||||
|
const QRect initRect(QPoint(), size);
|
||||||
|
return initRect.translated(center - initRect.center()).marginsAdded(_padding);
|
||||||
|
}();
|
||||||
|
setGeometry(rect);
|
||||||
|
setMinimumSize(rect.size());
|
||||||
|
setMaximumSize(rect.size());
|
||||||
|
updateControlsGeometry();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SeparatePanel::updateGeometry(QSize size) {
|
||||||
|
const auto rect = QRect(
|
||||||
|
x(),
|
||||||
|
y(),
|
||||||
|
_padding.left() + size.width() + _padding.right(),
|
||||||
|
_padding.top() + size.height() + _padding.bottom());
|
||||||
|
setGeometry(rect);
|
||||||
|
setMinimumSize(rect.size());
|
||||||
|
setMaximumSize(rect.size());
|
||||||
|
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.value(_visible ? 1. : 0.);
|
||||||
|
if (!_opacityAnimation.animating()) {
|
||||||
|
finishAnimating();
|
||||||
|
if (isHidden()) return;
|
||||||
|
} else {
|
||||||
|
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) {
|
||||||
|
paintShadowBorder(p);
|
||||||
|
} else {
|
||||||
|
paintOpaqueBorder(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SeparatePanel::paintShadowBorder(Painter &p) const {
|
||||||
|
const auto factor = style::DevicePixelRatio();
|
||||||
|
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) {
|
||||||
|
e->ignore();
|
||||||
|
_userCloseRequests.fire({});
|
||||||
|
}
|
||||||
|
|
||||||
|
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())) {
|
||||||
|
const auto dragViaSystem = [&] {
|
||||||
|
if (windowHandle()->startSystemMove()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}();
|
||||||
|
if (!dragViaSystem) {
|
||||||
|
_dragging = true;
|
||||||
|
_dragStartMousePosition = e->globalPos();
|
||||||
|
_dragStartMyPosition = QPoint(x(), y());
|
||||||
|
}
|
||||||
|
} else if (!rect().contains(e->pos()) && _hideOnDeactivate) {
|
||||||
|
LOG(("Export Info: Panel Hide On Click."));
|
||||||
|
hideGetDuration();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
_dragging = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SeparatePanel::leaveEventHook(QEvent *e) {
|
||||||
|
Ui::Tooltip::Hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SeparatePanel::leaveToChildEvent(QEvent *e, QWidget *child) {
|
||||||
|
Ui::Tooltip::Hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Ui
|
||||||
113
ui/widgets/separate_panel.h
Normal file
113
ui/widgets/separate_panel.h
Normal file
|
|
@ -0,0 +1,113 @@
|
||||||
|
/*
|
||||||
|
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 "ui/effects/animations.h"
|
||||||
|
#include "ui/layers/layer_widget.h"
|
||||||
|
#include "ui/text/text_entity.h"
|
||||||
|
|
||||||
|
class Painter;
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class BoxContent;
|
||||||
|
class IconButton;
|
||||||
|
class LayerStackWidget;
|
||||||
|
class FlatLabel;
|
||||||
|
template <typename Widget>
|
||||||
|
class FadeWrapScaled;
|
||||||
|
} // namespace Ui
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
|
||||||
|
class SeparatePanel final : public Ui::RpWidget {
|
||||||
|
public:
|
||||||
|
SeparatePanel();
|
||||||
|
|
||||||
|
void setTitle(rpl::producer<QString> title);
|
||||||
|
void setInnerSize(QSize size);
|
||||||
|
[[nodiscard]] QRect innerGeometry() const;
|
||||||
|
|
||||||
|
void setHideOnDeactivate(bool hideOnDeactivate);
|
||||||
|
void showAndActivate();
|
||||||
|
int hideGetDuration();
|
||||||
|
|
||||||
|
void showInner(base::unique_qptr<Ui::RpWidget> inner);
|
||||||
|
void showBox(
|
||||||
|
object_ptr<Ui::BoxContent> box,
|
||||||
|
Ui::LayerOptions options,
|
||||||
|
anim::type animated);
|
||||||
|
void showToast(const TextWithEntities &text);
|
||||||
|
void destroyLayer();
|
||||||
|
|
||||||
|
[[nodiscard]] rpl::producer<> backRequests() const;
|
||||||
|
[[nodiscard]] rpl::producer<> closeRequests() const;
|
||||||
|
[[nodiscard]] rpl::producer<> closeEvents() 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;
|
||||||
|
bool eventHook(QEvent *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 updateTitleGeometry(int newWidth);
|
||||||
|
void updateTitlePosition();
|
||||||
|
void paintShadowBorder(Painter &p) const;
|
||||||
|
void paintOpaqueBorder(Painter &p) const;
|
||||||
|
|
||||||
|
void toggleOpacityAnimation(bool visible);
|
||||||
|
void finishAnimating();
|
||||||
|
void finishClose();
|
||||||
|
|
||||||
|
object_ptr<Ui::IconButton> _close;
|
||||||
|
object_ptr<Ui::FlatLabel> _title = { nullptr };
|
||||||
|
object_ptr<Ui::FadeWrapScaled<Ui::IconButton>> _back;
|
||||||
|
object_ptr<Ui::RpWidget> _body;
|
||||||
|
base::unique_qptr<Ui::RpWidget> _inner;
|
||||||
|
base::unique_qptr<Ui::LayerStackWidget> _layer = { nullptr };
|
||||||
|
rpl::event_stream<> _synteticBackRequests;
|
||||||
|
rpl::event_stream<> _userCloseRequests;
|
||||||
|
rpl::event_stream<> _closeEvents;
|
||||||
|
|
||||||
|
bool _hideOnDeactivate = false;
|
||||||
|
bool _useTransparency = true;
|
||||||
|
style::margins _padding;
|
||||||
|
|
||||||
|
bool _dragging = false;
|
||||||
|
QPoint _dragStartMousePosition;
|
||||||
|
QPoint _dragStartMyPosition;
|
||||||
|
|
||||||
|
Ui::Animations::Simple _titleLeft;
|
||||||
|
bool _visible = false;
|
||||||
|
|
||||||
|
Ui::Animations::Simple _opacityAnimation;
|
||||||
|
QPixmap _animationCache;
|
||||||
|
QPixmap _borderParts;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Ui
|
||||||
|
|
@ -1445,6 +1445,10 @@ defaultToast: Toast {
|
||||||
durationSlide: 160;
|
durationSlide: 160;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
defaultMultilineToast: Toast(defaultToast) {
|
||||||
|
minWidth: msgMinWidth;
|
||||||
|
}
|
||||||
|
|
||||||
shakeShift: 4px;
|
shakeShift: 4px;
|
||||||
|
|
||||||
// Windows specific title
|
// Windows specific title
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue