From 8de6d0b63bcef2c1cfb0c1229bdbe0c9f7e99a2b Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Mon, 13 Jul 2020 13:13:01 +0300 Subject: [PATCH] Moved creation of touchbar buttons and sliders to separate file. --- Telegram/CMakeLists.txt | 2 + .../mac/touchbar/mac_touchbar_audio.mm | 102 ++--------- .../mac/touchbar/mac_touchbar_controls.h | 59 +++++++ .../mac/touchbar/mac_touchbar_controls.mm | 162 ++++++++++++++++++ 4 files changed, 242 insertions(+), 83 deletions(-) create mode 100644 Telegram/SourceFiles/platform/mac/touchbar/mac_touchbar_controls.h create mode 100644 Telegram/SourceFiles/platform/mac/touchbar/mac_touchbar_controls.mm diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index d7084f2c23..e8aa5227fc 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -901,6 +901,8 @@ PRIVATE platform/mac/touchbar/mac_touchbar_audio.mm platform/mac/touchbar/mac_touchbar_common.h platform/mac/touchbar/mac_touchbar_common.mm + platform/mac/touchbar/mac_touchbar_controls.h + platform/mac/touchbar/mac_touchbar_controls.mm platform/mac/touchbar/mac_touchbar_main.h platform/mac/touchbar/mac_touchbar_main.mm platform/mac/touchbar/mac_touchbar_manager.h diff --git a/Telegram/SourceFiles/platform/mac/touchbar/mac_touchbar_audio.mm b/Telegram/SourceFiles/platform/mac/touchbar/mac_touchbar_audio.mm index 4f12bcf39c..89e934ca96 100644 --- a/Telegram/SourceFiles/platform/mac/touchbar/mac_touchbar_audio.mm +++ b/Telegram/SourceFiles/platform/mac/touchbar/mac_touchbar_audio.mm @@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "media/audio/media_audio.h" #include "media/player/media_player_instance.h" #include "platform/mac/touchbar/mac_touchbar_common.h" +#include "platform/mac/touchbar/mac_touchbar_controls.h" #include "styles/style_media_player.h" #import @@ -40,25 +41,6 @@ const auto kPreviousItemIdentifier = Format(@"previousItem"); const auto kClosePlayerItemIdentifier = Format(@"closePlayer"); const auto kCurrentPositionItemIdentifier = Format(@"currentPosition"); -API_AVAILABLE(macos(10.12.2)) -NSButton* CreateTouchBarButton( - const style::icon &icon, - rpl::lifetime &lifetime, - Fn callback) { - id block = [^{ - Core::Sandbox::Instance().customEnterFromEventLoop(callback); - } copy]; - - NSButton* button = [NSButton - buttonWithImage:CreateNSImageFromStyleIcon(icon, kCircleDiameter / 2) - target:block - action:@selector(invoke)]; - lifetime.add([=] { - [block release]; - }); - return button; -} - } // namespace #pragma mark - TouchBarAudioPlayer @@ -113,58 +95,24 @@ NSButton* CreateTouchBarButton( }; if (isEqual(kSeekBarItemIdentifier)) { - auto *item = [[NSSliderTouchBarItem alloc] initWithIdentifier:itemId]; - item.slider.minValue = 0.0f; - item.slider.maxValue = 1.0f; - item.customizationLabel = @"Seek Bar"; - - id block = [^{ - // https://stackoverflow.com/a/45891017 - auto *event = [[NSApplication sharedApplication] currentEvent]; - const auto touchUp = [event - touchesMatchingPhase:NSTouchPhaseEnded - inView:nil].count > 0; - Core::Sandbox::Instance().customEnterFromEventLoop([=] { + auto *item = TouchBar::CreateTouchBarSlider( + itemId, + _lifetime, + [=](bool touchUp, double value, double duration) { if (touchUp) { - mediaPlayer->finishSeeking(kSongType, item.doubleValue); + mediaPlayer->finishSeeking(kSongType, value); } else { mediaPlayer->startSeeking(kSongType); } - }); - } copy]; - - rpl::duplicate( - _trackState - ) | rpl::start_with_next([=](const Media::Player::TrackState &state) { - const auto stop = Media::Player::IsStoppedOrStopping(state.state); - const auto duration = double(stop ? 0 : state.length); - auto slider = item.slider; - if (duration <= 0) { - slider.enabled = false; - slider.doubleValue = 0; - } else { - slider.enabled = true; - if (!slider.highlighted) { - const auto pos = stop - ? 0 - : std::max(state.position, int64(0)); - slider.doubleValue = (pos / duration) * slider.maxValue; - } - } - }, _lifetime); - - item.target = block; - item.action = @selector(invoke); - _lifetime.add([=] { - [block release]; - }); + }, + rpl::duplicate(_trackState)); return [item autorelease]; } else if (isEqual(kNextItemIdentifier) || isEqual(kPreviousItemIdentifier)) { const auto isNext = isEqual(kNextItemIdentifier); auto *item = [[NSCustomTouchBarItem alloc] initWithIdentifier:itemId]; - auto *button = CreateTouchBarButton( + auto *button = TouchBar::CreateTouchBarButton( isNext ? st::touchBarIconPlayerNext : st::touchBarIconPlayerPrevious, @@ -191,36 +139,24 @@ NSButton* CreateTouchBarButton( } else if (isEqual(kPlayItemIdentifier)) { auto *item = [[NSCustomTouchBarItem alloc] initWithIdentifier:itemId]; - auto *button = CreateTouchBarButton( + auto *button = TouchBar::CreateTouchBarButtonWithTwoStates( st::touchBarIconPlayerPause, - _lifetime, - [=] { mediaPlayer->playPause(kSongType); }); - - auto *pause = [button.image retain]; - auto *play = [CreateNSImageFromStyleIcon( st::touchBarIconPlayerPlay, - kCircleDiameter / 2) retain]; - - rpl::duplicate( - _trackState - ) | rpl::start_with_next([=](const auto &state) { - button.image = (state.state == Media::Player::State::Playing) - ? pause - : play; - }, _lifetime); - - _lifetime.add([=] { - // Avoid a memory leak from retaining of images. - [pause release]; - [play release]; - }); + _lifetime, + [=](bool value) { mediaPlayer->playPause(kSongType); }, + false, + rpl::duplicate( + _trackState + ) | rpl::map([](const auto &state) { + return (state.state == Media::Player::State::Playing); + }) | rpl::distinct_until_changed()); item.view = button; item.customizationLabel = @"Play/Pause"; return [item autorelease]; } else if (isEqual(kClosePlayerItemIdentifier)) { auto *item = [[NSCustomTouchBarItem alloc] initWithIdentifier:itemId]; - auto *button = CreateTouchBarButton( + auto *button = TouchBar::CreateTouchBarButton( st::touchBarIconPlayerClose, _lifetime, [=] { _closeRequests.fire({}); }); diff --git a/Telegram/SourceFiles/platform/mac/touchbar/mac_touchbar_controls.h b/Telegram/SourceFiles/platform/mac/touchbar/mac_touchbar_controls.h new file mode 100644 index 0000000000..62c36c66f1 --- /dev/null +++ b/Telegram/SourceFiles/platform/mac/touchbar/mac_touchbar_controls.h @@ -0,0 +1,59 @@ +/* +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 Media { +namespace Player { +struct TrackState; +} // namespace Player +} // namespace Media + +@class NSButton; +@class NSImage; +@class NSSliderTouchBarItem; + +namespace TouchBar { + +[[nodiscard]] API_AVAILABLE(macos(10.12.2)) +NSButton *CreateTouchBarButton( + NSImage *image, + rpl::lifetime &lifetime, + Fn callback); + +[[nodiscard]] API_AVAILABLE(macos(10.12.2)) +NSButton *CreateTouchBarButton( + const style::icon &icon, + rpl::lifetime &lifetime, + Fn callback); + +[[nodiscard]] API_AVAILABLE(macos(10.12.2)) +NSButton *CreateTouchBarButtonWithTwoStates( + NSImage *icon1, + NSImage *icon2, + rpl::lifetime &lifetime, + Fn callback, + bool firstState, + rpl::producer stateChanged = rpl::never()); + +[[nodiscard]] API_AVAILABLE(macos(10.12.2)) +NSButton *CreateTouchBarButtonWithTwoStates( + const style::icon &icon1, + const style::icon &icon2, + rpl::lifetime &lifetime, + Fn callback, + bool firstState, + rpl::producer stateChanged = rpl::never()); + +[[nodiscard]] API_AVAILABLE(macos(10.12.2)) +NSSliderTouchBarItem *CreateTouchBarSlider( + NSString *itemId, + rpl::lifetime &lifetime, + Fn callback, + rpl::producer stateChanged); + +} // namespace TouchBar diff --git a/Telegram/SourceFiles/platform/mac/touchbar/mac_touchbar_controls.mm b/Telegram/SourceFiles/platform/mac/touchbar/mac_touchbar_controls.mm new file mode 100644 index 0000000000..8d75429d2a --- /dev/null +++ b/Telegram/SourceFiles/platform/mac/touchbar/mac_touchbar_controls.mm @@ -0,0 +1,162 @@ +/* +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 "platform/mac/touchbar/mac_touchbar_controls.h" + +#include "core/sandbox.h" // Sandbox::customEnterFromEventLoop() +#include "media/audio/media_audio.h" +#include "platform/mac/touchbar/mac_touchbar_common.h" + +#import +#import +#import +#import + +namespace { + +inline NSImage *Icon(const style::icon &icon) { + using namespace TouchBar; + return CreateNSImageFromStyleIcon(icon, kCircleDiameter / 2); +} + +} // namespace + +namespace TouchBar { + +NSButton *CreateTouchBarButton( + // const style::icon &icon, + NSImage *image, + rpl::lifetime &lifetime, + Fn callback) { + id block = [^{ + Core::Sandbox::Instance().customEnterFromEventLoop(callback); + } copy]; + + NSButton* button = [NSButton + buttonWithImage:image + target:block + action:@selector(invoke)]; + lifetime.add([=] { + [block release]; + }); + return button; +} + +NSButton *CreateTouchBarButton( + const style::icon &icon, + rpl::lifetime &lifetime, + Fn callback) { + return CreateTouchBarButton(Icon(icon), lifetime, std::move(callback)); +} + +NSButton *CreateTouchBarButtonWithTwoStates( + NSImage *icon1, + NSImage *icon2, + rpl::lifetime &lifetime, + Fn callback, + bool firstState, + rpl::producer stateChanged) { + NSButton* button = [NSButton + buttonWithImage:(firstState ? icon2 : icon1) + target:nil + action:nil]; + + const auto isFirstState = lifetime.make_state(firstState); + id block = [^{ + const auto state = *isFirstState; + button.image = state ? icon1 : icon2; + *isFirstState = !state; + Core::Sandbox::Instance().customEnterFromEventLoop([=] { + callback(state); + }); + } copy]; + + button.target = block; + button.action = @selector(invoke); + + std::move( + stateChanged + ) | rpl::start_with_next([=](bool isChangedToFirstState) { + button.image = isChangedToFirstState ? icon1 : icon2; + }, lifetime); + + lifetime.add([=] { + [block release]; + }); + return button; +} + +NSButton *CreateTouchBarButtonWithTwoStates( + const style::icon &icon1, + const style::icon &icon2, + rpl::lifetime &lifetime, + Fn callback, + bool firstState, + rpl::producer stateChanged) { + return CreateTouchBarButtonWithTwoStates( + Icon(icon1), + Icon(icon2), + lifetime, + std::move(callback), + firstState, + std::move(stateChanged)); +} + +NSSliderTouchBarItem *CreateTouchBarSlider( + NSString *itemId, + rpl::lifetime &lifetime, + Fn callback, + rpl::producer stateChanged) { + const auto lastDurationMs = lifetime.make_state(0); + + auto *seekBar = [[NSSliderTouchBarItem alloc] initWithIdentifier:itemId]; + seekBar.slider.minValue = 0.0f; + seekBar.slider.maxValue = 1.0f; + seekBar.customizationLabel = @"Seek Bar"; + + id block = [^{ + // https://stackoverflow.com/a/45891017 + auto *event = [[NSApplication sharedApplication] currentEvent]; + const auto touchUp = [event + touchesMatchingPhase:NSTouchPhaseEnded + inView:nil].count > 0; + Core::Sandbox::Instance().customEnterFromEventLoop([=] { + callback(touchUp, seekBar.doubleValue, *lastDurationMs); + }); + } copy]; + + std::move( + stateChanged + ) | rpl::start_with_next([=](const Media::Player::TrackState &state) { + const auto stop = Media::Player::IsStoppedOrStopping(state.state); + const auto duration = double(stop ? 0 : state.length); + auto slider = seekBar.slider; + if (duration <= 0) { + slider.enabled = false; + slider.doubleValue = 0; + } else { + slider.enabled = true; + if (!slider.highlighted) { + const auto pos = stop + ? 0 + : std::max(state.position, int64(0)); + slider.doubleValue = (pos / duration) * slider.maxValue; + *lastDurationMs = duration; + } + } + }, lifetime); + + seekBar.target = block; + seekBar.action = @selector(invoke); + lifetime.add([=] { + [block release]; + }); + + return seekBar; +} + +} // namespace TouchBar