Moved creation of touchbar buttons and sliders to separate file.

This commit is contained in:
23rd 2020-07-13 13:13:01 +03:00 committed by John Preston
parent 8dceec5a9f
commit 8de6d0b63b
4 changed files with 242 additions and 83 deletions

View File

@ -901,6 +901,8 @@ PRIVATE
platform/mac/touchbar/mac_touchbar_audio.mm platform/mac/touchbar/mac_touchbar_audio.mm
platform/mac/touchbar/mac_touchbar_common.h platform/mac/touchbar/mac_touchbar_common.h
platform/mac/touchbar/mac_touchbar_common.mm 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.h
platform/mac/touchbar/mac_touchbar_main.mm platform/mac/touchbar/mac_touchbar_main.mm
platform/mac/touchbar/mac_touchbar_manager.h platform/mac/touchbar/mac_touchbar_manager.h

View File

@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "media/audio/media_audio.h" #include "media/audio/media_audio.h"
#include "media/player/media_player_instance.h" #include "media/player/media_player_instance.h"
#include "platform/mac/touchbar/mac_touchbar_common.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_player.h"
#import <AppKit/NSButton.h> #import <AppKit/NSButton.h>
@ -40,25 +41,6 @@ const auto kPreviousItemIdentifier = Format(@"previousItem");
const auto kClosePlayerItemIdentifier = Format(@"closePlayer"); const auto kClosePlayerItemIdentifier = Format(@"closePlayer");
const auto kCurrentPositionItemIdentifier = Format(@"currentPosition"); const auto kCurrentPositionItemIdentifier = Format(@"currentPosition");
API_AVAILABLE(macos(10.12.2))
NSButton* CreateTouchBarButton(
const style::icon &icon,
rpl::lifetime &lifetime,
Fn<void()> 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 } // namespace
#pragma mark - TouchBarAudioPlayer #pragma mark - TouchBarAudioPlayer
@ -113,58 +95,24 @@ NSButton* CreateTouchBarButton(
}; };
if (isEqual(kSeekBarItemIdentifier)) { if (isEqual(kSeekBarItemIdentifier)) {
auto *item = [[NSSliderTouchBarItem alloc] initWithIdentifier:itemId]; auto *item = TouchBar::CreateTouchBarSlider(
item.slider.minValue = 0.0f; itemId,
item.slider.maxValue = 1.0f; _lifetime,
item.customizationLabel = @"Seek Bar"; [=](bool touchUp, double value, double duration) {
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([=] {
if (touchUp) { if (touchUp) {
mediaPlayer->finishSeeking(kSongType, item.doubleValue); mediaPlayer->finishSeeking(kSongType, value);
} else { } else {
mediaPlayer->startSeeking(kSongType); mediaPlayer->startSeeking(kSongType);
} }
}); },
} copy]; rpl::duplicate(_trackState));
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];
});
return [item autorelease]; return [item autorelease];
} else if (isEqual(kNextItemIdentifier) } else if (isEqual(kNextItemIdentifier)
|| isEqual(kPreviousItemIdentifier)) { || isEqual(kPreviousItemIdentifier)) {
const auto isNext = isEqual(kNextItemIdentifier); const auto isNext = isEqual(kNextItemIdentifier);
auto *item = [[NSCustomTouchBarItem alloc] initWithIdentifier:itemId]; auto *item = [[NSCustomTouchBarItem alloc] initWithIdentifier:itemId];
auto *button = CreateTouchBarButton( auto *button = TouchBar::CreateTouchBarButton(
isNext isNext
? st::touchBarIconPlayerNext ? st::touchBarIconPlayerNext
: st::touchBarIconPlayerPrevious, : st::touchBarIconPlayerPrevious,
@ -191,36 +139,24 @@ NSButton* CreateTouchBarButton(
} else if (isEqual(kPlayItemIdentifier)) { } else if (isEqual(kPlayItemIdentifier)) {
auto *item = [[NSCustomTouchBarItem alloc] initWithIdentifier:itemId]; auto *item = [[NSCustomTouchBarItem alloc] initWithIdentifier:itemId];
auto *button = CreateTouchBarButton( auto *button = TouchBar::CreateTouchBarButtonWithTwoStates(
st::touchBarIconPlayerPause, st::touchBarIconPlayerPause,
_lifetime,
[=] { mediaPlayer->playPause(kSongType); });
auto *pause = [button.image retain];
auto *play = [CreateNSImageFromStyleIcon(
st::touchBarIconPlayerPlay, st::touchBarIconPlayerPlay,
kCircleDiameter / 2) retain]; _lifetime,
[=](bool value) { mediaPlayer->playPause(kSongType); },
rpl::duplicate( false,
_trackState rpl::duplicate(
) | rpl::start_with_next([=](const auto &state) { _trackState
button.image = (state.state == Media::Player::State::Playing) ) | rpl::map([](const auto &state) {
? pause return (state.state == Media::Player::State::Playing);
: play; }) | rpl::distinct_until_changed());
}, _lifetime);
_lifetime.add([=] {
// Avoid a memory leak from retaining of images.
[pause release];
[play release];
});
item.view = button; item.view = button;
item.customizationLabel = @"Play/Pause"; item.customizationLabel = @"Play/Pause";
return [item autorelease]; return [item autorelease];
} else if (isEqual(kClosePlayerItemIdentifier)) { } else if (isEqual(kClosePlayerItemIdentifier)) {
auto *item = [[NSCustomTouchBarItem alloc] initWithIdentifier:itemId]; auto *item = [[NSCustomTouchBarItem alloc] initWithIdentifier:itemId];
auto *button = CreateTouchBarButton( auto *button = TouchBar::CreateTouchBarButton(
st::touchBarIconPlayerClose, st::touchBarIconPlayerClose,
_lifetime, _lifetime,
[=] { _closeRequests.fire({}); }); [=] { _closeRequests.fire({}); });

View File

@ -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<void()> callback);
[[nodiscard]] API_AVAILABLE(macos(10.12.2))
NSButton *CreateTouchBarButton(
const style::icon &icon,
rpl::lifetime &lifetime,
Fn<void()> callback);
[[nodiscard]] API_AVAILABLE(macos(10.12.2))
NSButton *CreateTouchBarButtonWithTwoStates(
NSImage *icon1,
NSImage *icon2,
rpl::lifetime &lifetime,
Fn<void(bool)> callback,
bool firstState,
rpl::producer<bool> stateChanged = rpl::never<bool>());
[[nodiscard]] API_AVAILABLE(macos(10.12.2))
NSButton *CreateTouchBarButtonWithTwoStates(
const style::icon &icon1,
const style::icon &icon2,
rpl::lifetime &lifetime,
Fn<void(bool)> callback,
bool firstState,
rpl::producer<bool> stateChanged = rpl::never<bool>());
[[nodiscard]] API_AVAILABLE(macos(10.12.2))
NSSliderTouchBarItem *CreateTouchBarSlider(
NSString *itemId,
rpl::lifetime &lifetime,
Fn<void(bool, double, double)> callback,
rpl::producer<Media::Player::TrackState> stateChanged);
} // namespace TouchBar

View File

@ -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 <AppKit/NSButton.h>
#import <AppKit/NSImage.h>
#import <AppKit/NSSlider.h>
#import <AppKit/NSSliderTouchBarItem.h>
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<void()> 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<void()> callback) {
return CreateTouchBarButton(Icon(icon), lifetime, std::move(callback));
}
NSButton *CreateTouchBarButtonWithTwoStates(
NSImage *icon1,
NSImage *icon2,
rpl::lifetime &lifetime,
Fn<void(bool)> callback,
bool firstState,
rpl::producer<bool> stateChanged) {
NSButton* button = [NSButton
buttonWithImage:(firstState ? icon2 : icon1)
target:nil
action:nil];
const auto isFirstState = lifetime.make_state<bool>(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<void(bool)> callback,
bool firstState,
rpl::producer<bool> stateChanged) {
return CreateTouchBarButtonWithTwoStates(
Icon(icon1),
Icon(icon2),
lifetime,
std::move(callback),
firstState,
std::move(stateChanged));
}
NSSliderTouchBarItem *CreateTouchBarSlider(
NSString *itemId,
rpl::lifetime &lifetime,
Fn<void(bool, double, double)> callback,
rpl::producer<Media::Player::TrackState> stateChanged) {
const auto lastDurationMs = lifetime.make_state<crl::time>(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