2019-04-29 16:41:51 +03:00
|
|
|
/*
|
|
|
|
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
|
|
|
|
*/
|
|
|
|
|
|
|
|
#import "touchbar.h"
|
|
|
|
#import <QuartzCore/QuartzCore.h>
|
|
|
|
|
|
|
|
#include "mainwindow.h"
|
|
|
|
#include "mainwidget.h"
|
|
|
|
#include "core/sandbox.h"
|
|
|
|
#include "core/application.h"
|
|
|
|
#include "core/crash_reports.h"
|
|
|
|
#include "storage/localstorage.h"
|
|
|
|
#include "media/audio/media_audio.h"
|
|
|
|
#include "media/player/media_player_instance.h"
|
|
|
|
#include "media/view/media_view_playback_progress.h"
|
|
|
|
#include "media/audio/media_audio.h"
|
|
|
|
#include "platform/mac/mac_utilities.h"
|
|
|
|
#include "platform/platform_specific.h"
|
|
|
|
#include "lang/lang_keys.h"
|
|
|
|
#include "base/timer.h"
|
|
|
|
#include "styles/style_window.h"
|
2019-05-01 16:18:31 +03:00
|
|
|
#include "auth_session.h"
|
|
|
|
#include "data/data_session.h"
|
|
|
|
#include "history/history.h"
|
2019-05-01 20:50:59 +03:00
|
|
|
#include "ui/empty_userpic.h"
|
2019-05-01 21:09:40 +03:00
|
|
|
#include "observer_peer.h"
|
2019-05-01 22:10:09 +03:00
|
|
|
#include "styles/style_dialogs.h"
|
|
|
|
#include "data/data_folder.h"
|
2019-04-29 16:41:51 +03:00
|
|
|
|
|
|
|
namespace {
|
2019-05-01 20:50:59 +03:00
|
|
|
//https://developer.apple.com/design/human-interface-guidelines/macos/touch-bar/touch-bar-icons-and-images/
|
|
|
|
constexpr auto kIdealIconSize = 36;
|
|
|
|
constexpr auto kMaximumIconSize = 44;
|
|
|
|
|
2019-04-30 21:31:42 +03:00
|
|
|
constexpr auto kSavedMessages = 0x001;
|
|
|
|
|
|
|
|
constexpr auto kPlayPause = 0x002;
|
|
|
|
constexpr auto kPlaylistPrevious = 0x003;
|
|
|
|
constexpr auto kPlaylistNext = 0x004;
|
|
|
|
constexpr auto kClosePlayer = 0x005;
|
2019-04-29 19:55:11 +03:00
|
|
|
|
2019-04-29 16:41:51 +03:00
|
|
|
constexpr auto kMs = 1000;
|
2019-04-29 19:55:11 +03:00
|
|
|
|
2019-04-29 16:41:51 +03:00
|
|
|
constexpr auto kSongType = AudioMsgId::Type::Song;
|
2019-05-01 20:50:59 +03:00
|
|
|
|
|
|
|
constexpr auto kSavedMessagesId = 0;
|
2019-05-01 22:10:09 +03:00
|
|
|
constexpr auto kArchiveId = -1;
|
2019-04-29 19:55:11 +03:00
|
|
|
} // namespace
|
2019-04-29 16:41:51 +03:00
|
|
|
|
2019-05-01 16:18:31 +03:00
|
|
|
NSImage *qt_mac_create_nsimage(const QPixmap &pm);
|
|
|
|
|
|
|
|
@interface PinnedDialogButton : NSCustomTouchBarItem {
|
|
|
|
}
|
|
|
|
|
|
|
|
@property(nonatomic, assign) int number;
|
|
|
|
@property(nonatomic, assign) bool waiting;
|
|
|
|
@property(nonatomic, assign) PeerData * peer;
|
|
|
|
|
|
|
|
- (id) init:(int)num;
|
2019-05-01 20:50:59 +03:00
|
|
|
- (id) initSavedMessages;
|
2019-05-01 16:18:31 +03:00
|
|
|
- (NSImage *) getPinImage;
|
|
|
|
- (void)buttonActionPin:(NSButton *)sender;
|
|
|
|
- (void)updatePeerData;
|
|
|
|
|
|
|
|
@end // @interface PinnedDialogButton
|
|
|
|
|
|
|
|
@implementation PinnedDialogButton : NSCustomTouchBarItem
|
|
|
|
|
2019-05-02 10:59:15 +03:00
|
|
|
auto lifetime = rpl::lifetime();
|
|
|
|
|
2019-05-01 16:18:31 +03:00
|
|
|
- (id) init:(int)num {
|
2019-05-01 20:50:59 +03:00
|
|
|
if (num == kSavedMessagesId) {
|
|
|
|
return [self initSavedMessages];
|
2019-05-01 22:10:09 +03:00
|
|
|
} else if (num == kArchiveId) {
|
|
|
|
return [self initArchive];
|
2019-05-01 20:50:59 +03:00
|
|
|
}
|
2019-05-01 16:18:31 +03:00
|
|
|
NSString *identifier = [NSString stringWithFormat:@"%@.pinnedDialog%d", customIDMain, num];
|
|
|
|
self = [super initWithIdentifier:identifier];
|
|
|
|
if (!self) {
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
self.number = num;
|
|
|
|
self.waiting = true;
|
|
|
|
[self updatePeerData];
|
|
|
|
|
|
|
|
NSButton *button = [NSButton buttonWithImage:[self getPinImage] target:self action:@selector(buttonActionPin:)];
|
|
|
|
[button setBordered:NO];
|
|
|
|
[button sizeToFit];
|
2019-05-01 22:10:09 +03:00
|
|
|
[button setHidden:(num > Auth().data().pinnedChatsOrder(nullptr).size())];
|
2019-05-01 16:18:31 +03:00
|
|
|
self.view = button;
|
|
|
|
self.customizationLabel = [NSString stringWithFormat:@"Pinned Dialog %d", num];
|
|
|
|
|
2019-05-01 21:09:40 +03:00
|
|
|
const auto updateImage = [self]() {
|
|
|
|
NSButton *button = self.view;
|
|
|
|
button.image = [self getPinImage];
|
|
|
|
};
|
|
|
|
|
|
|
|
Notify::PeerUpdateViewer(
|
|
|
|
self.peer,
|
|
|
|
Notify::PeerUpdate::Flag::PhotoChanged
|
|
|
|
) | rpl::start_with_next([=] {
|
|
|
|
self.waiting = true;
|
|
|
|
updateImage();
|
2019-05-02 10:59:15 +03:00
|
|
|
}, lifetime);
|
2019-05-01 21:09:40 +03:00
|
|
|
|
2019-05-01 16:18:31 +03:00
|
|
|
base::ObservableViewer(
|
|
|
|
Auth().downloaderTaskFinished()
|
2019-05-01 21:09:40 +03:00
|
|
|
) | rpl::start_with_next([=] {
|
2019-05-01 16:18:31 +03:00
|
|
|
if (self.waiting) {
|
2019-05-01 21:09:40 +03:00
|
|
|
updateImage();
|
2019-05-01 16:18:31 +03:00
|
|
|
}
|
2019-05-02 10:59:15 +03:00
|
|
|
}, lifetime);
|
2019-05-01 16:18:31 +03:00
|
|
|
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
2019-05-01 20:50:59 +03:00
|
|
|
- (id) initSavedMessages {
|
|
|
|
self = [super initWithIdentifier:savedMessages];
|
|
|
|
if (!self) {
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
self.number = kSavedMessagesId;
|
|
|
|
self.waiting = false;
|
|
|
|
|
|
|
|
NSButton *button = [NSButton buttonWithImage:[self getPinImage] target:self action:@selector(buttonActionPin:)];
|
|
|
|
[button setBordered:NO];
|
|
|
|
[button sizeToFit];
|
|
|
|
self.view = button;
|
|
|
|
self.customizationLabel = @"Saved Messages";
|
|
|
|
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
2019-05-01 22:10:09 +03:00
|
|
|
- (id) initArchive {
|
|
|
|
self = [super initWithIdentifier:archiveFolder];
|
|
|
|
if (!self) {
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
self.number = kArchiveId;
|
|
|
|
self.waiting = false;
|
|
|
|
|
|
|
|
NSButton *button = [NSButton buttonWithImage:[self getPinImage] target:self action:@selector(buttonActionPin:)];
|
|
|
|
[button setBordered:NO];
|
|
|
|
[button sizeToFit];
|
|
|
|
self.view = button;
|
|
|
|
self.customizationLabel = @"Archive Folder";
|
|
|
|
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
2019-05-01 16:18:31 +03:00
|
|
|
- (void)updatePeerData {
|
2019-05-01 22:10:09 +03:00
|
|
|
const auto &order = Auth().data().pinnedChatsOrder(nullptr);
|
2019-05-01 16:18:31 +03:00
|
|
|
if (self.number > order.size()) {
|
|
|
|
self.peer = nil;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// Order is reversed.
|
2019-05-01 22:10:09 +03:00
|
|
|
const auto pinned = order.at(self.number - 1);
|
2019-05-01 16:18:31 +03:00
|
|
|
if (const auto history = pinned.history()) {
|
|
|
|
self.peer = history->peer;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)buttonActionPin:(NSButton *)sender {
|
|
|
|
Core::Sandbox::Instance().customEnterFromEventLoop([=] {
|
2019-05-01 22:10:09 +03:00
|
|
|
App::main()->choosePeer(self.number == kSavedMessagesId || self.number == kArchiveId
|
2019-05-01 20:50:59 +03:00
|
|
|
? Auth().userPeerId()
|
|
|
|
: self.peer->id, ShowAtUnreadMsgId);
|
2019-05-01 16:18:31 +03:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (NSImage *) getPinImage {
|
2019-05-01 22:10:09 +03:00
|
|
|
if (self.number <= kSavedMessagesId) {
|
2019-05-01 20:50:59 +03:00
|
|
|
const int s = kIdealIconSize * cRetinaFactor();
|
|
|
|
auto *pix = new QPixmap(s, s);
|
|
|
|
Painter paint(pix);
|
|
|
|
paint.fillRect(QRectF(0, 0, s, s), QColor(0, 0, 0, 255));
|
|
|
|
|
2019-05-01 22:10:09 +03:00
|
|
|
if (self.number == kArchiveId) {
|
|
|
|
paint.fillRect(QRectF(0, 0, s, s), QColor(0, 0, 0, 255));
|
|
|
|
if (const auto folder = Auth().data().folderLoaded(Data::Folder::kId)) {
|
|
|
|
folder->paintUserpic(paint, 0, 0, s);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Ui::EmptyUserpic::PaintSavedMessages(paint, 0, 0, s, s);
|
|
|
|
}
|
2019-05-01 20:50:59 +03:00
|
|
|
pix->setDevicePixelRatio(cRetinaFactor());
|
|
|
|
return static_cast<NSImage*>(qt_mac_create_nsimage(*pix));
|
|
|
|
}
|
2019-05-01 16:18:31 +03:00
|
|
|
if (!self.peer) {
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
self.waiting = !self.peer->userpicLoaded();
|
2019-05-01 20:50:59 +03:00
|
|
|
auto pixmap = self.peer->genUserpic(kIdealIconSize);
|
2019-05-01 16:18:31 +03:00
|
|
|
pixmap.setDevicePixelRatio(cRetinaFactor());
|
|
|
|
return static_cast<NSImage*>(qt_mac_create_nsimage(pixmap));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
|
|
@interface TouchBar()<NSTouchBarDelegate>
|
|
|
|
@end // @interface TouchBar
|
|
|
|
|
2019-04-29 16:41:51 +03:00
|
|
|
@interface TouchBar()<NSTouchBarDelegate>
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation TouchBar
|
|
|
|
|
2019-05-01 16:18:31 +03:00
|
|
|
- (id)init:(NSView *)view{
|
2019-04-29 19:55:11 +03:00
|
|
|
self = [super init];
|
|
|
|
if (self) {
|
2019-04-30 21:31:42 +03:00
|
|
|
self.view = view;
|
2019-04-29 19:55:11 +03:00
|
|
|
self.touchbarItems = @{
|
2019-05-01 20:50:59 +03:00
|
|
|
// savedMessages: [NSMutableDictionary dictionaryWithDictionary:@{
|
|
|
|
// @"type": @"button",
|
|
|
|
// @"name": @"Saved Messages",
|
|
|
|
// @"cmd": [NSNumber numberWithInt:kSavedMessages],
|
|
|
|
// @"image": static_cast<NSImage*>(qt_mac_create_nsimage(*pix)),
|
|
|
|
// }],
|
2019-05-01 16:18:31 +03:00
|
|
|
pinnedDialog1: [NSMutableDictionary dictionaryWithDictionary:@{
|
|
|
|
@"type": @"pinned",
|
|
|
|
@"num": @1,
|
|
|
|
}],
|
2019-05-01 20:50:59 +03:00
|
|
|
// pinnedDialog2: [NSMutableDictionary dictionaryWithDictionary:@{
|
|
|
|
// @"type": @"pinned",
|
|
|
|
// @"num": @2,
|
|
|
|
// }],
|
|
|
|
// pinnedDialog3: [NSMutableDictionary dictionaryWithDictionary:@{
|
|
|
|
// @"type": @"pinned",
|
|
|
|
// @"num": @3,
|
|
|
|
// }],
|
|
|
|
// pinnedDialog4: [NSMutableDictionary dictionaryWithDictionary:@{
|
|
|
|
// @"type": @"pinned",
|
|
|
|
// @"num": @4,
|
|
|
|
// }],
|
|
|
|
// pinnedDialog5: [NSMutableDictionary dictionaryWithDictionary:@{
|
|
|
|
// @"type": @"pinned",
|
|
|
|
// @"num": @5,
|
|
|
|
// }],
|
2019-04-29 19:55:11 +03:00
|
|
|
seekBar: [NSMutableDictionary dictionaryWithDictionary:@{
|
|
|
|
@"type": @"slider",
|
|
|
|
@"name": @"Seek Bar"
|
|
|
|
}],
|
|
|
|
play: [NSMutableDictionary dictionaryWithDictionary:@{
|
|
|
|
@"type": @"button",
|
|
|
|
@"name": @"Play Button",
|
|
|
|
@"cmd": [NSNumber numberWithInt:kPlayPause],
|
|
|
|
@"image": [NSImage imageNamed:NSImageNameTouchBarPauseTemplate],
|
|
|
|
@"imageAlt": [NSImage imageNamed:NSImageNameTouchBarPlayTemplate]
|
|
|
|
}],
|
|
|
|
previousItem: [NSMutableDictionary dictionaryWithDictionary:@{
|
|
|
|
@"type": @"button",
|
|
|
|
@"name": @"Previous Playlist Item",
|
|
|
|
@"cmd": [NSNumber numberWithInt:kPlaylistPrevious],
|
|
|
|
@"image": [NSImage imageNamed:NSImageNameTouchBarGoBackTemplate]
|
|
|
|
}],
|
|
|
|
nextItem: [NSMutableDictionary dictionaryWithDictionary:@{
|
|
|
|
@"type": @"button",
|
|
|
|
@"name": @"Next Playlist Item",
|
|
|
|
@"cmd": [NSNumber numberWithInt:kPlaylistNext],
|
|
|
|
@"image": [NSImage imageNamed:NSImageNameTouchBarGoForwardTemplate]
|
|
|
|
}],
|
2019-04-30 21:31:42 +03:00
|
|
|
closePlayer: [NSMutableDictionary dictionaryWithDictionary:@{
|
2019-04-29 19:55:11 +03:00
|
|
|
@"type": @"button",
|
2019-04-30 21:31:42 +03:00
|
|
|
@"name": @"Close Player",
|
|
|
|
@"cmd": [NSNumber numberWithInt:kClosePlayer],
|
|
|
|
@"image": [NSImage imageNamed:NSImageNameTouchBarExitFullScreenTemplate]
|
2019-04-29 19:55:11 +03:00
|
|
|
}],
|
|
|
|
currentPosition: [NSMutableDictionary dictionaryWithDictionary:@{
|
|
|
|
@"type": @"text",
|
|
|
|
@"name": @"Current Position"
|
|
|
|
}]
|
|
|
|
};
|
|
|
|
}
|
2019-05-01 16:18:31 +03:00
|
|
|
[self createTouchBar];
|
|
|
|
[self setTouchBar:TouchBarType::Main];
|
2019-04-29 19:55:11 +03:00
|
|
|
|
|
|
|
return self;
|
2019-04-29 16:41:51 +03:00
|
|
|
}
|
|
|
|
|
2019-04-29 19:55:11 +03:00
|
|
|
- (void) createTouchBar{
|
2019-04-30 21:31:42 +03:00
|
|
|
_touchBarMain = [[NSTouchBar alloc] init];
|
|
|
|
_touchBarMain.delegate = self;
|
|
|
|
|
|
|
|
_touchBarMain.customizationIdentifier = customIDMain;
|
2019-05-01 16:18:31 +03:00
|
|
|
_touchBarMain.defaultItemIdentifiers = @[savedMessages, pinnedDialog1, pinnedDialog2, pinnedDialog3, pinnedDialog4, pinnedDialog5];
|
2019-04-30 21:31:42 +03:00
|
|
|
_touchBarMain.customizationAllowedItemIdentifiers = @[savedMessages];
|
|
|
|
|
2019-05-01 16:18:31 +03:00
|
|
|
_touchBarAudioPlayer = [[NSTouchBar alloc] init];
|
|
|
|
_touchBarAudioPlayer.delegate = self;
|
2019-04-29 16:41:51 +03:00
|
|
|
|
2019-05-01 16:18:31 +03:00
|
|
|
_touchBarAudioPlayer.customizationIdentifier = customID;
|
|
|
|
_touchBarAudioPlayer.defaultItemIdentifiers = @[play, previousItem, nextItem, seekBar, closePlayer];
|
|
|
|
_touchBarAudioPlayer.customizationAllowedItemIdentifiers = @[play, previousItem,
|
|
|
|
nextItem, currentPosition, seekBar, closePlayer];
|
2019-04-29 16:41:51 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
- (nullable NSTouchBarItem *)touchBar:(NSTouchBar *)touchBar
|
2019-04-29 19:55:11 +03:00
|
|
|
makeItemForIdentifier:(NSTouchBarItemIdentifier)identifier {
|
2019-05-01 16:18:31 +03:00
|
|
|
|
2019-04-29 19:55:11 +03:00
|
|
|
if ([self.touchbarItems[identifier][@"type"] isEqualToString:@"slider"]) {
|
|
|
|
NSSliderTouchBarItem *item = [[NSSliderTouchBarItem alloc] initWithIdentifier:identifier];
|
|
|
|
item.slider.minValue = 0.0f;
|
|
|
|
item.slider.maxValue = 1.0f;
|
|
|
|
item.target = self;
|
|
|
|
item.action = @selector(seekbarChanged:);
|
|
|
|
item.customizationLabel = self.touchbarItems[identifier][@"name"];
|
|
|
|
[self.touchbarItems[identifier] setObject:item.slider forKey:@"view"];
|
|
|
|
return item;
|
|
|
|
} else if ([self.touchbarItems[identifier][@"type"] isEqualToString:@"button"]) {
|
|
|
|
NSCustomTouchBarItem *item = [[NSCustomTouchBarItem alloc] initWithIdentifier:identifier];
|
|
|
|
NSImage *image = self.touchbarItems[identifier][@"image"];
|
|
|
|
NSButton *button = [NSButton buttonWithImage:image target:self action:@selector(buttonAction:)];
|
|
|
|
item.view = button;
|
|
|
|
item.customizationLabel = self.touchbarItems[identifier][@"name"];
|
|
|
|
[self.touchbarItems[identifier] setObject:button forKey:@"view"];
|
|
|
|
return item;
|
|
|
|
} else if ([self.touchbarItems[identifier][@"type"] isEqualToString:@"text"]) {
|
|
|
|
NSCustomTouchBarItem *item = [[NSCustomTouchBarItem alloc] initWithIdentifier:identifier];
|
|
|
|
NSTextField *text = [NSTextField labelWithString:@"0:00"];
|
|
|
|
text.alignment = NSTextAlignmentCenter;
|
|
|
|
item.view = text;
|
|
|
|
item.customizationLabel = self.touchbarItems[identifier][@"name"];
|
|
|
|
[self.touchbarItems[identifier] setObject:text forKey:@"view"];
|
|
|
|
return item;
|
2019-05-01 16:18:31 +03:00
|
|
|
} else if ([self.touchbarItems[identifier][@"type"] isEqualToString:@"pinned"]) {
|
2019-05-01 20:50:59 +03:00
|
|
|
NSCustomTouchBarItem *item = [[NSCustomTouchBarItem alloc] initWithIdentifier:identifier];
|
|
|
|
NSMutableArray *pins = [[NSMutableArray alloc] init];
|
2019-05-01 16:18:31 +03:00
|
|
|
|
2019-05-01 22:10:09 +03:00
|
|
|
for (auto i = -1; i <= 5; i++) {
|
2019-05-01 20:50:59 +03:00
|
|
|
[pins addObject:[[PinnedDialogButton alloc] init:i].view];
|
|
|
|
}
|
|
|
|
NSStackView *stackView = [NSStackView stackViewWithViews:[pins copy]];
|
|
|
|
[stackView setSpacing:-15];
|
|
|
|
item.view = stackView;
|
2019-05-01 16:18:31 +03:00
|
|
|
[self.touchbarItems[identifier] setObject:item.view forKey:@"view"];
|
|
|
|
return item;
|
2019-04-29 19:55:11 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil;
|
|
|
|
}
|
2019-04-29 16:41:51 +03:00
|
|
|
|
2019-04-29 19:55:11 +03:00
|
|
|
- (void)setTouchBar:(TouchBarType)type {
|
|
|
|
if (self.touchBarType == type) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
self.touchBarType = type;
|
|
|
|
if (type == TouchBarType::Main) {
|
2019-04-30 21:31:42 +03:00
|
|
|
[self.view setTouchBar:_touchBarMain];
|
2019-04-29 19:55:11 +03:00
|
|
|
} else if (type == TouchBarType::AudioPlayer) {
|
2019-04-30 21:31:42 +03:00
|
|
|
[self.view setTouchBar:_touchBarAudioPlayer];
|
2019-04-29 19:55:11 +03:00
|
|
|
}
|
2019-04-29 16:41:51 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)handlePropertyChange:(Media::Player::TrackState)property {
|
2019-04-30 21:31:42 +03:00
|
|
|
// #TODO: fix hiding of touch bar when last track is ended.
|
2019-04-29 19:55:11 +03:00
|
|
|
if (property.state == Media::Player::State::Stopped) {
|
|
|
|
[self setTouchBar:TouchBarType::Main];
|
|
|
|
return;
|
2019-04-30 21:31:42 +03:00
|
|
|
} else if (property.state == Media::Player::State::StoppedAtEnd) {
|
|
|
|
[self setTouchBar:TouchBarType::AudioPlayer];
|
2019-04-29 19:55:11 +03:00
|
|
|
} else {
|
|
|
|
[self setTouchBar:TouchBarType::AudioPlayer];
|
|
|
|
}
|
|
|
|
|
|
|
|
self.position = property.position < 0 ? 0 : property.position;
|
|
|
|
self.duration = property.length;
|
|
|
|
[self updateTouchBarTimeItems];
|
|
|
|
NSButton *playButton = self.touchbarItems[play][@"view"];
|
|
|
|
if (property.state == Media::Player::State::Playing) {
|
|
|
|
playButton.image = self.touchbarItems[play][@"image"];
|
|
|
|
} else {
|
|
|
|
playButton.image = self.touchbarItems[play][@"imageAlt"];
|
|
|
|
}
|
|
|
|
|
|
|
|
[self.touchbarItems[nextItem][@"view"]
|
|
|
|
setEnabled:Media::Player::instance()->nextAvailable(kSongType)];
|
|
|
|
[self.touchbarItems[previousItem][@"view"]
|
|
|
|
setEnabled:Media::Player::instance()->previousAvailable(kSongType)];
|
2019-04-29 16:41:51 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString *)formatTime:(int)time {
|
2019-04-29 19:55:11 +03:00
|
|
|
const int seconds = time % 60;
|
|
|
|
const int minutes = (time / 60) % 60;
|
|
|
|
const int hours = time / (60 * 60);
|
2019-04-29 16:41:51 +03:00
|
|
|
|
2019-04-29 19:55:11 +03:00
|
|
|
NSString *stime = hours > 0 ? [NSString stringWithFormat:@"%d:", hours] : @"";
|
|
|
|
stime = (stime.length > 0 || minutes > 9) ?
|
|
|
|
[NSString stringWithFormat:@"%@%02d:", stime, minutes] :
|
|
|
|
[NSString stringWithFormat:@"%d:", minutes];
|
|
|
|
stime = [NSString stringWithFormat:@"%@%02d", stime, seconds];
|
2019-04-29 16:41:51 +03:00
|
|
|
|
2019-04-29 19:55:11 +03:00
|
|
|
return stime;
|
2019-04-29 16:41:51 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)removeConstraintForIdentifier:(NSTouchBarItemIdentifier)identifier {
|
2019-04-29 19:55:11 +03:00
|
|
|
NSTextField *field = self.touchbarItems[identifier][@"view"];
|
|
|
|
[field removeConstraint:self.touchbarItems[identifier][@"constrain"]];
|
2019-04-29 16:41:51 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)applyConstraintFromString:(NSString *)string
|
2019-04-29 19:55:11 +03:00
|
|
|
forIdentifier:(NSTouchBarItemIdentifier)identifier {
|
|
|
|
NSTextField *field = self.touchbarItems[identifier][@"view"];
|
|
|
|
if (field) {
|
|
|
|
NSString *fString = [[string componentsSeparatedByCharactersInSet:
|
|
|
|
[NSCharacterSet decimalDigitCharacterSet]] componentsJoinedByString:@"0"];
|
|
|
|
NSTextField *textField = [NSTextField labelWithString:fString];
|
|
|
|
NSSize size = [textField frame].size;
|
2019-04-29 16:41:51 +03:00
|
|
|
|
2019-04-29 19:55:11 +03:00
|
|
|
NSLayoutConstraint *con =
|
|
|
|
[NSLayoutConstraint constraintWithItem:field
|
|
|
|
attribute:NSLayoutAttributeWidth
|
|
|
|
relatedBy:NSLayoutRelationEqual
|
|
|
|
toItem:nil
|
|
|
|
attribute:NSLayoutAttributeNotAnAttribute
|
|
|
|
multiplier:1.0
|
|
|
|
constant:(int)ceil(size.width * 1.5)];
|
|
|
|
[field addConstraint:con];
|
|
|
|
[self.touchbarItems[identifier] setObject:con forKey:@"constrain"];
|
|
|
|
}
|
2019-04-29 16:41:51 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)updateTouchBarTimeItemConstrains {
|
2019-04-29 19:55:11 +03:00
|
|
|
[self removeConstraintForIdentifier:currentPosition];
|
2019-04-29 16:41:51 +03:00
|
|
|
|
2019-04-29 19:55:11 +03:00
|
|
|
if (self.duration <= 0) {
|
|
|
|
[self applyConstraintFromString:[self formatTime:self.position]
|
|
|
|
forIdentifier:currentPosition];
|
|
|
|
} else {
|
|
|
|
NSString *durFormat = [self formatTime:self.duration];
|
|
|
|
[self applyConstraintFromString:durFormat forIdentifier:currentPosition];
|
|
|
|
}
|
2019-04-29 16:41:51 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)updateTouchBarTimeItems {
|
2019-04-29 19:55:11 +03:00
|
|
|
NSSlider *seekSlider = self.touchbarItems[seekBar][@"view"];
|
|
|
|
NSTextField *curPosItem = self.touchbarItems[currentPosition][@"view"];
|
2019-04-29 16:41:51 +03:00
|
|
|
|
2019-04-29 19:55:11 +03:00
|
|
|
if (self.duration <= 0) {
|
|
|
|
seekSlider.enabled = NO;
|
|
|
|
seekSlider.doubleValue = 0;
|
|
|
|
} else {
|
|
|
|
seekSlider.enabled = YES;
|
|
|
|
if (!seekSlider.highlighted) {
|
|
|
|
seekSlider.doubleValue = (self.position / self.duration) * seekSlider.maxValue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
const auto timeToString = [&](int t) {
|
|
|
|
return [self formatTime:(int)floor(t / kMs)];
|
|
|
|
};
|
|
|
|
curPosItem.stringValue = [NSString stringWithFormat:@"%@ / %@",
|
|
|
|
timeToString(self.position),
|
|
|
|
timeToString(self.duration)];
|
2019-04-29 16:41:51 +03:00
|
|
|
|
2019-04-29 19:55:11 +03:00
|
|
|
[self updateTouchBarTimeItemConstrains];
|
2019-04-29 16:41:51 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString *)getIdentifierFromView:(id)view {
|
2019-04-29 19:55:11 +03:00
|
|
|
NSString *identifier;
|
|
|
|
for (identifier in self.touchbarItems)
|
|
|
|
if([self.touchbarItems[identifier][@"view"] isEqual:view])
|
|
|
|
break;
|
|
|
|
return identifier;
|
2019-04-29 16:41:51 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)buttonAction:(NSButton *)sender {
|
2019-04-29 19:55:11 +03:00
|
|
|
NSString *identifier = [self getIdentifierFromView:sender];
|
|
|
|
const auto command = [self.touchbarItems[identifier][@"cmd"] intValue];
|
|
|
|
|
|
|
|
Core::Sandbox::Instance().customEnterFromEventLoop([=] {
|
2019-04-30 21:31:42 +03:00
|
|
|
if (command == kSavedMessages) {
|
|
|
|
App::main()->choosePeer(Auth().userPeerId(), ShowAtUnreadMsgId);
|
|
|
|
} else if (command == kPlayPause) {
|
2019-04-29 19:55:11 +03:00
|
|
|
Media::Player::instance()->playPause();
|
|
|
|
} else if (command == kPlaylistPrevious) {
|
|
|
|
Media::Player::instance()->previous();
|
|
|
|
} else if (command == kPlaylistNext) {
|
|
|
|
Media::Player::instance()->next();
|
2019-04-30 21:31:42 +03:00
|
|
|
} else if (command == kClosePlayer) {
|
|
|
|
App::main()->closeBothPlayers();
|
|
|
|
}
|
2019-04-29 19:55:11 +03:00
|
|
|
});
|
2019-04-29 16:41:51 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)seekbarChanged:(NSSliderTouchBarItem *)sender {
|
2019-04-29 19:55:11 +03:00
|
|
|
Core::Sandbox::Instance().customEnterFromEventLoop([&] {
|
|
|
|
Media::Player::instance()->finishSeeking(kSongType, sender.slider.doubleValue);
|
|
|
|
});
|
2019-04-29 16:41:51 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
@end
|