diff --git a/CMakeLists.txt b/CMakeLists.txt index e1976e4..259e571 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -141,8 +141,6 @@ PRIVATE ui/widgets/buttons.h ui/widgets/call_button.cpp ui/widgets/call_button.h - ui/widgets/call_mute_button.cpp - ui/widgets/call_mute_button.h ui/widgets/checkbox.cpp ui/widgets/checkbox.h ui/widgets/dropdown_menu.cpp diff --git a/fonts/DAVazirBold.ttf b/fonts/DAVazirBold.ttf index 5d8d539..28e6a07 100644 Binary files a/fonts/DAVazirBold.ttf and b/fonts/DAVazirBold.ttf differ diff --git a/fonts/DAVazirMedium.ttf b/fonts/DAVazirMedium.ttf index 88637df..4e303f9 100644 Binary files a/fonts/DAVazirMedium.ttf and b/fonts/DAVazirMedium.ttf differ diff --git a/fonts/DAVazirMediumAsBold.ttf b/fonts/DAVazirMediumAsBold.ttf index 508bed4..f69068c 100644 Binary files a/fonts/DAVazirMediumAsBold.ttf and b/fonts/DAVazirMediumAsBold.ttf differ diff --git a/fonts/DAVazirRegular.ttf b/fonts/DAVazirRegular.ttf index 6847e28..15985f8 100644 Binary files a/fonts/DAVazirRegular.ttf and b/fonts/DAVazirRegular.ttf differ diff --git a/icons/calls/voice_muted_large.png b/icons/calls/voice_muted_large.png deleted file mode 100644 index ae5a118..0000000 Binary files a/icons/calls/voice_muted_large.png and /dev/null differ diff --git a/icons/calls/voice_muted_large@2x.png b/icons/calls/voice_muted_large@2x.png deleted file mode 100644 index cd1161c..0000000 Binary files a/icons/calls/voice_muted_large@2x.png and /dev/null differ diff --git a/icons/calls/voice_muted_large@3x.png b/icons/calls/voice_muted_large@3x.png deleted file mode 100644 index 3907746..0000000 Binary files a/icons/calls/voice_muted_large@3x.png and /dev/null differ diff --git a/icons/calls/voice_unmuted_large.png b/icons/calls/voice_unmuted_large.png deleted file mode 100644 index c2355a3..0000000 Binary files a/icons/calls/voice_unmuted_large.png and /dev/null differ diff --git a/icons/calls/voice_unmuted_large@2x.png b/icons/calls/voice_unmuted_large@2x.png deleted file mode 100644 index c2e70b2..0000000 Binary files a/icons/calls/voice_unmuted_large@2x.png and /dev/null differ diff --git a/icons/calls/voice_unmuted_large@3x.png b/icons/calls/voice_unmuted_large@3x.png deleted file mode 100644 index d15a5fc..0000000 Binary files a/icons/calls/voice_unmuted_large@3x.png and /dev/null differ diff --git a/ui/effects/animation_value.h b/ui/effects/animation_value.h index faf308a..4ecf3ff 100644 --- a/ui/effects/animation_value.h +++ b/ui/effects/animation_value.h @@ -6,11 +6,11 @@ // #pragma once +#include "ui/style/style_core.h" #include "base/basic_types.h" -#include "ui/style/style_core.h" - #include +#include namespace anim { diff --git a/ui/emoji_config.cpp b/ui/emoji_config.cpp index 98a3d07..dcde206 100644 --- a/ui/emoji_config.cpp +++ b/ui/emoji_config.cpp @@ -323,12 +323,12 @@ std::vector LoadSprites(int id) { ? internal::SetDataPath(id) + '/' : QStringLiteral(":/gui/emoji/"); const auto base = folder + "emoji_"; - return ranges::view::ints( + return ranges::views::ints( 0, SpritesCount - ) | ranges::view::transform([&](int index) { + ) | ranges::views::transform([&](int index) { return base + QString::number(index + 1) + ".webp"; - }) | ranges::view::transform([](const QString &path) { + }) | ranges::views::transform([](const QString &path) { return QImage(path, "WEBP").convertToFormat( QImage::Format_ARGB32_Premultiplied); }) | ranges::to_vector; @@ -343,15 +343,15 @@ std::vector LoadAndValidateSprites(int id) { return {}; } auto result = LoadSprites(id); - const auto sizes = ranges::view::ints( + const auto sizes = ranges::views::ints( 0, SpritesCount - ) | ranges::view::transform([](int index) { + ) | ranges::views::transform([](int index) { return QSize( kImagesPerRow * kUniversalSize, RowsCount(index) * kUniversalSize); }); - const auto good = ranges::view::zip_with( + const auto good = ranges::views::zip_with( [](const QImage &data, QSize size) { return data.size() == size; }, result, sizes); @@ -588,10 +588,10 @@ bool SetIsReady(int id) { return true; } const auto folder = internal::SetDataPath(id) + '/'; - auto names = ranges::view::ints( + auto names = ranges::views::ints( 0, SpritesCount + 1 - ) | ranges::view::transform([](int index) { + ) | ranges::views::transform([](int index) { return index ? "emoji_" + QString::number(index) + ".webp" : QString("config.json"); diff --git a/ui/image/image_prepare.cpp b/ui/image/image_prepare.cpp index 3e48444..ea650d7 100644 --- a/ui/image/image_prepare.cpp +++ b/ui/image/image_prepare.cpp @@ -274,8 +274,8 @@ QImage BlurLargeImage(QImage image, int radius) { const auto rgb = take(widthxheight * 3).data(); const auto dvs = take(dvcount); - auto &&ints = ranges::view::ints; - for (auto &&[value, index] : ranges::view::zip(dvs, ints(0, ranges::unreachable))) { + auto &&ints = ranges::views::ints; + for (auto &&[value, index] : ranges::views::zip(dvs, ints(0, ranges::unreachable))) { value = (index / divsum); } const auto dv = dvs.data(); diff --git a/ui/layers/box_content.h b/ui/layers/box_content.h index 8c738d1..d5eb32e 100644 --- a/ui/layers/box_content.h +++ b/ui/layers/box_content.h @@ -197,7 +197,7 @@ public: void scrollByDraggingDelta(int delta); -public slots: +public Q_SLOTS: void onScrollToY(int top, int bottom = -1); protected: @@ -260,7 +260,7 @@ protected: void paintEvent(QPaintEvent *e) override; void keyPressEvent(QKeyEvent *e) override; -private slots: +private Q_SLOTS: void onScroll(); void onInnerResize(); diff --git a/ui/paint/arcs.cpp b/ui/paint/arcs.cpp index 34a2c28..d3121d6 100644 --- a/ui/paint/arcs.cpp +++ b/ui/paint/arcs.cpp @@ -147,7 +147,7 @@ float ArcsAnimation::width() const { if (_arcs.empty()) { return 0; } - for (const auto &arc : ranges::view::reverse(_arcs)) { + for (const auto &arc : ranges::views::reverse(_arcs)) { if ((arc.progress != 1.)) { return arc.rect.x() + arc.rect.width(); } @@ -159,7 +159,7 @@ float ArcsAnimation::finishedWidth() const { if (_arcs.empty()) { return 0; } - for (const auto &arc : ranges::view::reverse(_arcs)) { + for (const auto &arc : ranges::views::reverse(_arcs)) { if (arc.threshold <= _currentValue) { return arc.rect.x() + arc.rect.width(); } @@ -224,6 +224,7 @@ void ArcsAnimation::paint(Painter &p, std::optional colorOverride) { : InterpolatedRect(arc.rect, previousRect, progress); p.drawArc(rect, _startAngle, _spanAngle); } + p.setOpacity(1.); } void ArcsAnimation::setStrokeRatio(float ratio) { diff --git a/ui/platform/linux/ui_linux_wayland_integration.cpp b/ui/platform/linux/ui_linux_wayland_integration.cpp index 7cb7dd1..45f0112 100644 --- a/ui/platform/linux/ui_linux_wayland_integration.cpp +++ b/ui/platform/linux/ui_linux_wayland_integration.cpp @@ -11,6 +11,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include +// private headers are using keywords :( +#ifdef QT_NO_KEYWORDS +#define signals Q_SIGNALS +#define slots Q_SLOTS +#endif // QT_NO_KEYWORDS + #include #include #include diff --git a/ui/platform/linux/ui_utility_linux.cpp b/ui/platform/linux/ui_utility_linux.cpp index 933ea21..bfcc482 100644 --- a/ui/platform/linux/ui_utility_linux.cpp +++ b/ui/platform/linux/ui_utility_linux.cpp @@ -32,10 +32,6 @@ namespace { constexpr auto kXCBFrameExtentsAtomName = "_GTK_FRAME_EXTENTS"_cs; -constexpr auto kXDGDesktopPortalService = "org.freedesktop.portal.Desktop"_cs; -constexpr auto kXDGDesktopPortalObjectPath = "/org/freedesktop/portal/desktop"_cs; -constexpr auto kSettingsPortalInterface = "org.freedesktop.portal.Settings"_cs; - #ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION bool SetXCBFrameExtents(QWindow *window, const QMargins &extents) { const auto connection = base::Platform::XCB::GetConnectionFromQt(); @@ -161,23 +157,27 @@ bool TranslucentWindowsSupported(QPoint globalPosition) { if (::Platform::IsWayland()) { return true; } - if (const auto native = QGuiApplication::platformNativeInterface()) { - if (const auto desktop = QApplication::desktop()) { - if (const auto screen = base::QScreenNearestTo(globalPosition)) { - if (native->nativeResourceForScreen(QByteArray("compositingEnabled"), screen)) { - return true; + + if (::Platform::IsX11()) { + if (const auto native = QGuiApplication::platformNativeInterface()) { + if (const auto desktop = QApplication::desktop()) { + if (const auto screen = base::QScreenNearestTo(globalPosition)) { + if (native->nativeResourceForScreen(QByteArray("compositingEnabled"), screen)) { + return true; + } + const auto index = QGuiApplication::screens().indexOf(screen); + static auto WarnedAbout = base::flat_set(); + if (!WarnedAbout.contains(index)) { + WarnedAbout.emplace(index); + UI_LOG(("WARNING: Compositing is disabled for screen index %1 (for position %2,%3)").arg(index).arg(globalPosition.x()).arg(globalPosition.y())); + } + } else { + UI_LOG(("WARNING: Could not get screen for position %1,%2").arg(globalPosition.x()).arg(globalPosition.y())); } - const auto index = QGuiApplication::screens().indexOf(screen); - static auto WarnedAbout = base::flat_set(); - if (!WarnedAbout.contains(index)) { - WarnedAbout.emplace(index); - UI_LOG(("WARNING: Compositing is disabled for screen index %1 (for position %2,%3)").arg(index).arg(globalPosition.x()).arg(globalPosition.y())); - } - } else { - UI_LOG(("WARNING: Could not get screen for position %1,%2").arg(globalPosition.x()).arg(globalPosition.y())); } } } + return false; } @@ -193,7 +193,7 @@ bool WindowExtentsSupported() { #ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION namespace XCB = base::Platform::XCB; - if (!::Platform::IsWayland() + if (::Platform::IsX11() && XCB::IsSupportedByWM(kXCBFrameExtentsAtomName.utf16())) { return true; } @@ -210,13 +210,15 @@ bool SetWindowExtents(QWindow *window, const QMargins &extents) { #else // DESKTOP_APP_QT_PATCHED return false; #endif // !DESKTOP_APP_QT_PATCHED - } else { + } else if (::Platform::IsX11()) { #ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION return SetXCBFrameExtents(window, extents); #else // !DESKTOP_APP_DISABLE_X11_INTEGRATION return false; #endif // DESKTOP_APP_DISABLE_X11_INTEGRATION } + + return false; } bool UnsetWindowExtents(QWindow *window) { @@ -227,28 +229,41 @@ bool UnsetWindowExtents(QWindow *window) { #else // DESKTOP_APP_QT_PATCHED return false; #endif // !DESKTOP_APP_QT_PATCHED - } else { + } else if (::Platform::IsX11()) { #ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION return UnsetXCBFrameExtents(window); #else // !DESKTOP_APP_DISABLE_X11_INTEGRATION return false; #endif // DESKTOP_APP_DISABLE_X11_INTEGRATION } + + return false; } bool ShowWindowMenu(QWindow *window) { if (const auto integration = WaylandIntegration::Instance()) { return integration->showWindowMenu(window); - } else { + } else if (::Platform::IsX11()) { #ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION return ShowXCBWindowMenu(window); #else // !DESKTOP_APP_DISABLE_X11_INTEGRATION return false; #endif // DESKTOP_APP_DISABLE_X11_INTEGRATION } + + return false; } TitleControls::Layout TitleControlsLayout() { + if (static auto Once = false; !std::exchange(Once, true)) { + const auto integration = base::Platform::GtkIntegration::Instance(); + if (integration && integration->checkVersion(3, 12, 0)) { + integration->connectToSetting( + "gtk-decoration-layout", + NotifyTitleControlsLayoutChanged); + } + } + const auto gtkResult = []() -> std::optional { const auto integration = base::Platform::GtkIntegration::Instance(); if (!integration || !integration->checkVersion(3, 12, 0)) { @@ -257,7 +272,7 @@ TitleControls::Layout TitleControlsLayout() { const auto decorationLayoutSetting = integration->getStringSetting( "gtk-decoration-layout"); - + if (!decorationLayoutSetting.has_value()) { return std::nullopt; } diff --git a/ui/platform/mac/ui_window_mac.mm b/ui/platform/mac/ui_window_mac.mm index 433bd73..a7d6d87 100644 --- a/ui/platform/mac/ui_window_mac.mm +++ b/ui/platform/mac/ui_window_mac.mm @@ -385,6 +385,11 @@ void WindowHelper::close() { } void WindowHelper::init() { + style::PaletteChanged( + ) | rpl::start_with_next([=] { + Ui::ForceFullRepaint(window()); + }, window()->lifetime()); + rpl::combine( window()->sizeValue(), _title->heightValue(), diff --git a/ui/platform/ui_platform_window_title.cpp b/ui/platform/ui_platform_window_title.cpp index 1079cfc..1a5b406 100644 --- a/ui/platform/ui_platform_window_title.cpp +++ b/ui/platform/ui_platform_window_title.cpp @@ -122,29 +122,36 @@ void TitleControls::init(Fn maximize) { updateControlsPosition(); }, _close->lifetime()); - const auto winIdEventFilter = std::make_shared(nullptr); - *winIdEventFilter = base::install_event_filter( - window(), - [=](not_null e) { - if (!*winIdEventFilter || e->type() != QEvent::WinIdChange) { - return base::EventFilterResult::Continue; - } - - QObject::connect( - window()->windowHandle(), - &QWindow::windowStateChanged, - [=](Qt::WindowState state) { - handleWindowStateChanged(state); - }); - - base::take(*winIdEventFilter)->deleteLater(); - return base::EventFilterResult::Continue; - }); + subscribeToStateChanges(); _activeState = parent()->isActiveWindow(); updateButtonsState(); } +void TitleControls::subscribeToStateChanges() { + const auto subscribe = [=] { + QObject::connect( + window()->windowHandle(), + &QWindow::windowStateChanged, + [=](Qt::WindowState state) { handleWindowStateChanged(state); }); + }; + if (window()->windowHandle()) { + subscribe(); + } else { + const auto winIdEventFilter = std::make_shared(nullptr); + *winIdEventFilter = base::install_event_filter( + window(), + [=](not_null e) { + if (!*winIdEventFilter || e->type() != QEvent::WinIdChange) { + return base::EventFilterResult::Continue; + } + subscribe(); + base::take(*winIdEventFilter)->deleteLater(); + return base::EventFilterResult::Continue; + }); + } +} + void TitleControls::setResizeEnabled(bool enabled) { _resizeEnabled = enabled; updateControlsPosition(); @@ -170,6 +177,23 @@ void TitleControls::updateControlsPosition() { const auto controlsLayout = TitleControlsLayout(); auto controlsLeft = controlsLayout.left; auto controlsRight = controlsLayout.right; + const auto moveFromTo = [&](auto &from, auto &to) { + for (const auto control : from) { + if (!ranges::contains(to, control)) { + to.push_back(control); + } + } + from.clear(); + }; + if (ranges::contains(controlsLeft, Control::Close)) { + moveFromTo(controlsRight, controlsLeft); + } else if (ranges::contains(controlsRight, Control::Close)) { + moveFromTo(controlsLeft, controlsRight); + } else if (controlsLeft.size() > controlsRight.size()) { + moveFromTo(controlsRight, controlsLeft); + } else { + moveFromTo(controlsLeft, controlsRight); + } const auto controlPresent = [&](Control control) { return ranges::contains(controlsLeft, control) @@ -216,7 +240,7 @@ void TitleControls::updateControlsPositionBySide( const std::vector &controls, bool right) { auto preparedControls = right - ? (ranges::view::reverse(controls) | ranges::to_vector) + ? (ranges::views::reverse(controls) | ranges::to_vector) : controls; RemoveDuplicates(preparedControls); diff --git a/ui/platform/ui_platform_window_title.h b/ui/platform/ui_platform_window_title.h index af66155..f8373ad 100644 --- a/ui/platform/ui_platform_window_title.h +++ b/ui/platform/ui_platform_window_title.h @@ -54,6 +54,7 @@ private: [[nodiscard]] Ui::IconButton *controlWidget(Control control) const; void init(Fn maximize); + void subscribeToStateChanges(); void updateButtonsState(); void updateControlsPosition(); void updateControlsPositionBySide( diff --git a/ui/platform/win/ui_window_win.cpp b/ui/platform/win/ui_window_win.cpp index 15fee67..9a12ef5 100644 --- a/ui/platform/win/ui_window_win.cpp +++ b/ui/platform/win/ui_window_win.cpp @@ -206,6 +206,7 @@ void WindowHelper::init() { style::PaletteChanged( ) | rpl::start_with_next([=] { _shadow.setColor(st::windowShadowFg->c); + Ui::ForceFullRepaint(window()); }, window()->lifetime()); rpl::combine( @@ -350,7 +351,7 @@ bool WindowHelper::handleNativeEvent( } else if (wParam == SIZE_MINIMIZED) { state = Qt::WindowMinimized; } - emit window()->windowHandle()->windowStateChanged(state); + window()->windowHandle()->windowStateChanged(state); } updateMargins(); const auto changes = (wParam == SIZE_MINIMIZED diff --git a/ui/style/style_core_font.cpp b/ui/style/style_core_font.cpp index bed4ca4..55d8fd5 100644 --- a/ui/style/style_core_font.cpp +++ b/ui/style/style_core_font.cpp @@ -49,7 +49,7 @@ bool ValidateFont(const QString &familyName, int flags = 0) { checkFont.setUnderline(flags & style::internal::FontUnderline); auto realFamily = QFontInfo(checkFont).family(); if (realFamily.trimmed().compare(familyName, Qt::CaseInsensitive)) { - UI_LOG(("Font Error: could not resolve '%1' font, got '%2'.").arg(familyName).arg(realFamily)); + UI_LOG(("Font Error: could not resolve '%1' font, got '%2'.").arg(familyName, realFamily)); return false; } @@ -71,7 +71,7 @@ bool LoadCustomFont(const QString &filePath, const QString &familyName, int flag const auto found = [&] { for (auto &family : QFontDatabase::applicationFontFamilies(regularId)) { - UI_LOG(("Font: from '%1' loaded '%2'").arg(filePath).arg(family)); + UI_LOG(("Font: from '%1' loaded '%2'").arg(filePath, family)); if (!family.trimmed().compare(familyName, Qt::CaseInsensitive)) { return true; } @@ -79,7 +79,7 @@ bool LoadCustomFont(const QString &filePath, const QString &familyName, int flag return false; }(); if (!found) { - UI_LOG(("Font Error: could not locate '%1' font in '%2'.").arg(familyName).arg(filePath)); + UI_LOG(("Font Error: could not locate '%1' font in '%2'.").arg(familyName, filePath)); return false; } @@ -320,7 +320,7 @@ QString MonospaceFont() { } void destroyFonts() { - for (auto fontData : fontsMap) { + for (auto fontData : std::as_const(fontsMap)) { delete fontData; } fontsMap.clear(); diff --git a/ui/style/style_core_icon.cpp b/ui/style/style_core_icon.cpp index 8b2b071..c250beb 100644 --- a/ui/style/style_core_icon.cpp +++ b/ui/style/style_core_icon.cpp @@ -19,7 +19,7 @@ uint32 colorKey(QColor c) { return (((((uint32(c.red()) << 8) | uint32(c.green())) << 8) | uint32(c.blue())) << 8) | uint32(c.alpha()); } -QMap iconMasks; +base::flat_map iconMasks; QMap, QPixmap> iconPixmaps; OrderedSet iconData; @@ -251,11 +251,11 @@ void MonoIcon::ensureLoaded() const { _size = readGeneratedSize(_mask, Scale()); if (_size.isEmpty()) { - auto i = iconMasks.constFind(_mask); + auto i = iconMasks.find(_mask); if (i == iconMasks.cend()) { - i = iconMasks.insert(_mask, createIconMask(_mask, Scale())); + i = iconMasks.emplace(_mask, createIconMask(_mask, Scale())).first; } - _maskImage = i.value(); + _maskImage = i->second; createCachedPixmap(); } diff --git a/ui/text/text_entity.cpp b/ui/text/text_entity.cpp index 9bdfc43..98deb4d 100644 --- a/ui/text/text_entity.cpp +++ b/ui/text/text_entity.cpp @@ -1146,7 +1146,7 @@ const QRegularExpression &RegExpWordSplit() { auto &&urls = ranges::make_subrange( entities.begin(), entities.end() - ) | ranges::view::filter([](const EntityInText &entity) { + ) | ranges::views::filter([](const EntityInText &entity) { return entity.type() == EntityType::CustomUrl; }); const auto &original = text.text; @@ -2146,7 +2146,7 @@ int EntityInText::FirstMonospaceOffset( auto &&monospace = ranges::make_subrange( entities.begin(), entities.end() - ) | ranges::view::filter([](const EntityInText & entity) { + ) | ranges::views::filter([](const EntityInText & entity) { return (entity.type() == EntityType::Pre) || (entity.type() == EntityType::Code); }); diff --git a/ui/widgets/call_mute_button.cpp b/ui/widgets/call_mute_button.cpp deleted file mode 100644 index 10c5062..0000000 --- a/ui/widgets/call_mute_button.cpp +++ /dev/null @@ -1,962 +0,0 @@ -// This file is part of Desktop App Toolkit, -// a set of libraries for developing nice desktop applications. -// -// For license and copyright information please follow this link: -// https://github.com/desktop-app/legal/blob/master/LEGAL -// -#include "ui/widgets/call_mute_button.h" - -#include "base/flat_map.h" -#include "ui/abstract_button.h" -#include "ui/paint/blobs.h" -#include "ui/painter.h" -#include "ui/widgets/call_button.h" -#include "ui/widgets/labels.h" - -#include "styles/palette.h" -#include "styles/style_widgets.h" - -#include - -namespace Ui { -namespace { - -using Radiuses = Paint::Blob::Radiuses; - -constexpr auto kMaxLevel = 1.; - -constexpr auto kLevelDuration = 100. + 500. * 0.33; - -constexpr auto kScaleBig = 0.807 - 0.1; -constexpr auto kScaleSmall = 0.704 - 0.1; - -constexpr auto kScaleBigMin = 0.878; -constexpr auto kScaleSmallMin = 0.926; - -constexpr auto kScaleBigMax = (float)(kScaleBigMin + kScaleBig); -constexpr auto kScaleSmallMax = (float)(kScaleSmallMin + kScaleSmall); - -constexpr auto kMainRadiusFactor = (float)(50. / 57.); - -constexpr auto kGlowPaddingFactor = 1.2; -constexpr auto kGlowMinScale = 0.6; -constexpr auto kGlowAlpha = 150; - -constexpr auto kOverrideColorBgAlpha = 76; -constexpr auto kOverrideColorRippleAlpha = 50; - -constexpr auto kShiftDuration = crl::time(300); -constexpr auto kSwitchStateDuration = crl::time(120); -constexpr auto kSwitchLabelDuration = crl::time(180); - -// Switch state from Connecting animation. -constexpr auto kSwitchRadialDuration = crl::time(350); -constexpr auto kSwitchCirclelDuration = crl::time(275); -constexpr auto kBlobsScaleEnterDuration = crl::time(400); -constexpr auto kSwitchStateFromConnectingDuration = kSwitchRadialDuration - + kSwitchCirclelDuration - + kBlobsScaleEnterDuration; - -constexpr auto kRadialEndPartAnimation = float(kSwitchRadialDuration) - / kSwitchStateFromConnectingDuration; -constexpr auto kBlobsWidgetPartAnimation = 1. - kRadialEndPartAnimation; -constexpr auto kFillCirclePartAnimation = float(kSwitchCirclelDuration) - / (kSwitchCirclelDuration + kBlobsScaleEnterDuration); -constexpr auto kBlobPartAnimation = float(kBlobsScaleEnterDuration) - / (kSwitchCirclelDuration + kBlobsScaleEnterDuration); - -constexpr auto kOverlapProgressRadialHide = 1.2; - -constexpr auto kRadialFinishArcShift = 1200; - -auto MuteBlobs() { - return std::vector{ - { - .segmentsCount = 9, - .minScale = kScaleSmallMin / kScaleSmallMax, - .minRadius = st::callMuteMinorBlobMinRadius - * kScaleSmallMax - * kMainRadiusFactor, - .maxRadius = st::callMuteMinorBlobMaxRadius - * kScaleSmallMax - * kMainRadiusFactor, - .speedScale = 1., - .alpha = (76. / 255.), - }, - { - .segmentsCount = 12, - .minScale = kScaleBigMin / kScaleBigMax, - .minRadius = st::callMuteMajorBlobMinRadius - * kScaleBigMax - * kMainRadiusFactor, - .maxRadius = st::callMuteMajorBlobMaxRadius - * kScaleBigMax - * kMainRadiusFactor, - .speedScale = 1., - .alpha = (76. / 255.), - }, - }; -} - -auto Colors() { - using Vector = std::vector; - using Colors = anim::gradient_colors; - return base::flat_map{ - { - CallMuteButtonType::ForceMuted, - Colors(QGradientStops{ - { .0, st::groupCallForceMuted1->c }, - { .5, st::groupCallForceMuted2->c }, - { 1., st::groupCallForceMuted3->c } }) - }, - { - CallMuteButtonType::Active, - Colors(Vector{ st::groupCallLive1->c, st::groupCallLive2->c }) - }, - { - CallMuteButtonType::Connecting, - Colors(st::callIconBg->c) - }, - { - CallMuteButtonType::Muted, - Colors(Vector{ st::groupCallMuted1->c, st::groupCallMuted2->c }) - }, - }; -} - -bool IsMuted(CallMuteButtonType type) { - return (type != CallMuteButtonType::Active); -} - -bool IsConnecting(CallMuteButtonType type) { - return (type == CallMuteButtonType::Connecting); -} - -bool IsInactive(CallMuteButtonType type) { - return IsConnecting(type) || (type == CallMuteButtonType::ForceMuted); -} - -auto Clamp(float64 value) { - return std::clamp(value, 0., 1.); -} - -void ComputeRadialFinish( - int &value, - float64 progress, - int to = -RadialState::kFull) { - value = anim::interpolate(value, to, Clamp(progress)); -} - -} // namespace - -class AnimatedLabel final : public RpWidget { -public: - AnimatedLabel( - QWidget *parent, - rpl::producer &&text, - crl::time duration, - int additionalHeight, - const style::FlatLabel &st = st::defaultFlatLabel); - - int height() const; - -private: - int realHeight() const; - - void setText(const QString &text); - - const style::FlatLabel &_st; - const crl::time _duration; - const int _additionalHeight; - const TextParseOptions _options; - - Text::String _text; - Text::String _previousText; - - Animations::Simple _animation; - -}; - -AnimatedLabel::AnimatedLabel( - QWidget *parent, - rpl::producer &&text, - crl::time duration, - int additionalHeight, - const style::FlatLabel &st) -: RpWidget(parent) -, _st(st) -, _duration(duration) -, _additionalHeight(additionalHeight) -, _options({ 0, 0, 0, Qt::LayoutDirectionAuto }) { - std::move( - text - ) | rpl::start_with_next([=](const QString &value) { - setText(value); - }, lifetime()); - - paintRequest( - ) | rpl::start_with_next([=] { - Painter p(this); - const auto progress = _animation.value(1.); - - p.setFont(_st.style.font); - p.setPen(_st.textFg); - p.setTextPalette(_st.palette); - - const auto textHeight = height(); - const auto diffHeight = realHeight() - textHeight; - const auto center = (diffHeight) / 2; - - p.setOpacity(1. - progress); - if (p.opacity()) { - _previousText.draw( - p, - 0, - anim::interpolate(center, diffHeight, progress), - width(), - style::al_center); - } - - p.setOpacity(progress); - if (p.opacity()) { - _text.draw( - p, - 0, - anim::interpolate(0, center, progress), - width(), - style::al_center); - } - }, lifetime()); -} - -int AnimatedLabel::height() const { - return _st.style.font->height; -} - -int AnimatedLabel::realHeight() const { - return RpWidget::height(); -} - -void AnimatedLabel::setText(const QString &text) { - if (_text.toString() == text) { - return; - } - _previousText = _text; - _text.setText(_st.style, text, _options); - - const auto width = std::max( - _st.style.font->width(_text.toString()), - _st.style.font->width(_previousText.toString())); - resize(width + _additionalHeight, height() + _additionalHeight * 2); - - _animation.stop(); - _animation.start([=] { update(); }, 0., 1., _duration); -} - -class BlobsWidget final : public RpWidget { -public: - BlobsWidget( - not_null parent, - rpl::producer &&hideBlobs); - - void setLevel(float level); - void setBlobBrush(QBrush brush); - void setGlowBrush(QBrush brush); - - [[nodiscard]] QRectF innerRect() const; - - [[nodiscard]] float64 switchConnectingProgress() const; - void setSwitchConnectingProgress(float64 progress); - -private: - void init(); - - Paint::Blobs _blobs; - - const float _circleRadius; - QBrush _blobBrush; - QBrush _glowBrush; - int _center = 0; - QRectF _circleRect; - - float64 _switchConnectingProgress = 0.; - - crl::time _blobsLastTime = 0; - crl::time _blobsHideLastTime = 0; - - float64 _blobsScaleEnter = 0.; - crl::time _blobsScaleLastTime = 0; - - bool _hideBlobs = true; - - Animations::Basic _animation; - -}; - -BlobsWidget::BlobsWidget( - not_null parent, - rpl::producer &&hideBlobs) -: RpWidget(parent) -, _blobs(MuteBlobs(), kLevelDuration, kMaxLevel) -, _circleRadius(st::callMuteButtonActive.bgSize / 2.) -, _blobBrush(Qt::transparent) -, _glowBrush(Qt::transparent) -, _blobsLastTime(crl::now()) -, _blobsScaleLastTime(crl::now()) { - init(); - - std::move( - hideBlobs - ) | rpl::start_with_next([=](bool hide) { - if (_hideBlobs != hide) { - const auto now = crl::now(); - if ((now - _blobsScaleLastTime) >= kBlobsScaleEnterDuration) { - _blobsScaleLastTime = now; - } - _hideBlobs = hide; - } - if (hide) { - setLevel(0.); - } - _blobsHideLastTime = hide ? crl::now() : 0; - if (!hide && !_animation.animating()) { - _animation.start(); - } - }, lifetime()); -} - -void BlobsWidget::init() { - setAttribute(Qt::WA_TransparentForMouseEvents); - - const auto cutRect = [](Painter &p, const QRectF &r) { - p.save(); - p.setOpacity(1.); - p.setBrush(st::groupCallBg); - p.setCompositionMode(QPainter::CompositionMode_Source); - p.drawEllipse(r); - p.restore(); - }; - - { - const auto s = _blobs.maxRadius() * 2 * kGlowPaddingFactor; - resize(s, s); - } - - sizeValue( - ) | rpl::start_with_next([=](QSize size) { - _center = size.width() / 2; - - { - const auto &r = _circleRadius; - const auto left = (size.width() - r * 2.) / 2.; - const auto add = st::callConnectingRadial.thickness / 2; - _circleRect = QRectF(left, left, r * 2, r * 2).marginsAdded( - style::margins(add, add, add, add)); - } - }, lifetime()); - - paintRequest( - ) | rpl::start_with_next([=] { - Painter p(this); - PainterHighQualityEnabler hq(p); - - p.setPen(Qt::NoPen); - - // Glow. - const auto s = kGlowMinScale - + (1. - kGlowMinScale) * _blobs.currentLevel(); - p.translate(_center, _center); - p.scale(s, s); - p.translate(-_center, -_center); - p.fillRect(rect(), _glowBrush); - p.resetTransform(); - - // Blobs. - p.translate(_center, _center); - const auto scale = (_switchConnectingProgress > 0.) - ? anim::easeOutBack( - 1., - _blobsScaleEnter * (1. - Clamp( - _switchConnectingProgress / kBlobPartAnimation))) - : _blobsScaleEnter; - _blobs.paint(p, _blobBrush, scale); - p.translate(-_center, -_center); - - if (scale < 1.) { - cutRect(p, _circleRect); - } - - // Main circle. - const auto circleProgress = - Clamp(_switchConnectingProgress - kBlobPartAnimation) - / kFillCirclePartAnimation; - const auto skipColoredCircle = (circleProgress == 1.); - - if (!skipColoredCircle) { - p.setBrush(_blobBrush); - p.drawEllipse(_circleRect); - } - - if (_switchConnectingProgress > 0.) { - p.resetTransform(); - - const auto mF = (_circleRect.width() / 2) * (1. - circleProgress); - const auto cutOutRect = _circleRect.marginsRemoved( - QMarginsF(mF, mF, mF, mF)); - - if (!skipColoredCircle) { - p.setBrush(st::callConnectingRadial.color); - p.setOpacity(circleProgress); - p.drawEllipse(_circleRect); - } - - p.setOpacity(1.); - - cutRect(p, cutOutRect); - - p.setBrush(st::callIconBg); - p.drawEllipse(cutOutRect); - } - }, lifetime()); - - _animation.init([=](crl::time now) { - if (const auto &last = _blobsHideLastTime; (last > 0) - && (now - last >= kBlobsScaleEnterDuration)) { - _animation.stop(); - return false; - } - _blobs.updateLevel(now - _blobsLastTime); - _blobsLastTime = now; - - const auto dt = Clamp( - (now - _blobsScaleLastTime) / float64(kBlobsScaleEnterDuration)); - _blobsScaleEnter = _hideBlobs - ? (1. - anim::easeInCirc(1., dt)) - : anim::easeOutBack(1., dt); - - update(); - return true; - }); - shownValue( - ) | rpl::start_with_next([=](bool shown) { - if (shown) { - _animation.start(); - } else { - _animation.stop(); - } - }, lifetime()); -} - -QRectF BlobsWidget::innerRect() const { - return _circleRect; -} - -void BlobsWidget::setBlobBrush(QBrush brush) { - if (_blobBrush == brush) { - return; - } - _blobBrush = brush; -} - -void BlobsWidget::setGlowBrush(QBrush brush) { - if (_glowBrush == brush) { - return; - } - _glowBrush = brush; -} - -void BlobsWidget::setLevel(float level) { - if (_blobsHideLastTime) { - return; - } - _blobs.setLevel(level); -} - -float64 BlobsWidget::switchConnectingProgress() const { - return _switchConnectingProgress; -} - -void BlobsWidget::setSwitchConnectingProgress(float64 progress) { - _switchConnectingProgress = progress; -} - -CallMuteButton::CallMuteButton( - not_null parent, - rpl::producer &&hideBlobs, - CallMuteButtonState initial) -: _state(initial) -, _st(st::callMuteButtonActive) -, _blobs(base::make_unique_q( - parent, - rpl::combine( - rpl::single(anim::Disabled()) | rpl::then(anim::Disables()), - std::move(hideBlobs), - _state.value( - ) | rpl::map([](const CallMuteButtonState &state) { - return IsInactive(state.type); - }) - ) | rpl::map([](bool animDisabled, bool hide, bool isBadState) { - return isBadState || !(!animDisabled && !hide); - }))) -, _content(base::make_unique_q(parent)) -, _centerLabel(base::make_unique_q( - parent, - _state.value( - ) | rpl::map([](const CallMuteButtonState &state) { - return state.subtext.isEmpty() ? state.text : QString(); - }), - kSwitchLabelDuration, - st::callMuteButtonLabelAdditional, - _st.label)) -, _label(base::make_unique_q( - parent, - _state.value( - ) | rpl::map([](const CallMuteButtonState &state) { - return state.subtext.isEmpty() ? QString() : state.text; - }), - kSwitchLabelDuration, - st::callMuteButtonLabelAdditional, - _st.label)) -, _sublabel(base::make_unique_q( - parent, - _state.value( - ) | rpl::map([](const CallMuteButtonState &state) { - return state.subtext; - }), - kSwitchLabelDuration, - st::callMuteButtonLabelAdditional, - st::callMuteButtonSublabel)) -, _radial(nullptr) -, _colors(Colors()) -, _crossLineMuteAnimation(st::callMuteCrossLine) { - init(); -} - -void CallMuteButton::init() { - _content->resize(_st.button.width, _st.button.height); - - style::PaletteChanged( - ) | rpl::start_with_next([=] { - _crossLineMuteAnimation.invalidate(); - }, lifetime()); - - // Label text. - _label->show(); - rpl::combine( - _content->geometryValue(), - _label->sizeValue() - ) | rpl::start_with_next([=](QRect my, QSize size) { - updateLabelGeometry(my, size); - }, _label->lifetime()); - _label->setAttribute(Qt::WA_TransparentForMouseEvents); - - _sublabel->show(); - rpl::combine( - _content->geometryValue(), - _sublabel->sizeValue() - ) | rpl::start_with_next([=](QRect my, QSize size) { - updateSublabelGeometry(my, size); - }, _sublabel->lifetime()); - _sublabel->setAttribute(Qt::WA_TransparentForMouseEvents); - - _centerLabel->show(); - rpl::combine( - _content->geometryValue(), - _centerLabel->sizeValue() - ) | rpl::start_with_next([=](QRect my, QSize size) { - updateCenterLabelGeometry(my, size); - }, _centerLabel->lifetime()); - _centerLabel->setAttribute(Qt::WA_TransparentForMouseEvents); - - rpl::combine( - _radialInfo.rawShowProgress.value(), - anim::Disables() - ) | rpl::start_with_next([=](float64 value, bool disabled) { - auto &info = _radialInfo; - info.realShowProgress = (1. - value) / kRadialEndPartAnimation; - - const auto guard = gsl::finally([&] { - _content->update(); - }); - - if (((value == 0.) || disabled) && _radial) { - _radial->stop(); - _radial = nullptr; - return; - } - if ((value > 0.) && !disabled && !_radial) { - _radial = std::make_unique( - [=] { _content->update(); }, - _radialInfo.st); - _radial->start(); - } - if ((info.realShowProgress < 1.) && !info.isDirectionToShow) { - if (_radial) { - _radial->stop(anim::type::instant); - _radial->start(); - } - info.state = std::nullopt; - return; - } - - if (value == 1.) { - info.state = std::nullopt; - } else { - if (_radial && !info.state.has_value()) { - info.state = _radial->computeState(); - } - } - }, lifetime()); - - // State type. - const auto previousType = - lifetime().make_state(_state.current().type); - setHandleMouseState(HandleMouseState::Disabled); - - const auto blobsInner = [&] { - // The point of the circle at 45 degrees. - const auto w = _blobs->innerRect().width(); - const auto mF = (1 - std::cos(M_PI / 4.)) * (w / 2.); - return _blobs->innerRect().marginsRemoved(QMarginsF(mF, mF, mF, mF)); - }(); - - auto linearGradients = anim::linear_gradients( - _colors, - QPointF(blobsInner.x() + blobsInner.width(), blobsInner.y()), - QPointF(blobsInner.x(), blobsInner.y() + blobsInner.height())); - - auto glowColors = [&] { - auto copy = _colors; - for (auto &[type, stops] : copy) { - auto firstColor = IsInactive(type) - ? st::groupCallBg->c - : stops.stops[0].second; - firstColor.setAlpha(kGlowAlpha); - stops.stops = QGradientStops{ - { 0., std::move(firstColor) }, - { 1., QColor(Qt::transparent) } - }; - } - return copy; - }(); - auto glows = anim::radial_gradients( - std::move(glowColors), - blobsInner.center(), - _blobs->width() / 2); - - _state.value( - ) | rpl::map([](const CallMuteButtonState &state) { - return state.type; - }) | rpl::start_with_next([=](CallMuteButtonType type) { - const auto previous = *previousType; - *previousType = type; - - const auto mouseState = HandleMouseStateFromType(type); - setHandleMouseState(HandleMouseState::Disabled); - if (mouseState != HandleMouseState::Enabled) { - setHandleMouseState(mouseState); - } - - const auto fromConnecting = IsConnecting(previous); - const auto toConnecting = IsConnecting(type); - - const auto crossFrom = IsMuted(previous) ? 0. : 1.; - const auto crossTo = IsMuted(type) ? 0. : 1.; - - const auto radialShowFrom = fromConnecting ? 1. : 0.; - const auto radialShowTo = toConnecting ? 1. : 0.; - - const auto from = (_switchAnimation.animating() && !fromConnecting) - ? (1. - _switchAnimation.value(0.)) - : 0.; - const auto to = 1.; - - _radialInfo.isDirectionToShow = fromConnecting; - - auto callback = [=](float64 value) { - const auto brushProgress = fromConnecting ? 1. : value; - _blobs->setBlobBrush(QBrush( - linearGradients.gradient(previous, type, brushProgress))); - _blobs->setGlowBrush(QBrush( - glows.gradient(previous, type, value))); - _blobs->update(); - - const auto crossProgress = (crossFrom == crossTo) - ? crossTo - : anim::interpolateF(crossFrom, crossTo, value); - if (crossProgress != _crossLineProgress) { - _crossLineProgress = crossProgress; - _content->update(_muteIconRect); - } - - const auto radialShowProgress = (radialShowFrom == radialShowTo) - ? radialShowTo - : anim::interpolateF(radialShowFrom, radialShowTo, value); - if (radialShowProgress != _radialInfo.rawShowProgress.current()) { - _radialInfo.rawShowProgress = radialShowProgress; - _blobs->setSwitchConnectingProgress(Clamp( - radialShowProgress / kBlobsWidgetPartAnimation)); - } - - overridesColors(previous, type, value); - - if (value == to) { - setHandleMouseState(mouseState); - } - }; - - _switchAnimation.stop(); - const auto duration = (1. - from) * ((fromConnecting || toConnecting) - ? kSwitchStateFromConnectingDuration - : kSwitchStateDuration); - _switchAnimation.start(std::move(callback), from, to, duration); - }, lifetime()); - - // Icon rect. - _content->sizeValue( - ) | rpl::start_with_next([=](QSize size) { - const auto &icon = _st.button.icon; - const auto &pos = _st.button.iconPosition; - - _muteIconRect = QRect( - (pos.x() < 0) ? ((size.width() - icon.width()) / 2) : pos.x(), - (pos.y() < 0) ? ((size.height() - icon.height()) / 2) : pos.y(), - icon.width(), - icon.height()); - }, lifetime()); - - // Paint. - _content->paintRequest( - ) | rpl::start_with_next([=](QRect clip) { - Painter p(_content); - - _crossLineMuteAnimation.paint( - p, - _muteIconRect.topLeft(), - 1. - _crossLineProgress); - - if (_radialInfo.state.has_value() && _switchAnimation.animating()) { - const auto radialProgress = _radialInfo.realShowProgress; - - auto r = *_radialInfo.state; - r.shown = 1.; - if (_radialInfo.isDirectionToShow) { - const auto to = r.arcFrom - kRadialFinishArcShift; - ComputeRadialFinish(r.arcFrom, radialProgress, to); - ComputeRadialFinish(r.arcLength, radialProgress); - } else { - r.arcLength = RadialState::kFull; - } - - const auto opacity = (radialProgress > kOverlapProgressRadialHide) - ? 0. - : _blobs->switchConnectingProgress(); - p.setOpacity(opacity); - InfiniteRadialAnimation::Draw( - p, - r, - _st.bgPosition, - _radialInfo.st.size, - _content->width(), - QPen(_radialInfo.st.color), - _radialInfo.st.thickness); - } else if (_radial) { - auto state = _radial->computeState(); - state.shown = 1.; - - InfiniteRadialAnimation::Draw( - p, - std::move(state), - _st.bgPosition, - _radialInfo.st.size, - _content->width(), - QPen(_radialInfo.st.color), - _radialInfo.st.thickness); - } - }, _content->lifetime()); -} - -void CallMuteButton::updateLabelsGeometry() { - updateLabelGeometry(_content->geometry(), _label->size()); - updateCenterLabelGeometry(_content->geometry(), _centerLabel->size()); - updateSublabelGeometry(_content->geometry(), _sublabel->size()); -} - -void CallMuteButton::updateLabelGeometry(QRect my, QSize size) { - const auto skip = st::callMuteButtonSublabelSkip - + st::callMuteButtonLabelsSkip; - _label->moveToLeft( - my.x() + (my.width() - size.width()) / 2 + _labelShakeShift, - my.y() + my.height() - _label->height() - skip, - my.width()); -} - -void CallMuteButton::updateCenterLabelGeometry(QRect my, QSize size) { - const auto skip = (st::callMuteButtonSublabelSkip / 2) - + st::callMuteButtonLabelsSkip; - _centerLabel->moveToLeft( - my.x() + (my.width() - size.width()) / 2 + _labelShakeShift, - my.y() + my.height() - _centerLabel->height() - skip, - my.width()); -} - -void CallMuteButton::updateSublabelGeometry(QRect my, QSize size) { - const auto skip = st::callMuteButtonLabelsSkip; - _sublabel->moveToLeft( - my.x() + (my.width() - size.width()) / 2 + _labelShakeShift, - my.y() + my.height() - _sublabel->height() - skip, - my.width()); -} - -void CallMuteButton::shake() { - if (_shakeAnimation.animating()) { - return; - } - const auto update = [=] { - const auto fullProgress = _shakeAnimation.value(1.) * 6; - const auto segment = std::clamp(int(std::floor(fullProgress)), 0, 5); - const auto part = fullProgress - segment; - const auto from = (segment == 0) - ? 0. - : (segment == 1 || segment == 3 || segment == 5) - ? 1. - : -1.; - const auto to = (segment == 0 || segment == 2 || segment == 4) - ? 1. - : (segment == 1 || segment == 3) - ? -1. - : 0.; - const auto shift = from * (1. - part) + to * part; - _labelShakeShift = int(std::round(shift * st::shakeShift)); - updateLabelsGeometry(); - }; - _shakeAnimation.start( - update, - 0., - 1., - kShiftDuration); -} - -CallMuteButton::HandleMouseState CallMuteButton::HandleMouseStateFromType( - CallMuteButtonType type) { - switch (type) { - case CallMuteButtonType::Active: - case CallMuteButtonType::Muted: - return HandleMouseState::Enabled; - case CallMuteButtonType::Connecting: - return HandleMouseState::Disabled; - case CallMuteButtonType::ForceMuted: - return HandleMouseState::Blocked; - } - Unexpected("Type in HandleMouseStateFromType."); -} - -void CallMuteButton::setState(const CallMuteButtonState &state) { - _state = state; -} - -void CallMuteButton::setLevel(float level) { - _level = level; - _blobs->setLevel(level); -} - -rpl::producer CallMuteButton::clicks() const { - return _content->clicks(); -} - -QSize CallMuteButton::innerSize() const { - return innerGeometry().size(); -} - -QRect CallMuteButton::innerGeometry() const { - const auto &skip = _st.outerRadius; - return QRect( - _content->x(), - _content->y(), - _content->width() - 2 * skip, - _content->width() - 2 * skip); -} - -void CallMuteButton::moveInner(QPoint position) { - const auto &skip = _st.outerRadius; - _content->move(position - QPoint(skip, skip)); - - { - const auto offset = QPoint( - (_blobs->width() - _content->width()) / 2, - (_blobs->height() - _content->width()) / 2); - _blobs->move(_content->pos() - offset); - } -} - -void CallMuteButton::setVisible(bool visible) { - _content->setVisible(visible); - _blobs->setVisible(visible); -} - -void CallMuteButton::raise() { - _blobs->raise(); - _content->raise(); -} - -void CallMuteButton::lower() { - _content->lower(); - _blobs->lower(); -} - -void CallMuteButton::setHandleMouseState(HandleMouseState state) { - if (_handleMouseState == state) { - return; - } - _handleMouseState = state; - const auto handle = (_handleMouseState != HandleMouseState::Disabled); - const auto pointer = (_handleMouseState == HandleMouseState::Enabled); - _content->setAttribute(Qt::WA_TransparentForMouseEvents, !handle); - _content->setPointerCursor(pointer); -} - -void CallMuteButton::overridesColors( - CallMuteButtonType fromType, - CallMuteButtonType toType, - float64 progress) { - const auto forceMutedToConnecting = [](CallMuteButtonType &type) { - if (type == CallMuteButtonType::ForceMuted) { - type = CallMuteButtonType::Connecting; - } - }; - forceMutedToConnecting(toType); - forceMutedToConnecting(fromType); - const auto toInactive = IsInactive(toType); - const auto fromInactive = IsInactive(fromType); - if (toInactive && (progress == 1)) { - _colorOverrides.fire({ std::nullopt, std::nullopt }); - return; - } - auto from = _colors.find(fromType)->second.stops[0].second; - auto to = _colors.find(toType)->second.stops[0].second; - auto fromRipple = from; - auto toRipple = to; - if (!toInactive) { - toRipple.setAlpha(kOverrideColorRippleAlpha); - to.setAlpha(kOverrideColorBgAlpha); - } - if (!fromInactive) { - fromRipple.setAlpha(kOverrideColorRippleAlpha); - from.setAlpha(kOverrideColorBgAlpha); - } - const auto resultBg = anim::color(from, to, progress); - const auto resultRipple = anim::color(fromRipple, toRipple, progress); - _colorOverrides.fire({ resultBg, resultRipple }); -} - -rpl::producer CallMuteButton::colorOverrides() const { - return _colorOverrides.events(); -} - -rpl::lifetime &CallMuteButton::lifetime() { - return _blobs->lifetime(); -} - -CallMuteButton::~CallMuteButton() = default; - -} // namespace Ui diff --git a/ui/widgets/call_mute_button.h b/ui/widgets/call_mute_button.h deleted file mode 100644 index 154bfb3..0000000 --- a/ui/widgets/call_mute_button.h +++ /dev/null @@ -1,126 +0,0 @@ -// This file is part of Desktop App Toolkit, -// a set of libraries for developing nice desktop applications. -// -// For license and copyright information please follow this link: -// https://github.com/desktop-app/legal/blob/master/LEGAL -// -#pragma once - -#include "base/unique_qptr.h" -#include "ui/effects/animations.h" -#include "ui/effects/cross_line.h" -#include "ui/effects/gradient.h" -#include "ui/effects/radial_animation.h" - -namespace Ui { - -class BlobsWidget; - -class AbstractButton; -class FlatLabel; -class RpWidget; -class AnimatedLabel; - -struct CallButtonColors; - -enum class CallMuteButtonType { - Connecting, - Active, - Muted, - ForceMuted, -}; - -struct CallMuteButtonState { - QString text; - QString subtext; - CallMuteButtonType type = CallMuteButtonType::Connecting; -}; - -class CallMuteButton final { -public: - explicit CallMuteButton( - not_null parent, - rpl::producer &&hideBlobs, - CallMuteButtonState initial = CallMuteButtonState()); - ~CallMuteButton(); - - void setState(const CallMuteButtonState &state); - void setLevel(float level); - [[nodiscard]] rpl::producer clicks() const; - - [[nodiscard]] QSize innerSize() const; - [[nodiscard]] QRect innerGeometry() const; - void moveInner(QPoint position); - - void shake(); - - void setVisible(bool visible); - void show() { - setVisible(true); - } - void hide() { - setVisible(false); - } - void raise(); - void lower(); - - [[nodiscard]] rpl::producer colorOverrides() const; - - [[nodiscard]] rpl::lifetime &lifetime(); - -private: - enum class HandleMouseState { - Enabled, - Blocked, - Disabled, - }; - struct RadialInfo { - std::optional state = std::nullopt; - bool isDirectionToShow = false; - rpl::variable rawShowProgress = 0.; - float64 realShowProgress = 0.; - const style::InfiniteRadialAnimation &st = st::callConnectingRadial; - }; - void init(); - void overridesColors( - CallMuteButtonType fromType, - CallMuteButtonType toType, - float64 progress); - - void setHandleMouseState(HandleMouseState state); - void updateCenterLabelGeometry(QRect my, QSize size); - void updateLabelGeometry(QRect my, QSize size); - void updateSublabelGeometry(QRect my, QSize size); - void updateLabelsGeometry(); - - [[nodiscard]] static HandleMouseState HandleMouseStateFromType( - CallMuteButtonType type); - - rpl::variable _state; - float _level = 0.; - float64 _crossLineProgress = 0.; - QRect _muteIconRect; - HandleMouseState _handleMouseState = HandleMouseState::Enabled; - - const style::CallButton &_st; - - const base::unique_qptr _blobs; - const base::unique_qptr _content; - const base::unique_qptr _centerLabel; - const base::unique_qptr _label; - const base::unique_qptr _sublabel; - int _labelShakeShift = 0; - - RadialInfo _radialInfo; - std::unique_ptr _radial; - const base::flat_map _colors; - - CrossLineAnimation _crossLineMuteAnimation; - Animations::Simple _switchAnimation; - Animations::Simple _shakeAnimation; - - rpl::event_stream _colorOverrides; - -}; - -} // namespace Ui diff --git a/ui/widgets/dropdown_menu.cpp b/ui/widgets/dropdown_menu.cpp index a8f1960..d70ddb8 100644 --- a/ui/widgets/dropdown_menu.cpp +++ b/ui/widgets/dropdown_menu.cpp @@ -95,7 +95,7 @@ void DropdownMenu::handleTriggered(const Menu::CallbackData &data) { if (!popupSubmenuFromAction(data)) { hideMenu(); _triggering = true; - emit data.action->trigger(); + data.action->trigger(); _triggering = false; if (_deleteLater) { _deleteLater = false; diff --git a/ui/widgets/dropdown_menu.h b/ui/widgets/dropdown_menu.h index 166616f..214ecdd 100644 --- a/ui/widgets/dropdown_menu.h +++ b/ui/widgets/dropdown_menu.h @@ -27,8 +27,12 @@ public: _hiddenCallback = std::move(callback); } - const std::vector> &actions() const; - bool empty() const; + [[nodiscard]] const std::vector> &actions() const; + [[nodiscard]] bool empty() const; + + [[nodiscard]] not_null menu() const { + return _menu; + } ~DropdownMenu(); @@ -39,7 +43,7 @@ protected: void mouseMoveEvent(QMouseEvent *e) override; void mousePressEvent(QMouseEvent *e) override; -private slots: +private Q_SLOTS: void onHidden() { hideFinish(); } diff --git a/ui/widgets/inner_dropdown.h b/ui/widgets/inner_dropdown.h index be29bf4..e19a1ea 100644 --- a/ui/widgets/inner_dropdown.h +++ b/ui/widgets/inner_dropdown.h @@ -81,7 +81,7 @@ protected: int resizeGetHeight(int newWidth) override; -private slots: +private Q_SLOTS: void onHideAnimated() { hideAnimated(); } diff --git a/ui/widgets/input_fields.cpp b/ui/widgets/input_fields.cpp index 51bb098..bd7adcb 100644 --- a/ui/widgets/input_fields.cpp +++ b/ui/widgets/input_fields.cpp @@ -1094,7 +1094,7 @@ void FlatInput::focusInEvent(QFocusEvent *e) { update(); } QLineEdit::focusInEvent(e); - emit focused(); + focused(); } void FlatInput::focusOutEvent(QFocusEvent *e) { @@ -1108,7 +1108,7 @@ void FlatInput::focusOutEvent(QFocusEvent *e) { update(); } QLineEdit::focusOutEvent(e); - emit blurred(); + blurred(); } void FlatInput::resizeEvent(QResizeEvent *e) { @@ -1196,13 +1196,13 @@ void FlatInput::keyPressEvent(QKeyEvent *e) { if (wasText == newText) { // call correct manually correctValue(wasText, newText); _oldtext = newText; - if (wasText != _oldtext) emit changed(); + if (wasText != _oldtext) changed(); updatePlaceholder(); } if (e->key() == Qt::Key_Escape) { - emit cancelled(); + cancelled(); } else if (e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) { - emit submitted(e->modifiers()); + submitted(e->modifiers()); #ifdef Q_OS_MAC } else if (e->key() == Qt::Key_E && e->modifiers().testFlag(Qt::ControlModifier)) { auto selected = selectedText(); @@ -1218,7 +1218,7 @@ void FlatInput::onTextEdited() { correctValue(wasText, newText); _oldtext = newText; - if (wasText != _oldtext) emit changed(); + if (wasText != _oldtext) changed(); updatePlaceholder(); Integration::Instance().textActionsUpdated(); @@ -1604,7 +1604,7 @@ bool InputField::heightAutoupdated() { void InputField::checkContentHeight() { if (heightAutoupdated()) { - emit resized(); + resized(); } } @@ -1800,13 +1800,13 @@ void InputField::focusInEventInner(QFocusEvent *e) { : (width() / 2); setFocused(true); _inner->QTextEdit::focusInEvent(e); - emit focused(); + focused(); } void InputField::focusOutEventInner(QFocusEvent *e) { setFocused(false); _inner->QTextEdit::focusOutEvent(e); - emit blurred(); + blurred(); } void InputField::setFocused(bool focused) { @@ -2335,7 +2335,7 @@ void InputField::handleContentsChanged() { if (tagsChanged || (_lastTextWithTags.text != currentText)) { _lastTextWithTags.text = currentText; const auto weak = MakeWeak(this); - emit changed(); + changed(); if (!weak) { return; } @@ -2690,15 +2690,15 @@ void InputField::keyPressEventInner(QKeyEvent *e) { && revertFormatReplace()) { e->accept(); } else if (enter && enterSubmit) { - emit submitted(e->modifiers()); + submitted(e->modifiers()); } else if (e->key() == Qt::Key_Escape) { e->ignore(); - emit cancelled(); + cancelled(); } else if (e->key() == Qt::Key_Tab || e->key() == Qt::Key_Backtab) { if (alt || ctrl) { e->ignore(); } else if (_customTab) { - emit tabbed(); + tabbed(); } else if (!focusNextPrevChild(e->key() == Qt::Key_Tab && !shift)) { e->ignore(); } @@ -3857,13 +3857,13 @@ void MaskedInputField::focusInEvent(QFocusEvent *e) { _borderAnimationStart = (e->reason() == Qt::MouseFocusReason) ? mapFromGlobal(QCursor::pos()).x() : (width() / 2); setFocused(true); QLineEdit::focusInEvent(e); - emit focused(); + focused(); } void MaskedInputField::focusOutEvent(QFocusEvent *e) { setFocused(false); QLineEdit::focusOutEvent(e); - emit blurred(); + blurred(); } void MaskedInputField::setFocused(bool focused) { @@ -3989,14 +3989,14 @@ void MaskedInputField::keyPressEvent(QKeyEvent *e) { correctValue(wasText, wasCursor, newText, newCursor); _oldtext = newText; _oldcursor = newCursor; - if (wasText != _oldtext) emit changed(); + if (wasText != _oldtext) changed(); startPlaceholderAnimation(); } if (e->key() == Qt::Key_Escape) { e->ignore(); - emit cancelled(); + cancelled(); } else if (e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) { - emit submitted(e->modifiers()); + submitted(e->modifiers()); #ifdef Q_OS_MAC } else if (e->key() == Qt::Key_E && e->modifiers().testFlag(Qt::ControlModifier)) { auto selected = selectedText(); @@ -4014,7 +4014,7 @@ void MaskedInputField::onTextEdited() { correctValue(wasText, wasCursor, newText, newCursor); _oldtext = newText; _oldcursor = newCursor; - if (wasText != _oldtext) emit changed(); + if (wasText != _oldtext) changed(); startPlaceholderAnimation(); Integration::Instance().textActionsUpdated(); diff --git a/ui/widgets/input_fields.h b/ui/widgets/input_fields.h index b9290ce..dec13d1 100644 --- a/ui/widgets/input_fields.h +++ b/ui/widgets/input_fields.h @@ -85,13 +85,13 @@ public: return _oldtext; } -public slots: +public Q_SLOTS: void onTextChange(const QString &text); void onTextEdited(); void onTouchTimer(); -signals: +Q_SIGNALS: void changed(); void cancelled(); void submitted(Qt::KeyboardModifiers); @@ -338,7 +338,7 @@ public: ~InputField(); -private slots: +private Q_SLOTS: void onTouchTimer(); void onDocumentContentsChange(int position, int charsRemoved, int charsAdded); @@ -349,7 +349,7 @@ private slots: void onFocusInner(); -signals: +Q_SIGNALS: void changed(); void submitted(Qt::KeyboardModifiers); void cancelled(); @@ -589,7 +589,7 @@ public: startPlaceholderAnimation(); } -public slots: +public Q_SLOTS: void onTextChange(const QString &text); void onCursorPositionChanged(int oldPosition, int position); @@ -597,7 +597,7 @@ public slots: void onTouchTimer(); -signals: +Q_SIGNALS: void changed(); void cancelled(); void submitted(Qt::KeyboardModifiers); diff --git a/ui/widgets/labels.h b/ui/widgets/labels.h index c4327c8..c43bf0f 100644 --- a/ui/widgets/labels.h +++ b/ui/widgets/labels.h @@ -166,7 +166,7 @@ protected: int resizeGetHeight(int newWidth) override; -private slots: +private Q_SLOTS: void onCopySelectedText(); void onCopyContextText(); diff --git a/ui/widgets/menu/menu.cpp b/ui/widgets/menu/menu.cpp index 61ee9a4..a87a642 100644 --- a/ui/widgets/menu/menu.cpp +++ b/ui/widgets/menu/menu.cpp @@ -129,28 +129,29 @@ not_null Menu::addAction(base::unique_qptr widget) { const auto raw = widget.get(); _actionWidgets.push_back(std::move(widget)); - raw->minWidthValue( + rpl::combine( + raw->minWidthValue(), + raw->heightValue() ) | rpl::start_with_next([=] { const auto newWidth = _forceWidth ? _forceWidth : std::clamp( _actionWidgets.empty() - ? 0 - : (*ranges::max_element( - _actionWidgets, - std::less<>(), - &ItemBase::minWidth))->minWidth(), + ? 0 + : (*ranges::max_element( + _actionWidgets, + std::less<>(), + &ItemBase::minWidth))->minWidth(), _st.widthMin, _st.widthMax); - resizeFromInner(newWidth, height()); + const auto newHeight = ranges::accumulate( + _actionWidgets, + 0, + ranges::plus(), + &ItemBase::height); + resizeFromInner(newWidth, newHeight); }, raw->lifetime()); - const auto newHeight = ranges::accumulate( - _actionWidgets, - 0, - ranges::plus(), - &ItemBase::height); - resizeFromInner(width(), newHeight); updateSelected(QCursor::pos()); return action; diff --git a/ui/widgets/popup_menu.cpp b/ui/widgets/popup_menu.cpp index 38d0acc..22212bb 100644 --- a/ui/widgets/popup_menu.cpp +++ b/ui/widgets/popup_menu.cpp @@ -175,7 +175,7 @@ void PopupMenu::handleTriggered(const Menu::CallbackData &data) { if (!popupSubmenuFromAction(data)) { _triggering = true; hideMenu(); - emit data.action->trigger(); + data.action->trigger(); _triggering = false; if (_deleteLater) { _deleteLater = false; diff --git a/ui/widgets/scroll_area.cpp b/ui/widgets/scroll_area.cpp index 526bd06..b67f72b 100644 --- a/ui/widgets/scroll_area.cpp +++ b/ui/widgets/scroll_area.cpp @@ -76,8 +76,8 @@ void ScrollBar::updateBar(bool force) { if (h >= rh || !area()->scrollTopMax() || rh < _st->minHeight) { if (!isHidden()) hide(); bool newTopSh = (_st->topsh < 0), newBottomSh = (_st->bottomsh < 0); - if (newTopSh != _topSh || force) emit topShadowVisibility(_topSh = newTopSh); - if (newBottomSh != _bottomSh || force) emit bottomShadowVisibility(_bottomSh = newBottomSh); + if (newTopSh != _topSh || force) topShadowVisibility(_topSh = newTopSh); + if (newBottomSh != _bottomSh || force) bottomShadowVisibility(_bottomSh = newBottomSh); return; } @@ -105,8 +105,8 @@ void ScrollBar::updateBar(bool force) { } if (_vertical) { bool newTopSh = (_st->topsh < 0) || (area()->scrollTop() > _st->topsh), newBottomSh = (_st->bottomsh < 0) || (area()->scrollTop() < area()->scrollTopMax() - _st->bottomsh); - if (newTopSh != _topSh || force) emit topShadowVisibility(_topSh = newTopSh); - if (newBottomSh != _bottomSh || force) emit bottomShadowVisibility(_bottomSh = newBottomSh); + if (newTopSh != _topSh || force) topShadowVisibility(_topSh = newTopSh); + if (newBottomSh != _bottomSh || force) bottomShadowVisibility(_bottomSh = newBottomSh); } if (isHidden()) show(); } @@ -252,7 +252,7 @@ void ScrollBar::mousePressEvent(QMouseEvent *e) { } area()->setMovingByScrollBar(true); - emit area()->scrollStarted(); + area()->scrollStarted(); } void ScrollBar::mouseReleaseEvent(QMouseEvent *e) { @@ -260,7 +260,7 @@ void ScrollBar::mouseReleaseEvent(QMouseEvent *e) { setMoving(false); area()->setMovingByScrollBar(false); - emit area()->scrollFinished(); + area()->scrollFinished(); } if (!_over) { setMouseTracking(false); @@ -342,7 +342,7 @@ void ScrollArea::onScrolled() { } } if (em) { - emit scrolled(); + scrolled(); if (!_movingByScrollBar) { SendSynteticMouseEvent(this, QEvent::MouseMove, Qt::NoButton); } @@ -350,7 +350,7 @@ void ScrollArea::onScrolled() { } void ScrollArea::onInnerResized() { - emit innerResized(); + innerResized(); } int ScrollArea::scrollWidth() const { @@ -601,12 +601,12 @@ void ScrollArea::resizeEvent(QResizeEvent *e) { _verticalBar->recountSize(); _topShadow->setGeometry(QRect(0, 0, width(), qAbs(_st.topsh))); _bottomShadow->setGeometry(QRect(0, height() - qAbs(_st.bottomsh), width(), qAbs(_st.bottomsh))); - emit geometryChanged(); + geometryChanged(); } void ScrollArea::moveEvent(QMoveEvent *e) { QScrollArea::moveEvent(e); - emit geometryChanged(); + geometryChanged(); } void ScrollArea::keyPressEvent(QKeyEvent *e) { diff --git a/ui/widgets/scroll_area.h b/ui/widgets/scroll_area.h index d6c64c1..bcf2fdd 100644 --- a/ui/widgets/scroll_area.h +++ b/ui/widgets/scroll_area.h @@ -56,7 +56,7 @@ public: void paintEvent(QPaintEvent *e); -public slots: +public Q_SLOTS: void changeVisibility(bool shown); private: @@ -75,12 +75,12 @@ public: void hideTimeout(crl::time dt); -private slots: +private Q_SLOTS: void onValueChanged(); void onRangeChanged(); void onHideTimer(); -signals: +Q_SIGNALS: void topShadowVisibility(bool); void bottomShadowVisibility(bool); @@ -180,7 +180,7 @@ protected: void enterEventHook(QEvent *e) override; void leaveEventHook(QEvent *e) override; -public slots: +public Q_SLOTS: void scrollToY(int toTop, int toBottom = -1); void disableScroll(bool dis); void onScrolled(); @@ -189,7 +189,7 @@ public slots: void onTouchTimer(); void onTouchScrollTimer(); -signals: +Q_SIGNALS: void scrolled(); void innerResized(); void scrollStarted(); diff --git a/ui/widgets/tooltip.cpp b/ui/widgets/tooltip.cpp index 788756c..863ed07 100644 --- a/ui/widgets/tooltip.cpp +++ b/ui/widgets/tooltip.cpp @@ -354,7 +354,6 @@ void ImportantTooltip::updateGeometry() { } void ImportantTooltip::resizeEvent(QResizeEvent *e) { - auto inner = countInner(); auto contentTop = _st.padding.top(); if (_useTransparency && (_side & RectPart::Bottom)) { contentTop += _st.arrow; diff --git a/ui/widgets/widgets.style b/ui/widgets/widgets.style index ee44c39..e9a605e 100644 --- a/ui/widgets/widgets.style +++ b/ui/widgets/widgets.style @@ -1431,13 +1431,9 @@ callMuteMinorBlobMaxRadius: 74px; callMuteMajorBlobMinRadius: 67px; callMuteMajorBlobMaxRadius: 77px; -callMuteButtonActiveIcon: icon {{ "calls/voice_unmuted_large", groupCallIconFg }}; callMuteButtonActiveInner: IconButton { width: 136px; height: 165px; - - iconPosition: point(-1px, 50px); - icon: callMuteButtonActiveIcon; } callMuteButtonLabel: FlatLabel(defaultFlatLabel) { textFg: groupCallMembersFg; @@ -1462,29 +1458,15 @@ callMuteButtonActive: CallButton { label: callMuteButtonLabel; } callMuteButtonMuted: CallButton(callMuteButtonActive) { - button: IconButton(callMuteButtonActiveInner) { - icon: icon {{ "calls/voice_muted_large", groupCallIconFg }}; - } bg: groupCallMuted1; label: callMuteButtonLabel; } callMuteButtonConnecting: CallButton(callMuteButtonMuted) { - button: IconButton(callMuteButtonActiveInner) { - icon: icon {{ "calls/voice_muted_large", groupCallIconFg }}; - } bg: callIconBg; label: callMuteButtonLabel; } callMuteButtonLabelAdditional: 5px; -callMuteCrossLine: CrossLineAnimation { - fg: groupCallIconFg; - icon: callMuteButtonActiveIcon; - startPosition: point(7px, 2px); - endPosition: point(34px, 30px); - stroke: 4px; -} - callConnectingRadial: InfiniteRadialAnimation(defaultInfiniteRadialAnimation) { color: lightButtonFg; thickness: 4px;