diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index e8aa5227fc..a224fa23f8 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -907,6 +907,8 @@ PRIVATE platform/mac/touchbar/mac_touchbar_main.mm platform/mac/touchbar/mac_touchbar_manager.h platform/mac/touchbar/mac_touchbar_manager.mm + platform/mac/touchbar/mac_touchbar_media_view.h + platform/mac/touchbar/mac_touchbar_media_view.mm platform/win/audio_win.cpp platform/win/audio_win.h platform/win/file_utilities_win.cpp diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index d34ddafbc0..d02aa7e442 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -62,6 +62,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "styles/style_media_view.h" #include "styles/style_history.h" +#ifdef Q_OS_MAC +#include "platform/mac/touchbar/mac_touchbar_media_view.h" +#endif // Q_OS_MAC + #include #include #include @@ -341,6 +345,15 @@ OverlayWidget::OverlayWidget() setWindowState(Qt::WindowFullScreen); } +#if defined Q_OS_MAC && !defined OS_OSX + TouchBar::SetupMediaViewTouchBar( + winId(), + static_cast(this), + _touchbarTrackState.events(), + _touchbarDisplay.events(), + _touchbarFullscreenToggled.events()); +#endif // Q_OS_MAC && !OS_OSX + Core::App().calls().currentCallValue( ) | rpl::start_with_next([=](Calls::Call *call) { if (!_streamed) { @@ -2014,6 +2027,7 @@ void OverlayWidget::displayPhoto(not_null photo, HistoryItem *item) if (isHidden()) { moveToScreen(); } + _touchbarDisplay.fire(TouchBarItemType::Photo); clearStreaming(); destroyThemePreview(); @@ -2081,6 +2095,8 @@ void OverlayWidget::displayDocument( _themeCloudData = cloud; _radial.stop(); + _touchbarDisplay.fire(TouchBarItemType::None); + refreshMediaViewer(); if (_document) { if (_document->sticker()) { @@ -2284,6 +2300,8 @@ void OverlayWidget::startStreamingPlayer() { void OverlayWidget::initStreamingThumbnail() { Expects(_document != nullptr); + _touchbarDisplay.fire(TouchBarItemType::Video); + const auto good = _documentMedia->goodThumbnail(); const auto useGood = (good != nullptr); const auto thumbnail = _documentMedia->thumbnail(); @@ -2730,6 +2748,7 @@ void OverlayWidget::playbackToggleFullScreen() { } _streamed->controls.setInFullScreen(_fullScreenVideo); + _touchbarFullscreenToggled.fire_copy(_fullScreenVideo); updateControls(); update(); } @@ -2776,6 +2795,7 @@ void OverlayWidget::updatePlaybackState() { const auto state = _streamed->instance.player().prepareLegacyState(); if (state.position != kTimeUnknown && state.length != kTimeUnknown) { _streamed->controls.updatePlayback(state); + _touchbarTrackState.fire_copy(state); } } diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.h b/Telegram/SourceFiles/media/view/media_view_overlay_widget.h index c1519b8926..d0fea49a9c 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.h +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.h @@ -71,6 +71,12 @@ class OverlayWidget final public: OverlayWidget(); + enum class TouchBarItemType { + Photo, + Video, + None, + }; + void showPhoto(not_null photo, HistoryItem *context); void showPhoto(not_null photo, not_null context); void showDocument( @@ -496,6 +502,10 @@ private: base::flat_map _animations; base::flat_map _animationOpacities; + rpl::event_stream _touchbarTrackState; + rpl::event_stream _touchbarDisplay; + rpl::event_stream _touchbarFullscreenToggled; + int _verticalWheelDelta = 0; bool _themePreviewShown = false; diff --git a/Telegram/SourceFiles/platform/mac/touchbar/mac_touchbar_media_view.h b/Telegram/SourceFiles/platform/mac/touchbar/mac_touchbar_media_view.h new file mode 100644 index 0000000000..fe7661bf78 --- /dev/null +++ b/Telegram/SourceFiles/platform/mac/touchbar/mac_touchbar_media_view.h @@ -0,0 +1,26 @@ +/* +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 + +#ifndef OS_OSX + +#include "media/view/media_view_playback_controls.h" +#include "media/view/media_view_overlay_widget.h" + +namespace TouchBar { + +void SetupMediaViewTouchBar( + WId winId, + not_null controlsDelegate, + rpl::producer trackState, + rpl::producer display, + rpl::producer fullscreenToggled); + +} // namespace TouchBar + +#endif // OS_OSX diff --git a/Telegram/SourceFiles/platform/mac/touchbar/mac_touchbar_media_view.mm b/Telegram/SourceFiles/platform/mac/touchbar/mac_touchbar_media_view.mm new file mode 100644 index 0000000000..622aa8ee96 --- /dev/null +++ b/Telegram/SourceFiles/platform/mac/touchbar/mac_touchbar_media_view.mm @@ -0,0 +1,198 @@ +/* +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_media_view.h" + +#ifndef OS_OSX + +#include "media/audio/media_audio.h" +#include "platform/mac/touchbar/mac_touchbar_common.h" +#include "platform/mac/touchbar/mac_touchbar_controls.h" +#include "styles/style_media_player.h" +#include "styles/style_media_view.h" + +#import +#import +#import + +using namespace TouchBar; +using Delegate = Media::View::PlaybackControls::Delegate; +using ItemType = Media::View::OverlayWidget::TouchBarItemType; + +namespace { + +inline NSTouchBarItemIdentifier Format(NSString *s) { + return [NSString stringWithFormat:@"button.%@", s]; +} + +const auto kPlayItemIdentifier = Format(@"playPause"); +const auto kRotateItemIdentifier = Format(@"rotate"); +const auto kFullscreenItemIdentifier = Format(@"fullscreen"); +const auto kPipItemIdentifier = Format(@"pip"); +const auto kTrackItemIdentifier = @"trackPosition"; +const auto kSeekItemIdentifier = @"seekBar"; + +} + +#pragma mark - MediaViewTouchBar + +@interface MediaViewTouchBar : NSTouchBar +- (id)init:(not_null)controlsDelegate + trackState:(rpl::producer)trackState + display:(rpl::producer)display + fullscreenToggled:(rpl::producer)fullscreenToggled; +@end + +@implementation MediaViewTouchBar { + rpl::lifetime _lifetime; +} + +- (id)init:(not_null)controlsDelegate + trackState:(rpl::producer)trackState + display:(rpl::producer)display + fullscreenToggled:(rpl::producer)fullscreenToggled { + self = [super init]; + if (!self) { + return self; + } + const auto allocate = [](NSTouchBarItemIdentifier i) { + return [[NSCustomTouchBarItem alloc] initWithIdentifier:i]; + }; + + auto *playPause = allocate(kPlayItemIdentifier); + { + auto *button = CreateTouchBarButtonWithTwoStates( + st::touchBarIconPlayerPause, + st::touchBarIconPlayerPlay, + _lifetime, + [=](bool value) { + value + ? controlsDelegate->playbackControlsPlay() + : controlsDelegate->playbackControlsPause(); + }, + false, + rpl::duplicate( + trackState + ) | rpl::map([](const auto &state) { + return (state.state == Media::Player::State::Playing); + }) | rpl::distinct_until_changed()); + playPause.view = button; + playPause.customizationLabel = @"Play/Pause"; + } + + auto *rotate = allocate(kRotateItemIdentifier); + { + auto *button = CreateTouchBarButton( + [NSImage imageNamed:NSImageNameTouchBarRotateLeftTemplate], + _lifetime, + [=] { controlsDelegate->playbackControlsRotate(); }); + rotate.view = button; + rotate.customizationLabel = @"Rotate"; + } + + auto *fullscreen = allocate(kFullscreenItemIdentifier); + { + auto *button = CreateTouchBarButtonWithTwoStates( + [NSImage imageNamed:NSImageNameTouchBarExitFullScreenTemplate], + [NSImage imageNamed:NSImageNameTouchBarEnterFullScreenTemplate], + _lifetime, + [=](bool value) { + value + ? controlsDelegate->playbackControlsFromFullScreen() + : controlsDelegate->playbackControlsToFullScreen(); + }, + true, + std::move(fullscreenToggled)); + fullscreen.view = button; + fullscreen.customizationLabel = @"Fullscreen"; + } + + auto *pip = allocate(kPipItemIdentifier); + { + auto *button = TouchBar::CreateTouchBarButton( + CreateNSImageFromStyleIcon( + st::mediaviewPipButton.icon, + kCircleDiameter / 4 * 3), + _lifetime, + [=] { controlsDelegate->playbackControlsToPictureInPicture(); }); + pip.view = button; + pip.customizationLabel = @"Picture-in-Picture"; + } + + auto *trackPosition = CreateTouchBarTrackPosition( + kTrackItemIdentifier, + rpl::duplicate(trackState)); + + auto *seekBar = TouchBar::CreateTouchBarSlider( + kSeekItemIdentifier, + _lifetime, + [=](bool touchUp, double value, double duration) { + const auto progress = value * duration; + touchUp + ? controlsDelegate->playbackControlsSeekFinished(progress) + : controlsDelegate->playbackControlsSeekProgress(progress); + }, + std::move(trackState)); + + self.templateItems = [NSSet setWithArray:@[ + playPause, + rotate, + fullscreen, + pip, + seekBar, + trackPosition]]; + + const auto items = [](ItemType type) { + switch (type) { + case ItemType::Photo: return @[kRotateItemIdentifier]; + case ItemType::Video: return @[ + kRotateItemIdentifier, + kFullscreenItemIdentifier, + kPipItemIdentifier, + kPlayItemIdentifier, + kSeekItemIdentifier, + kTrackItemIdentifier]; + default: return @[]; + }; + }; + + std::move( + display + ) | rpl::distinct_until_changed( + ) | rpl::start_with_next([=](ItemType type) { + TouchBar::CustomEnterToCocoaEventLoop([=] { + self.defaultItemIdentifiers = items(type); + }); + }, _lifetime); + + return self; +} + +@end // @implementation MediaViewTouchBar + +namespace TouchBar { + +void SetupMediaViewTouchBar( + WId winId, + not_null controlsDelegate, + rpl::producer trackState, + rpl::producer display, + rpl::producer fullscreenToggled) { + auto *window = [reinterpret_cast(winId) window]; + CustomEnterToCocoaEventLoop([=] { + [window setTouchBar:[[[MediaViewTouchBar alloc] + init:std::move(controlsDelegate) + trackState:std::move(trackState) + display:std::move(display) + fullscreenToggled:std::move(fullscreenToggled) + ] autorelease]]; + }); +} + +} // namespace TouchBar + +#endif // OS_OSX