Moved creation of touchbar buttons and sliders to separate file.
This commit is contained in:
parent
8dceec5a9f
commit
8de6d0b63b
|
@ -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
|
||||||
|
|
|
@ -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); },
|
||||||
|
false,
|
||||||
rpl::duplicate(
|
rpl::duplicate(
|
||||||
_trackState
|
_trackState
|
||||||
) | rpl::start_with_next([=](const auto &state) {
|
) | rpl::map([](const auto &state) {
|
||||||
button.image = (state.state == Media::Player::State::Playing)
|
return (state.state == Media::Player::State::Playing);
|
||||||
? pause
|
}) | rpl::distinct_until_changed());
|
||||||
: play;
|
|
||||||
}, _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({}); });
|
||||||
|
|
|
@ -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
|
|
@ -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
|
Loading…
Reference in New Issue