mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-16 14:17:12 +02:00
Allow huge range of interface scales.
This commit is contained in:
parent
3532e187fd
commit
ff331c040a
16 changed files with 987 additions and 69 deletions
|
@ -1201,6 +1201,8 @@ PRIVATE
|
|||
settings/settings_privacy_controllers.h
|
||||
settings/settings_privacy_security.cpp
|
||||
settings/settings_privacy_security.h
|
||||
settings/settings_scale_preview.cpp
|
||||
settings/settings_scale_preview.h
|
||||
settings/settings_type.h
|
||||
storage/details/storage_file_utilities.cpp
|
||||
storage/details/storage_file_utilities.h
|
||||
|
|
|
@ -514,7 +514,8 @@ bool ChannelHasActiveCall(not_null<ChannelData*> channel) {
|
|||
|
||||
rpl::producer<QImage> PeerUserpicImageValue(
|
||||
not_null<PeerData*> peer,
|
||||
int size) {
|
||||
int size,
|
||||
std::optional<int> radius) {
|
||||
return [=](auto consumer) {
|
||||
auto result = rpl::lifetime();
|
||||
struct State {
|
||||
|
@ -541,7 +542,10 @@ rpl::producer<QImage> PeerUserpicImageValue(
|
|||
}
|
||||
state->key = key;
|
||||
state->empty = false;
|
||||
consumer.put_next(peer->generateUserpicImage(state->view, size));
|
||||
consumer.put_next(peer->generateUserpicImage(
|
||||
state->view,
|
||||
size,
|
||||
radius));
|
||||
};
|
||||
peer->session().changes().peerFlagsValue(
|
||||
peer,
|
||||
|
|
|
@ -133,7 +133,8 @@ inline auto PeerFullFlagValue(
|
|||
|
||||
[[nodiscard]] rpl::producer<QImage> PeerUserpicImageValue(
|
||||
not_null<PeerData*> peer,
|
||||
int size);
|
||||
int size,
|
||||
std::optional<int> radius = {});
|
||||
|
||||
[[nodiscard]] const AllowedReactions &PeerAllowedReactions(
|
||||
not_null<PeerData*> peer);
|
||||
|
|
|
@ -47,7 +47,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
namespace {
|
||||
|
||||
const auto kPsaForwardedPrefix = "cloud_lng_forwarded_psa_";
|
||||
constexpr auto kReplyBarAlpha = 230. / 255.;
|
||||
|
||||
} // namespace
|
||||
|
||||
|
@ -465,7 +464,7 @@ void HistoryMessageReply::paint(
|
|||
st::msgReplyBarSize.height(),
|
||||
w + 2 * x);
|
||||
const auto opacity = p.opacity();
|
||||
p.setOpacity(opacity * kReplyBarAlpha);
|
||||
p.setOpacity(opacity * kBarAlpha);
|
||||
p.fillRect(rbar, bar);
|
||||
p.setOpacity(opacity);
|
||||
}
|
||||
|
|
|
@ -195,6 +195,8 @@ struct HistoryMessageReply
|
|||
Expects(replyToVia == nullptr);
|
||||
}
|
||||
|
||||
static constexpr auto kBarAlpha = 230. / 255.;
|
||||
|
||||
bool updateData(not_null<HistoryItem*> holder, bool force = false);
|
||||
|
||||
// Must be called before destructor.
|
||||
|
|
|
@ -42,13 +42,19 @@ settingsOptionDisabled: SettingsButton(settingsButtonNoIcon) {
|
|||
settingsSectionSkip: 6px;
|
||||
settingsSeparatorPadding: margins(22px, infoProfileSkip, 0px, infoProfileSkip);
|
||||
settingsButtonRightSkip: 23px;
|
||||
settingsScalePadding: margins(63px, 7px, 21px, 4px);
|
||||
settingsScalePadding: margins(60px, 7px, 22px, 4px);
|
||||
settingsBigScalePadding: margins(21px, 7px, 21px, 4px);
|
||||
settingsSlider: SettingsSlider(defaultSettingsSlider) {
|
||||
barFg: windowBgOver;
|
||||
labelFg: windowSubTextFg;
|
||||
labelFgActive: windowActiveTextFg;
|
||||
}
|
||||
settingsScale: MediaSlider(defaultContinuousSlider) {
|
||||
seekSize: size(15px, 15px);
|
||||
}
|
||||
settingsScaleLabel: FlatLabel(defaultFlatLabel) {
|
||||
textFg: windowActiveTextFg;
|
||||
}
|
||||
settingsUpdateToggle: SettingsButton(settingsButtonNoIcon) {
|
||||
height: 40px;
|
||||
padding: margins(22px, 8px, 22px, 8px);
|
||||
|
|
|
@ -26,6 +26,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "ui/widgets/labels.h"
|
||||
#include "ui/widgets/box_content_divider.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/continuous_sliders.h"
|
||||
#include "ui/widgets/menu/menu_add_action_callback.h"
|
||||
#include "ui/painter.h"
|
||||
#include "boxes/abstract_box.h"
|
||||
|
@ -393,4 +394,38 @@ void FillMenu(
|
|||
}
|
||||
}
|
||||
|
||||
SliderWithLabel MakeSliderWithLabel(
|
||||
QWidget *parent,
|
||||
const style::MediaSlider &sliderSt,
|
||||
const style::FlatLabel &labelSt,
|
||||
int skip,
|
||||
int minLabelWidth) {
|
||||
auto result = object_ptr<Ui::RpWidget>(parent);
|
||||
const auto raw = result.data();
|
||||
const auto height = std::max(
|
||||
sliderSt.seekSize.height(),
|
||||
labelSt.style.font->height);
|
||||
raw->resize(sliderSt.seekSize.width(), height);
|
||||
const auto slider = Ui::CreateChild<Ui::MediaSlider>(raw, sliderSt);
|
||||
const auto label = Ui::CreateChild<Ui::FlatLabel>(raw, labelSt);
|
||||
slider->resize(slider->width(), sliderSt.seekSize.height());
|
||||
rpl::combine(
|
||||
raw->sizeValue(),
|
||||
label->sizeValue()
|
||||
) | rpl::start_with_next([=](QSize outer, QSize size) {
|
||||
const auto right = std::max(size.width(), minLabelWidth) + skip;
|
||||
label->moveToRight(0, (outer.height() - size.height()) / 2);
|
||||
const auto width = std::max(
|
||||
sliderSt.seekSize.width(),
|
||||
outer.width() - right);
|
||||
slider->resizeToWidth(width);
|
||||
slider->moveToLeft(0, (outer.height() - slider->height()) / 2);
|
||||
}, label->lifetime());
|
||||
return {
|
||||
.widget = std::move(result),
|
||||
.slider = slider,
|
||||
.label = label,
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace Settings
|
||||
|
|
|
@ -25,6 +25,7 @@ class VerticalLayout;
|
|||
class FlatLabel;
|
||||
class SettingsButton;
|
||||
class AbstractButton;
|
||||
class MediaSlider;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Ui::Menu {
|
||||
|
@ -38,6 +39,7 @@ class SessionController;
|
|||
namespace style {
|
||||
struct FlatLabel;
|
||||
struct SettingsButton;
|
||||
struct MediaSlider;
|
||||
} // namespace style
|
||||
|
||||
namespace Lottie {
|
||||
|
@ -228,4 +230,16 @@ void FillMenu(
|
|||
Fn<void(Type)> showOther,
|
||||
Ui::Menu::MenuCallback addAction);
|
||||
|
||||
struct SliderWithLabel {
|
||||
object_ptr<Ui::RpWidget> widget;
|
||||
not_null<Ui::MediaSlider*> slider;
|
||||
not_null<Ui::FlatLabel*> label;
|
||||
};
|
||||
[[nodiscard]] SliderWithLabel MakeSliderWithLabel(
|
||||
QWidget *parent,
|
||||
const style::MediaSlider &sliderSt,
|
||||
const style::FlatLabel &labelSt,
|
||||
int skip,
|
||||
int minLabelWidth = 0);
|
||||
|
||||
} // namespace Settings
|
||||
|
|
|
@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "settings/settings_folders.h"
|
||||
#include "settings/settings_calls.h"
|
||||
#include "settings/settings_premium.h"
|
||||
#include "settings/settings_scale_preview.h"
|
||||
#include "boxes/language_box.h"
|
||||
#include "boxes/username_box.h"
|
||||
#include "boxes/about_box.h"
|
||||
|
@ -27,7 +28,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "ui/wrap/slide_wrap.h"
|
||||
#include "ui/wrap/padding_wrap.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "ui/widgets/discrete_sliders.h"
|
||||
#include "ui/widgets/continuous_sliders.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/toast/toast.h"
|
||||
|
@ -421,34 +422,58 @@ void SetupInterfaceScale(
|
|||
{ icon ? &st::settingsIconInterfaceScale : nullptr, kIconLightOrange }
|
||||
)->toggleOn(toggled->events_starting_with_copy(switched));
|
||||
|
||||
const auto slider = container->add(
|
||||
object_ptr<Ui::SettingsSlider>(container, st::settingsSlider),
|
||||
icon ? st::settingsScalePadding : st::settingsBigScalePadding);
|
||||
|
||||
static const auto ScaleValues = [&] {
|
||||
auto values = (cIntRetinaFactor() > 1)
|
||||
? std::vector<int>{ 100, 110, 120, 130, 140, 150 }
|
||||
: std::vector<int>{ 100, 125, 150, 200, 250, 300 };
|
||||
if (cConfigScale() == style::kScaleAuto) {
|
||||
return values;
|
||||
const auto ratio = style::DevicePixelRatio();
|
||||
const auto scaleMin = style::kScaleMin;
|
||||
const auto scaleMax = style::kScaleMax / ratio;
|
||||
const auto scaleConfig = cConfigScale();
|
||||
const auto step = 5;
|
||||
Assert(!((scaleMax - scaleMin) % step));
|
||||
auto values = std::vector<int>();
|
||||
for (auto i = scaleMin; i != scaleMax; i += step) {
|
||||
values.push_back(i);
|
||||
if (scaleConfig > i && scaleConfig < i + step) {
|
||||
values.push_back(scaleConfig);
|
||||
}
|
||||
if (ranges::find(values, cConfigScale()) == end(values)) {
|
||||
values.push_back(cConfigScale());
|
||||
}
|
||||
return values;
|
||||
}();
|
||||
}
|
||||
values.push_back(scaleMax);
|
||||
const auto valuesCount = int(values.size());
|
||||
|
||||
const auto sectionFromScale = [](int scale) {
|
||||
const auto valueFromScale = [=](int scale) {
|
||||
scale = cEvalScale(scale);
|
||||
auto result = 0;
|
||||
for (const auto value : ScaleValues) {
|
||||
for (const auto value : values) {
|
||||
if (scale == value) {
|
||||
break;
|
||||
}
|
||||
++result;
|
||||
}
|
||||
return (result == ScaleValues.size()) ? (result - 1) : result;
|
||||
return ((result == valuesCount) ? (result - 1) : result)
|
||||
/ float64(valuesCount - 1);
|
||||
};
|
||||
auto sliderWithLabel = MakeSliderWithLabel(
|
||||
container,
|
||||
st::settingsScale,
|
||||
st::settingsScaleLabel,
|
||||
st::normalFont->spacew * 2,
|
||||
st::settingsScaleLabel.style.font->width("300%"));
|
||||
container->add(
|
||||
std::move(sliderWithLabel.widget),
|
||||
icon ? st::settingsScalePadding : st::settingsBigScalePadding);
|
||||
const auto slider = sliderWithLabel.slider;
|
||||
const auto label = sliderWithLabel.label;
|
||||
|
||||
const auto updateLabel = [=](int scale) {
|
||||
const auto labelText = [&](int scale) {
|
||||
if constexpr (Platform::IsMac()) {
|
||||
return QString::number(scale) + '%';
|
||||
} else {
|
||||
return QString::number(scale * ratio) + '%';
|
||||
}
|
||||
};
|
||||
label->setText(labelText(cEvalScale(scale)));
|
||||
};
|
||||
updateLabel(cConfigScale());
|
||||
|
||||
const auto inSetScale = container->lifetime().make_state<bool>();
|
||||
const auto setScale = [=](int scale, const auto &repeatSetScale) -> void {
|
||||
if (*inSetScale) {
|
||||
|
@ -457,8 +482,9 @@ void SetupInterfaceScale(
|
|||
*inSetScale = true;
|
||||
const auto guard = gsl::finally([=] { *inSetScale = false; });
|
||||
|
||||
updateLabel(scale);
|
||||
toggled->fire(scale == style::kScaleAuto);
|
||||
slider->setActiveSection(sectionFromScale(scale));
|
||||
slider->setValue(valueFromScale(scale));
|
||||
if (cEvalScale(scale) != cEvalScale(cConfigScale())) {
|
||||
const auto confirmed = crl::guard(button, [=] {
|
||||
cSetConfigScale(scale);
|
||||
|
@ -484,31 +510,37 @@ void SetupInterfaceScale(
|
|||
}
|
||||
};
|
||||
|
||||
const auto label = [](int scale) {
|
||||
if constexpr (Platform::IsMac()) {
|
||||
return QString::number(scale) + '%';
|
||||
} else {
|
||||
return QString::number(scale * cIntRetinaFactor()) + '%';
|
||||
const auto shown = container->lifetime().make_state<bool>();
|
||||
const auto togglePreview = SetupScalePreview(window, slider);
|
||||
const auto toggleForScale = [=](int scale) {
|
||||
scale = cEvalScale(scale);
|
||||
const auto show = *shown
|
||||
? ScalePreviewShow::Update
|
||||
: ScalePreviewShow::Show;
|
||||
*shown = true;
|
||||
auto index = 0;
|
||||
for (auto i = 0; i != valuesCount; ++i) {
|
||||
if (values[i] <= scale
|
||||
&& (i + 1 == valuesCount || values[i + 1] > scale)) {
|
||||
const auto x = (slider->width() * i) / (valuesCount - 1);
|
||||
const auto globalX = slider->mapToGlobal(QPoint(x, 0)).x();
|
||||
togglePreview(show, scale, globalX);
|
||||
return;
|
||||
}
|
||||
}
|
||||
togglePreview(show, scale, QCursor::pos().x());
|
||||
};
|
||||
const auto scaleByIndex = [](int index) {
|
||||
return *(ScaleValues.begin() + index);
|
||||
const auto toggleHidePreview = [=] {
|
||||
togglePreview(ScalePreviewShow::Hide, 0, 0);
|
||||
*shown = false;
|
||||
};
|
||||
|
||||
for (const auto value : ScaleValues) {
|
||||
slider->addSection(label(value));
|
||||
}
|
||||
slider->setActiveSectionFast(sectionFromScale(cConfigScale()));
|
||||
slider->sectionActivated(
|
||||
) | rpl::map([=](int section) {
|
||||
return scaleByIndex(section);
|
||||
}) | rpl::filter([=](int scale) {
|
||||
return cEvalScale(scale) != cEvalScale(cConfigScale());
|
||||
}) | rpl::start_with_next([=](int scale) {
|
||||
setScale(
|
||||
(scale == cScreenScale()) ? style::kScaleAuto : scale,
|
||||
setScale);
|
||||
}, slider->lifetime());
|
||||
slider->setPseudoDiscrete(
|
||||
valuesCount,
|
||||
[=](int index) { return values[index]; },
|
||||
cConfigScale(),
|
||||
[=](int scale) { updateLabel(scale); toggleForScale(scale); },
|
||||
[=](int scale) { toggleHidePreview(); setScale(scale, setScale); });
|
||||
|
||||
button->toggledValue(
|
||||
) | rpl::map([](bool checked) {
|
||||
|
|
733
Telegram/SourceFiles/settings/settings_scale_preview.cpp
Normal file
733
Telegram/SourceFiles/settings/settings_scale_preview.cpp
Normal file
|
@ -0,0 +1,733 @@
|
|||
/*
|
||||
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 "settings/settings_scale_preview.h"
|
||||
|
||||
#include "base/platform/base_platform_info.h"
|
||||
#include "base/event_filter.h"
|
||||
#include "data/data_user.h"
|
||||
#include "data/data_peer_values.h"
|
||||
#include "history/history_item_components.h"
|
||||
#include "main/main_session.h"
|
||||
#include "ui/chat/chat_theme.h"
|
||||
#include "ui/image/image_prepare.h"
|
||||
#include "ui/platform/ui_platform_utility.h"
|
||||
#include "ui/text/text_options.h"
|
||||
#include "ui/widgets/shadow.h"
|
||||
#include "ui/cached_round_corners.h"
|
||||
#include "ui/painter.h"
|
||||
#include "window/themes/window_theme.h"
|
||||
#include "window/section_widget.h"
|
||||
#include "window/window_controller.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "styles/style_chat.h"
|
||||
|
||||
#include <QtGui/QGuiApplication>
|
||||
#include <QtGui/QScreen>
|
||||
|
||||
namespace Settings {
|
||||
namespace {
|
||||
|
||||
constexpr auto kMinTextWidth = 120;
|
||||
constexpr auto kMaxTextWidth = 320;
|
||||
constexpr auto kMaxTextLines = 3;
|
||||
|
||||
class Preview final {
|
||||
public:
|
||||
Preview(QWidget *parent, rpl::producer<QImage> userpic);
|
||||
|
||||
void toggle(ScalePreviewShow show, int scale, int globalX);
|
||||
|
||||
private:
|
||||
void init();
|
||||
void initAsWindow();
|
||||
|
||||
void updateToScale(int scale);
|
||||
void updateGlobalPosition(int globalX);
|
||||
void updateGlobalPosition();
|
||||
void updateWindowGlobalPosition(QPoint global, int globalX);
|
||||
void updateOuterPosition(int globalX);
|
||||
[[nodiscard]] QRect adjustByScreenGeometry(QRect geometry) const;
|
||||
|
||||
void toggleShown(bool shown);
|
||||
void toggleFilter();
|
||||
void update();
|
||||
|
||||
void paint(Painter &p, QRect clip);
|
||||
void paintLayer(Painter &p, QRect clip);
|
||||
void paintInner(Painter &p, QRect clip);
|
||||
void paintUserpic(Painter &p, QRect clip);
|
||||
void paintBubble(Painter &p, QRect clip);
|
||||
void paintContent(Painter &p, QRect clip);
|
||||
void paintReply(Painter &p, QRect clip);
|
||||
void paintMessage(Painter &p, QRect clip);
|
||||
|
||||
void validateUserpicCache();
|
||||
void validateBubbleCache();
|
||||
void validateShadowCache();
|
||||
|
||||
[[nodiscard]] int scaled(int value) const;
|
||||
[[nodiscard]] QPoint scaled(QPoint value) const;
|
||||
[[nodiscard]] QSize scaled(QSize value) const;
|
||||
[[nodiscard]] QRect scaled(QRect value) const;
|
||||
[[nodiscard]] QMargins scaled(QMargins value) const;
|
||||
[[nodiscard]] style::font scaled(
|
||||
const style::font &value, int size) const;
|
||||
[[nodiscard]] style::TextStyle scaled(
|
||||
const style::TextStyle &value,
|
||||
int fontSize,
|
||||
int lineHeight) const;
|
||||
[[nodiscard]] QImage scaled(
|
||||
const style::icon &icon,
|
||||
const QColor &color) const;
|
||||
|
||||
Ui::RpWidget _widget;
|
||||
Ui::ChatTheme _theme;
|
||||
style::TextStyle _nameStyle = st::fwdTextStyle;
|
||||
Ui::Text::String _nameText = { kMaxTextWidth / 3 };
|
||||
style::TextStyle _textStyle = st::messageTextStyle;
|
||||
Ui::Text::String _replyText = { kMaxTextWidth / 3 };
|
||||
Ui::Text::String _messageText = { kMaxTextWidth / 3 };
|
||||
style::Shadow _shadow = st::callShadow;
|
||||
std::array<QImage, 4> _shadowSides;
|
||||
std::array<QImage, 4> _shadowCorners;
|
||||
Ui::CornersPixmaps _bubbleCorners;
|
||||
QPixmap _bubbleShadowBottomRight;
|
||||
int _bubbleShadow = 0;
|
||||
int _localShiftLeft = 0;
|
||||
QImage _bubbleTail;
|
||||
QRect _replyBar;
|
||||
QRect _name;
|
||||
QRect _reply;
|
||||
QRect _message;
|
||||
QRect _content;
|
||||
QRect _bubble;
|
||||
QRect _userpic;
|
||||
QRect _inner;
|
||||
QRect _outer;
|
||||
QSize _minOuterSize;
|
||||
QSize _maxOuterSize;
|
||||
QImage _layer, _canvas;
|
||||
QPoint _cursor;
|
||||
std::array<QImage, 4> _canvasCornerMasks;
|
||||
QImage _userpicOriginal;
|
||||
QImage _userpicImage;
|
||||
int _scale = 0;
|
||||
int _ratio = 0;
|
||||
bool _window = false;
|
||||
|
||||
Ui::Animations::Simple _shownAnimation;
|
||||
bool _shown = false;
|
||||
|
||||
std::unique_ptr<QObject> _filter;
|
||||
|
||||
};
|
||||
|
||||
[[nodiscard]] bool UseSeparateWindow() {
|
||||
return !Platform::IsWayland()
|
||||
&& Ui::Platform::TranslucentWindowsSupported();
|
||||
}
|
||||
|
||||
Preview::Preview(QWidget *parent, rpl::producer<QImage> userpic)
|
||||
: _widget(parent)
|
||||
, _ratio(style::DevicePixelRatio())
|
||||
, _window(UseSeparateWindow()) {
|
||||
std::move(userpic) | rpl::start_with_next([=](QImage &&userpic) {
|
||||
_userpicOriginal = std::move(userpic);
|
||||
if (!_userpicImage.isNull()) {
|
||||
_userpicImage = {};
|
||||
update();
|
||||
}
|
||||
}, _widget.lifetime());
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
void Preview::toggle(ScalePreviewShow show, int scale, int globalX) {
|
||||
if (show == ScalePreviewShow::Hide) {
|
||||
toggleShown(false);
|
||||
return;
|
||||
} else if (show == ScalePreviewShow::Update && !_shown) {
|
||||
return;
|
||||
}
|
||||
updateToScale(scale);
|
||||
updateGlobalPosition(globalX);
|
||||
if (_widget.isHidden()) {
|
||||
Ui::Platform::UpdateOverlayed(&_widget);
|
||||
}
|
||||
toggleShown(true);
|
||||
}
|
||||
|
||||
void Preview::toggleShown(bool shown) {
|
||||
if (_shown == shown) {
|
||||
return;
|
||||
}
|
||||
_shown = shown;
|
||||
toggleFilter();
|
||||
if (_shown) {
|
||||
_widget.show();
|
||||
} else if (_widget.isHidden()) {
|
||||
_shownAnimation.stop();
|
||||
return;
|
||||
}
|
||||
const auto callback = [=] {
|
||||
update();
|
||||
if (!_shown && !_shownAnimation.animating()) {
|
||||
_widget.hide();
|
||||
}
|
||||
};
|
||||
_shownAnimation.start(
|
||||
callback,
|
||||
shown ? 0. : 1.,
|
||||
shown ? 1. : 0.,
|
||||
st::slideWrapDuration);
|
||||
}
|
||||
|
||||
void Preview::toggleFilter() {
|
||||
if (!_shown) {
|
||||
_filter = nullptr;
|
||||
return;
|
||||
} else if (_filter) {
|
||||
return;
|
||||
}
|
||||
_filter = std::make_unique<QObject>();
|
||||
const auto watch = [&](QWidget *widget, const auto &self) -> void {
|
||||
if (!widget) {
|
||||
return;
|
||||
}
|
||||
base::install_event_filter(_filter.get(), widget, [=](
|
||||
not_null<QEvent*> e) {
|
||||
if (e->type() == QEvent::Move
|
||||
|| e->type() == QEvent::Resize
|
||||
|| e->type() == QEvent::Show
|
||||
|| e->type() == QEvent::ShowToParent
|
||||
|| e->type() == QEvent::ZOrderChange) {
|
||||
updateGlobalPosition();
|
||||
}
|
||||
return base::EventFilterResult::Continue;
|
||||
});
|
||||
if (!_window && widget == _widget.window()) {
|
||||
return;
|
||||
}
|
||||
self(widget->parentWidget(), self);
|
||||
};
|
||||
watch(_widget.parentWidget(), watch);
|
||||
|
||||
const auto checkDeactivation = [=](Qt::ApplicationState state) {
|
||||
if (state != Qt::ApplicationActive) {
|
||||
toggle(ScalePreviewShow::Hide, 0, 0);
|
||||
}
|
||||
};
|
||||
QObject::connect(
|
||||
qApp,
|
||||
&QGuiApplication::applicationStateChanged,
|
||||
_filter.get(),
|
||||
checkDeactivation,
|
||||
Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void Preview::update() {
|
||||
_widget.update(_outer);
|
||||
}
|
||||
|
||||
void Preview::init() {
|
||||
const auto background = Window::Theme::Background();
|
||||
const auto &paper = background->paper();
|
||||
_theme.setBackground({
|
||||
.prepared = background->prepared(),
|
||||
.preparedForTiled = background->preparedForTiled(),
|
||||
.gradientForFill = background->gradientForFill(),
|
||||
.colorForFill = background->colorForFill(),
|
||||
.colors = paper.backgroundColors(),
|
||||
.patternOpacity = paper.patternOpacity(),
|
||||
.gradientRotation = paper.gradientRotation(),
|
||||
.isPattern = paper.isPattern(),
|
||||
.tile = background->tile(),
|
||||
});
|
||||
|
||||
_widget.paintRequest(
|
||||
) | rpl::start_with_next([=](QRect clip) {
|
||||
auto p = Painter(&_widget);
|
||||
paint(p, clip);
|
||||
}, _widget.lifetime());
|
||||
|
||||
style::PaletteChanged(
|
||||
) | rpl::start_with_next([=] {
|
||||
_bubbleCorners = {};
|
||||
_bubbleTail = {};
|
||||
_bubbleShadowBottomRight = {};
|
||||
update();
|
||||
}, _widget.lifetime());
|
||||
|
||||
if (_window) {
|
||||
initAsWindow();
|
||||
updateToScale(style::kScaleMin);
|
||||
_minOuterSize = _outer.size();
|
||||
updateToScale(style::kScaleMax / _ratio);
|
||||
_maxOuterSize = _outer.size();
|
||||
}
|
||||
}
|
||||
|
||||
int Preview::scaled(int value) const {
|
||||
return style::ConvertScale(value, _scale);
|
||||
}
|
||||
|
||||
QPoint Preview::scaled(QPoint value) const {
|
||||
return { scaled(value.x()), scaled(value.y()) };
|
||||
}
|
||||
|
||||
QSize Preview::scaled(QSize value) const {
|
||||
return { scaled(value.width()), scaled(value.height()) };
|
||||
}
|
||||
|
||||
QRect Preview::scaled(QRect value) const {
|
||||
return { scaled(value.topLeft()), scaled(value.size()) };
|
||||
}
|
||||
|
||||
QMargins Preview::scaled(QMargins value) const {
|
||||
return {
|
||||
scaled(value.left()),
|
||||
scaled(value.top()),
|
||||
scaled(value.right()),
|
||||
scaled(value.bottom()),
|
||||
};
|
||||
}
|
||||
|
||||
style::font Preview::scaled(const style::font &font, int size) const {
|
||||
return style::font(scaled(size), font->flags(), font->family());
|
||||
}
|
||||
|
||||
style::TextStyle Preview::scaled(
|
||||
const style::TextStyle &value,
|
||||
int fontSize,
|
||||
int lineHeight) const {
|
||||
return {
|
||||
.font = scaled(value.font, fontSize),
|
||||
.linkFont = scaled(value.linkFont, fontSize),
|
||||
.linkFontOver = scaled(value.linkFontOver, fontSize),
|
||||
.lineHeight = scaled(value.lineHeight),
|
||||
};
|
||||
}
|
||||
|
||||
QImage Preview::scaled(
|
||||
const style::icon &icon,
|
||||
const QColor &color) const {
|
||||
return icon.instance(color, _scale);
|
||||
}
|
||||
|
||||
void Preview::updateToScale(int scale) {
|
||||
using style::ConvertScale;
|
||||
|
||||
if (_scale == scale) {
|
||||
return;
|
||||
}
|
||||
_scale = scale;
|
||||
_nameStyle = scaled(_nameStyle, 13, 0);
|
||||
_textStyle = scaled(_textStyle, 13, 0);
|
||||
_nameText.setText(
|
||||
_nameStyle,
|
||||
u"Bob Harris"_q,
|
||||
Ui::NameTextOptions());
|
||||
_replyText.setText(
|
||||
_textStyle,
|
||||
u"Good morning!"_q,
|
||||
Ui::ItemTextDefaultOptions());
|
||||
_messageText.setText(
|
||||
_textStyle,
|
||||
u"Do you know what time it is?"_q,
|
||||
Ui::ItemTextDefaultOptions());
|
||||
|
||||
_replyBar = QRect(
|
||||
scaled(1), // st::msgReplyBarPos.x(),
|
||||
scaled(6) + 0,// st::msgReplyPadding.top() + st::msgReplyBarPos.y(),
|
||||
scaled(2), //st::msgReplyBarSize.width(),
|
||||
scaled(36)); // st::msgReplyBarSize.height(),
|
||||
const auto namePosition = QPoint(
|
||||
scaled(10), // st::msgReplyBarSkip
|
||||
scaled(6)); // st::msgReplyPadding.top()
|
||||
const auto replyPosition = QPoint(
|
||||
scaled(10), // st::msgReplyBarSkip
|
||||
scaled(6) + _nameStyle.font->height); // st::msgReplyPadding.top()
|
||||
|
||||
const auto wantedWidth = std::max({
|
||||
namePosition.x() + _nameText.maxWidth(),
|
||||
replyPosition.x() + _replyText.maxWidth(),
|
||||
_messageText.maxWidth(),
|
||||
});
|
||||
|
||||
const auto minTextWidth = scaled(kMinTextWidth);
|
||||
const auto maxTextWidth = scaled(kMaxTextWidth);
|
||||
const auto messageWidth = std::clamp(
|
||||
wantedWidth,
|
||||
minTextWidth,
|
||||
maxTextWidth);
|
||||
const auto messageHeight = std::min(
|
||||
_messageText.countHeight(maxTextWidth),
|
||||
kMaxTextLines * _textStyle.font->height);
|
||||
|
||||
_name = QRect(
|
||||
namePosition,
|
||||
QSize(messageWidth - namePosition.x(), _nameStyle.font->height));
|
||||
_reply = QRect(
|
||||
replyPosition,
|
||||
QSize(messageWidth - replyPosition.x(), _textStyle.font->height));
|
||||
_message = QRect(0, 0, messageWidth, messageHeight);
|
||||
|
||||
// replyBar.bottom + st::msgReplyPadding.bottom();
|
||||
const auto replySkip = _replyBar.y() + _replyBar.height() + scaled(6);
|
||||
_message.moveTop(replySkip);
|
||||
|
||||
_content = QRect(0, 0, messageWidth, replySkip + messageHeight);
|
||||
|
||||
const auto msgPadding = scaled(QMargins(13, 7, 13, 8)); // st::msgPadding
|
||||
_bubble = _content.marginsAdded(msgPadding);
|
||||
_content.moveTopLeft(-_bubble.topLeft());
|
||||
_bubble.moveTopLeft({});
|
||||
_bubbleShadow = scaled(2); // st::msgShadow
|
||||
_bubbleCorners = {};
|
||||
_bubbleTail = {};
|
||||
_bubbleShadowBottomRight = {};
|
||||
|
||||
const auto hasUserpic = !_userpicOriginal.isNull();
|
||||
const auto bubbleMargin = scaled(QMargins(20, 16, 20, 16));
|
||||
const auto userpicSkip = hasUserpic ? scaled(40) : 0; // st::msgPhotoSkip
|
||||
_inner = _bubble.marginsAdded(
|
||||
bubbleMargin + QMargins(userpicSkip, 0, 0, 0));
|
||||
_bubble.moveTopLeft(-_inner.topLeft());
|
||||
_inner.moveTopLeft({});
|
||||
if (hasUserpic) {
|
||||
const auto userpicSize = scaled(33); // st::msgPhotoSize
|
||||
_userpic = QRect(
|
||||
bubbleMargin.left(),
|
||||
_bubble.y() + _bubble.height() - userpicSize,
|
||||
userpicSize,
|
||||
userpicSize);
|
||||
_userpicImage = {};
|
||||
}
|
||||
|
||||
_shadow.extend = scaled(QMargins(9, 8, 9, 10)); // st::callShadow.extend
|
||||
_shadowSides = {};
|
||||
_shadowCorners = {};
|
||||
|
||||
update();
|
||||
_outer = _inner.marginsAdded(_shadow.extend);
|
||||
_inner.moveTopLeft(-_outer.topLeft());
|
||||
_outer.moveTopLeft({});
|
||||
|
||||
_layer = QImage(
|
||||
_outer.size() * _ratio,
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
_layer.setDevicePixelRatio(_ratio);
|
||||
_canvas = QImage(
|
||||
_inner.size() * _ratio,
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
_canvas.setDevicePixelRatio(_ratio);
|
||||
_canvas.fill(Qt::transparent);
|
||||
|
||||
_canvasCornerMasks = Images::CornersMask(scaled(6)); // st::callRadius
|
||||
}
|
||||
|
||||
void Preview::updateGlobalPosition(int globalX) {
|
||||
const auto parent = _widget.parentWidget();
|
||||
if (_window) {
|
||||
const auto global = parent->mapToGlobal(QPoint());
|
||||
_localShiftLeft = globalX - global.x();
|
||||
updateWindowGlobalPosition(global, globalX);
|
||||
} else {
|
||||
const auto position = parent->pos();
|
||||
+ QPoint(parent->width() / 2, 0)
|
||||
- QPoint(_outer.width() / 2, _outer.height());
|
||||
_widget.setGeometry(QRect(position, _outer.size()));
|
||||
updateOuterPosition(globalX);
|
||||
}
|
||||
}
|
||||
|
||||
void Preview::updateGlobalPosition() {
|
||||
const auto parent = _widget.parentWidget();
|
||||
const auto global = parent->mapToGlobal(QPoint());
|
||||
updateWindowGlobalPosition(global, global.x() + _localShiftLeft);
|
||||
}
|
||||
|
||||
void Preview::updateWindowGlobalPosition(QPoint global, int globalX) {
|
||||
const auto desiredLeft = global.x() - (_minOuterSize.width() / 2);
|
||||
const auto desiredRight = global.x()
|
||||
+ _widget.parentWidget()->width()
|
||||
+ (_maxOuterSize.width() / 2);
|
||||
const auto requiredLeft = desiredRight - _maxOuterSize.width();
|
||||
const auto left = std::min(desiredLeft, requiredLeft);
|
||||
const auto requiredRight = left + _maxOuterSize.width();
|
||||
const auto right = std::max(desiredRight, requiredRight);
|
||||
const auto top = global.y() - _maxOuterSize.height();
|
||||
auto result = QRect(left, top, right - left, _maxOuterSize.height());
|
||||
_widget.setGeometry(adjustByScreenGeometry(result));
|
||||
updateOuterPosition(globalX);
|
||||
}
|
||||
|
||||
QRect Preview::adjustByScreenGeometry(QRect geometry) const {
|
||||
const auto parent = _widget.parentWidget();
|
||||
const auto screen = parent->screen();
|
||||
if (!screen) {
|
||||
return geometry;
|
||||
}
|
||||
const auto screenGeometry = screen->availableGeometry();
|
||||
if (!screenGeometry.intersects(geometry)
|
||||
|| screenGeometry.width() < _maxOuterSize.width()
|
||||
|| screenGeometry.height() < _maxOuterSize.height()) {
|
||||
return geometry;
|
||||
}
|
||||
const auto edgeLeft = screenGeometry.x();
|
||||
const auto edgeRight = screenGeometry.x() + screenGeometry.width();
|
||||
const auto edgedRight = std::min(
|
||||
edgeRight,
|
||||
geometry.x() + geometry.width());
|
||||
const auto left = std::max(
|
||||
std::min(geometry.x(), edgedRight - _maxOuterSize.width()),
|
||||
edgeLeft);
|
||||
const auto right = std::max(edgedRight, left + _maxOuterSize.width());
|
||||
return { left, geometry.y(), right - left, geometry.height() };
|
||||
}
|
||||
|
||||
void Preview::updateOuterPosition(int globalX) {
|
||||
if (_window) {
|
||||
update();
|
||||
const auto global = _widget.geometry();
|
||||
const auto desiredLeft = globalX
|
||||
- (_outer.width() / 2)
|
||||
- global.x();
|
||||
_outer.moveLeft(std::max(
|
||||
std::min(desiredLeft, global.width() - _outer.width()),
|
||||
0));
|
||||
_outer.moveTop(_maxOuterSize.height() - _outer.height());
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
void Preview::paint(Painter &p, QRect clip) {
|
||||
//p.setCompositionMode(QPainter::CompositionMode_Source);
|
||||
//p.fillRect(clip, Qt::transparent);
|
||||
//p.setCompositionMode(QPainter::CompositionMode_SourceOver);
|
||||
|
||||
const auto outer = clip.intersected(_outer);
|
||||
if (outer.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
const auto local = outer.translated(-_outer.topLeft());
|
||||
auto q = Painter(&_layer);
|
||||
q.setClipRect(local);
|
||||
paintLayer(q, local);
|
||||
q.end();
|
||||
|
||||
const auto shown = _shownAnimation.value(_shown ? 1. : 0.);
|
||||
p.setClipRect(clip);
|
||||
p.setOpacity(shown);
|
||||
auto hq = std::optional<PainterHighQualityEnabler>();
|
||||
if (shown < 1.) {
|
||||
const auto middle = _outer.x() + (_outer.width() / 2);
|
||||
const auto bottom = _outer.y() + _outer.height();
|
||||
const auto scale = 0.3 + shown * 0.7;
|
||||
p.translate(middle, bottom);
|
||||
p.scale(scale, scale);
|
||||
p.translate(-middle, -bottom);
|
||||
hq.emplace(p);
|
||||
}
|
||||
p.drawImage(_outer.topLeft(), _layer);
|
||||
}
|
||||
|
||||
void Preview::paintLayer(Painter &p, QRect clip) {
|
||||
p.setCompositionMode(QPainter::CompositionMode_Source);
|
||||
validateShadowCache();
|
||||
Ui::Shadow::paint(
|
||||
p,
|
||||
_inner,
|
||||
_outer.width(),
|
||||
_shadow,
|
||||
_shadowSides,
|
||||
_shadowCorners);
|
||||
|
||||
const auto inner = clip.intersected(_inner);
|
||||
if (inner.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
const auto local = inner.translated(-_inner.topLeft());
|
||||
auto q = Painter(&_canvas);
|
||||
q.setClipRect(local);
|
||||
paintInner(q, local);
|
||||
q.end();
|
||||
_canvas = Images::Round(std::move(_canvas), _canvasCornerMasks);
|
||||
|
||||
p.setCompositionMode(QPainter::CompositionMode_SourceOver);
|
||||
p.drawImage(_inner.topLeft(), _canvas);
|
||||
}
|
||||
|
||||
void Preview::paintInner(Painter &p, QRect clip) {
|
||||
Window::SectionWidget::PaintBackground(
|
||||
p,
|
||||
&_theme,
|
||||
QSize(_inner.width(), _inner.width() * 3),
|
||||
clip);
|
||||
|
||||
paintUserpic(p, clip);
|
||||
|
||||
p.translate(_bubble.topLeft());
|
||||
paintBubble(p, clip.translated(-_bubble.topLeft()));
|
||||
}
|
||||
|
||||
void Preview::paintUserpic(Painter &p, QRect clip) {
|
||||
if (clip.intersected(_userpic).isEmpty()) {
|
||||
return;
|
||||
}
|
||||
validateUserpicCache();
|
||||
p.drawImage(_userpic.topLeft(), _userpicImage);
|
||||
}
|
||||
|
||||
void Preview::paintBubble(Painter &p, QRect clip) {
|
||||
validateBubbleCache();
|
||||
const auto bubble = QRect(QPoint(), _bubble.size());
|
||||
const auto cornerShadow = _bubbleShadowBottomRight.size()
|
||||
/ _bubbleShadowBottomRight.devicePixelRatio();
|
||||
p.drawPixmap(
|
||||
bubble.width() - cornerShadow.width(),
|
||||
bubble.height() + _bubbleShadow - cornerShadow.height(),
|
||||
_bubbleShadowBottomRight);
|
||||
Ui::FillRoundRect(p, bubble, st::msgInBg, _bubbleCorners);
|
||||
const auto tail = _bubbleTail.size() / _bubbleTail.devicePixelRatio();
|
||||
p.drawImage(-tail.width(), bubble.height() - tail.height(), _bubbleTail);
|
||||
p.fillRect(
|
||||
-tail.width(),
|
||||
bubble.height(),
|
||||
tail.width() + bubble.width() - cornerShadow.width(),
|
||||
_bubbleShadow,
|
||||
st::msgInShadow);
|
||||
|
||||
const auto content = clip.intersected(_content);
|
||||
if (content.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
p.translate(_content.topLeft());
|
||||
const auto local = content.translated(-_content.topLeft());
|
||||
p.setClipRect(local);
|
||||
paintContent(p, local);
|
||||
}
|
||||
|
||||
void Preview::paintContent(Painter &p, QRect clip) {
|
||||
paintReply(p, clip);
|
||||
|
||||
const auto message = clip.intersected(_message);
|
||||
if (message.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
p.translate(_message.topLeft());
|
||||
const auto local = message.translated(-_message.topLeft());
|
||||
p.setClipRect(local);
|
||||
paintMessage(p, local);
|
||||
}
|
||||
|
||||
void Preview::paintReply(Painter &p, QRect clip) {
|
||||
p.setOpacity(HistoryMessageReply::kBarAlpha);
|
||||
p.fillRect(_replyBar, st::msgInReplyBarColor);
|
||||
p.setOpacity(1.);
|
||||
|
||||
p.setPen(st::msgInServiceFg);
|
||||
_nameText.drawLeftElided(
|
||||
p,
|
||||
_name.x(),
|
||||
_name.y(),
|
||||
_name.width(),
|
||||
_content.width());
|
||||
|
||||
p.setPen(st::historyTextInFg);
|
||||
_replyText.drawLeftElided(
|
||||
p,
|
||||
_reply.x(),
|
||||
_reply.y(),
|
||||
_reply.width(),
|
||||
_content.width());
|
||||
}
|
||||
|
||||
void Preview::paintMessage(Painter &p, QRect clip) {
|
||||
p.setPen(st::historyTextInFg);
|
||||
_messageText.drawLeftElided(
|
||||
p,
|
||||
0,
|
||||
0,
|
||||
_message.width(),
|
||||
_message.width(),
|
||||
kMaxTextLines);
|
||||
}
|
||||
|
||||
void Preview::validateUserpicCache() {
|
||||
if (!_userpicImage.isNull()
|
||||
|| _userpicOriginal.isNull()
|
||||
|| _userpic.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
_userpicImage = Images::Circle(_userpicOriginal.scaled(
|
||||
_userpic.size() * _ratio,
|
||||
Qt::IgnoreAspectRatio,
|
||||
Qt::SmoothTransformation));
|
||||
_userpicImage.setDevicePixelRatio(_ratio);
|
||||
}
|
||||
|
||||
void Preview::validateBubbleCache() {
|
||||
if (!_bubbleCorners.p[0].isNull()) {
|
||||
return;
|
||||
}
|
||||
const auto radius = scaled(16); // st::bubbleRadiusLarge
|
||||
_bubbleCorners = Ui::PrepareCornerPixmaps(radius, st::msgInBg);
|
||||
_bubbleCorners.p[2] = {};
|
||||
_bubbleTail = scaled(st::historyBubbleTailInLeft, st::msgInBg->c);
|
||||
_bubbleShadowBottomRight
|
||||
= Ui::PrepareCornerPixmaps(radius, st::msgInShadow).p[3];
|
||||
}
|
||||
|
||||
void Preview::validateShadowCache() {
|
||||
if (!_shadowSides[0].isNull()) {
|
||||
return;
|
||||
}
|
||||
const auto &shadowColor = st::windowShadowFg->c;
|
||||
_shadowSides[0] = scaled(st::callShadow.left, shadowColor);
|
||||
_shadowSides[1] = scaled(st::callShadow.top, shadowColor);
|
||||
_shadowSides[2] = scaled(st::callShadow.right, shadowColor);
|
||||
_shadowSides[3] = scaled(st::callShadow.bottom, shadowColor);
|
||||
_shadowCorners[0] = scaled(st::callShadow.topLeft, shadowColor);
|
||||
_shadowCorners[1] = scaled(st::callShadow.bottomLeft, shadowColor);
|
||||
_shadowCorners[2] = scaled(st::callShadow.topRight, shadowColor);
|
||||
_shadowCorners[3] = scaled(st::callShadow.bottomRight, shadowColor);
|
||||
}
|
||||
|
||||
void Preview::initAsWindow() {
|
||||
_widget.setWindowFlags(Qt::WindowFlags(Qt::FramelessWindowHint)
|
||||
| Qt::BypassWindowManagerHint
|
||||
| Qt::NoDropShadowWindowHint
|
||||
| Qt::ToolTip);
|
||||
_widget.setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
_widget.hide();
|
||||
|
||||
_widget.setAttribute(Qt::WA_NoSystemBackground);
|
||||
_widget.setAttribute(Qt::WA_TranslucentBackground);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
[[nodiscard]] Fn<void(ScalePreviewShow, int, int)> SetupScalePreview(
|
||||
not_null<Window::Controller*> window,
|
||||
not_null<Ui::RpWidget*> slider) {
|
||||
const auto parent = slider->parentWidget();
|
||||
const auto controller = window->sessionController();
|
||||
const auto user = controller
|
||||
? controller->session().user().get()
|
||||
: nullptr;
|
||||
auto view = user->activeUserpicView();
|
||||
const auto preview = slider->lifetime().make_state<Preview>(
|
||||
slider.get(),
|
||||
user ? Data::PeerUserpicImageValue(user, 160, 0) : nullptr);
|
||||
return [=](ScalePreviewShow show, int scale, int globalX) {
|
||||
preview->toggle(show, scale, globalX);
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace Settings
|
30
Telegram/SourceFiles/settings/settings_scale_preview.h
Normal file
30
Telegram/SourceFiles/settings/settings_scale_preview.h
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
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
namespace Ui {
|
||||
class RpWidget;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Window {
|
||||
class Controller;
|
||||
} // namespace Window
|
||||
|
||||
namespace Settings {
|
||||
|
||||
enum class ScalePreviewShow {
|
||||
Show,
|
||||
Update,
|
||||
Hide,
|
||||
};
|
||||
|
||||
[[nodiscard]] Fn<void(ScalePreviewShow, int, int)> SetupScalePreview(
|
||||
not_null<Window::Controller*> window,
|
||||
not_null<Ui::RpWidget*> slider);
|
||||
|
||||
} // namespace Settings
|
|
@ -17,4 +17,4 @@ namespace Ui {
|
|||
|
||||
extern const char kOptionUseSmallMsgBubbleRadius[];
|
||||
|
||||
}
|
||||
} // namespace Ui
|
||||
|
|
|
@ -144,15 +144,15 @@ public:
|
|||
template <
|
||||
typename Value,
|
||||
typename Convert,
|
||||
typename Callback,
|
||||
typename Progress,
|
||||
typename = std::enable_if_t<
|
||||
rpl::details::is_callable_plain_v<Callback, Value>
|
||||
rpl::details::is_callable_plain_v<Progress, Value>
|
||||
&& std::is_same_v<Value, decltype(std::declval<Convert>()(1))>>>
|
||||
void setPseudoDiscrete(
|
||||
int valuesCount,
|
||||
Convert &&convert,
|
||||
Value current,
|
||||
Callback &&callback) {
|
||||
Progress &&progress) {
|
||||
Expects(valuesCount > 1);
|
||||
|
||||
setAlwaysDisplayMarker(true);
|
||||
|
@ -169,15 +169,39 @@ public:
|
|||
setAdjustCallback([=](float64 value) {
|
||||
return base::SafeRound(value * sectionsCount) / sectionsCount;
|
||||
});
|
||||
setChangeProgressCallback([
|
||||
=,
|
||||
convert = std::forward<Convert>(convert),
|
||||
callback = std::forward<Callback>(callback)
|
||||
](float64 value) {
|
||||
setChangeProgressCallback([=](float64 value) {
|
||||
const auto index = int(base::SafeRound(value * sectionsCount));
|
||||
callback(convert(index));
|
||||
progress(convert(index));
|
||||
});
|
||||
}
|
||||
|
||||
template <
|
||||
typename Value,
|
||||
typename Convert,
|
||||
typename Progress,
|
||||
typename Finished,
|
||||
typename = std::enable_if_t<
|
||||
rpl::details::is_callable_plain_v<Progress, Value>
|
||||
&& rpl::details::is_callable_plain_v<Finished, Value>
|
||||
&& std::is_same_v<Value, decltype(std::declval<Convert>()(1))>>>
|
||||
void setPseudoDiscrete(
|
||||
int valuesCount,
|
||||
Convert &&convert,
|
||||
Value current,
|
||||
Progress &&progress,
|
||||
Finished &&finished) {
|
||||
setPseudoDiscrete(
|
||||
valuesCount,
|
||||
std::forward<Convert>(convert),
|
||||
current,
|
||||
std::forward<Progress>(progress));
|
||||
setChangeFinishedCallback([=](float64 value) {
|
||||
const auto sectionsCount = (valuesCount - 1);
|
||||
const auto index = int(base::SafeRound(value * sectionsCount));
|
||||
finished(convert(index));
|
||||
});
|
||||
}
|
||||
|
||||
void setActiveFgOverride(std::optional<QColor> color);
|
||||
void addDivider(float64 atValue, const QSize &size);
|
||||
|
||||
|
|
|
@ -187,20 +187,43 @@ void SectionWidget::PaintBackground(
|
|||
not_null<Ui::ChatTheme*> theme,
|
||||
not_null<QWidget*> widget,
|
||||
QRect clip) {
|
||||
auto p = QPainter(widget);
|
||||
PaintBackground(
|
||||
theme,
|
||||
widget,
|
||||
controller->content()->height(),
|
||||
controller->content()->backgroundFromY(),
|
||||
clip);
|
||||
}
|
||||
|
||||
void SectionWidget::PaintBackground(
|
||||
not_null<Ui::ChatTheme*> theme,
|
||||
not_null<QWidget*> widget,
|
||||
int fillHeight,
|
||||
int fromy,
|
||||
QRect clip) {
|
||||
auto p = QPainter(widget);
|
||||
if (fromy) {
|
||||
p.translate(0, fromy);
|
||||
clip = clip.translated(0, -fromy);
|
||||
}
|
||||
PaintBackground(p, theme, QSize(widget->width(), fillHeight), clip);
|
||||
}
|
||||
|
||||
void SectionWidget::PaintBackground(
|
||||
QPainter &p,
|
||||
not_null<Ui::ChatTheme*> theme,
|
||||
QSize fill,
|
||||
QRect clip) {
|
||||
const auto &background = theme->background();
|
||||
if (background.colorForFill) {
|
||||
p.fillRect(clip, *background.colorForFill);
|
||||
return;
|
||||
}
|
||||
const auto &gradient = background.gradientForFill;
|
||||
const auto fill = QSize(widget->width(), controller->content()->height());
|
||||
auto fromy = controller->content()->backgroundFromY();
|
||||
auto state = theme->backgroundState(fill);
|
||||
const auto paintCache = [&](const Ui::CachedBackground &cache) {
|
||||
const auto to = QRect(
|
||||
QPoint(cache.x, fromy + cache.y),
|
||||
QPoint(cache.x, cache.y),
|
||||
cache.pixmap.size() / cIntRetinaFactor());
|
||||
if (cache.waitingForNegativePattern) {
|
||||
// While we wait for pattern being loaded we paint just gradient.
|
||||
|
@ -229,11 +252,15 @@ void SectionWidget::PaintBackground(
|
|||
const auto goodNow = hasNow && (state.now.area == fill);
|
||||
const auto useCache = goodNow || !gradient.isNull();
|
||||
if (useCache) {
|
||||
if (state.shown < 1. && !gradient.isNull()) {
|
||||
const auto fade = (state.shown < 1. && !gradient.isNull());
|
||||
if (fade) {
|
||||
paintCache(state.was);
|
||||
p.setOpacity(state.shown);
|
||||
}
|
||||
paintCache(state.now);
|
||||
if (fade) {
|
||||
p.setOpacity(1.);
|
||||
}
|
||||
return;
|
||||
}
|
||||
const auto &prepared = background.prepared;
|
||||
|
@ -259,12 +286,12 @@ void SectionWidget::PaintBackground(
|
|||
const auto w = tiled.width() / cRetinaFactor();
|
||||
const auto h = tiled.height() / cRetinaFactor();
|
||||
const auto sx = qFloor(left / w);
|
||||
const auto sy = qFloor((top - fromy) / h);
|
||||
const auto sy = qFloor(top / h);
|
||||
const auto cx = qCeil(right / w);
|
||||
const auto cy = qCeil((bottom - fromy) / h);
|
||||
const auto cy = qCeil(bottom / h);
|
||||
for (auto i = sx; i < cx; ++i) {
|
||||
for (auto j = sy; j < cy; ++j) {
|
||||
p.drawImage(QPointF(i * w, fromy + j * h), tiled);
|
||||
p.drawImage(QPointF(i * w, j * h), tiled);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -272,9 +299,7 @@ void SectionWidget::PaintBackground(
|
|||
const auto rects = Ui::ComputeChatBackgroundRects(
|
||||
fill,
|
||||
prepared.size());
|
||||
auto to = rects.to;
|
||||
to.moveTop(to.top() + fromy);
|
||||
p.drawImage(to, prepared, rects.from);
|
||||
p.drawImage(rects.to, prepared, rects.from);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -184,6 +184,17 @@ public:
|
|||
not_null<Ui::ChatTheme*> theme,
|
||||
not_null<QWidget*> widget,
|
||||
QRect clip);
|
||||
static void PaintBackground(
|
||||
not_null<Ui::ChatTheme*> theme,
|
||||
not_null<QWidget*> widget,
|
||||
int fillHeight,
|
||||
int fromy,
|
||||
QRect clip);
|
||||
static void PaintBackground(
|
||||
QPainter &p,
|
||||
not_null<Ui::ChatTheme*> theme,
|
||||
QSize fill,
|
||||
QRect clip);
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 7882604a29acc5d4c7deca51133180d74fb36f87
|
||||
Subproject commit 3cb645f50704b67537167e14623840a25fbdda7a
|
Loading…
Add table
Reference in a new issue