0
0
mirror of https://github.com/mpv-player/mpv.git synced 2024-09-20 20:03:10 +02:00
mpv/osdep/macosx_touchbar.m
der richter 7f8d069087 mac: only update touch bar items when necessary
the slider on the touch bar was always updated when any of the related
properties changed their value. this is partially dependent on the
refresh rate of the video, in the case of time-pos. too many updates to
touch bar impact the render performance.

to prevent this we only update the slider when necessary, when the touch
bar or the touch bar item is visible. the touch bar items only need a
granularity of seconds without any decimals, but the time-pos property
provides a granularity with decimals. we floor those values and only
update the touch bar items when we have at least a 1 second difference.
we also check for the visibility of the touch bar and its items.

Fixes #8477
2021-02-13 15:51:32 +01:00

335 lines
12 KiB
Objective-C

/*
* This file is part of mpv.
*
* mpv is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* mpv is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include "player/client.h"
#import "macosx_touchbar.h"
@implementation TouchBar
@synthesize app = _app;
@synthesize touchbarItems = _touchbar_items;
@synthesize duration = _duration;
@synthesize position = _position;
@synthesize pause = _pause;
- (id)init
{
if (self = [super init]) {
self.touchbarItems = @{
seekBar: [NSMutableDictionary dictionaryWithDictionary:@{
@"type": @"slider",
@"name": @"Seek Bar",
@"cmd": @"seek %f absolute-percent"
}],
play: [NSMutableDictionary dictionaryWithDictionary:@{
@"type": @"button",
@"name": @"Play Button",
@"cmd": @"cycle pause",
@"image": [NSImage imageNamed:NSImageNameTouchBarPauseTemplate],
@"imageAlt": [NSImage imageNamed:NSImageNameTouchBarPlayTemplate]
}],
previousItem: [NSMutableDictionary dictionaryWithDictionary:@{
@"type": @"button",
@"name": @"Previous Playlist Item",
@"cmd": @"playlist-prev",
@"image": [NSImage imageNamed:NSImageNameTouchBarGoBackTemplate]
}],
nextItem: [NSMutableDictionary dictionaryWithDictionary:@{
@"type": @"button",
@"name": @"Next Playlist Item",
@"cmd": @"playlist-next",
@"image": [NSImage imageNamed:NSImageNameTouchBarGoForwardTemplate]
}],
previousChapter: [NSMutableDictionary dictionaryWithDictionary:@{
@"type": @"button",
@"name": @"Previous Chapter",
@"cmd": @"add chapter -1",
@"image": [NSImage imageNamed:NSImageNameTouchBarSkipBackTemplate]
}],
nextChapter: [NSMutableDictionary dictionaryWithDictionary:@{
@"type": @"button",
@"name": @"Next Chapter",
@"cmd": @"add chapter 1",
@"image": [NSImage imageNamed:NSImageNameTouchBarSkipAheadTemplate]
}],
cycleAudio: [NSMutableDictionary dictionaryWithDictionary:@{
@"type": @"button",
@"name": @"Cycle Audio",
@"cmd": @"cycle audio",
@"image": [NSImage imageNamed:NSImageNameTouchBarAudioInputTemplate]
}],
cycleSubtitle: [NSMutableDictionary dictionaryWithDictionary:@{
@"type": @"button",
@"name": @"Cycle Subtitle",
@"cmd": @"cycle sub",
@"image": [NSImage imageNamed:NSImageNameTouchBarComposeTemplate]
}],
currentPosition: [NSMutableDictionary dictionaryWithDictionary:@{
@"type": @"text",
@"name": @"Current Position"
}],
timeLeft: [NSMutableDictionary dictionaryWithDictionary:@{
@"type": @"text",
@"name": @"Time Left"
}]
};
[self addObserver:self forKeyPath:@"visible" options:0 context:nil];
}
return self;
}
- (nullable NSTouchBarItem *)touchBar:(NSTouchBar *)touchBar
makeItemForIdentifier:(NSTouchBarItemIdentifier)identifier
{
if ([self.touchbarItems[identifier][@"type"] isEqualToString:@"slider"]) {
NSCustomTouchBarItem *tbItem = [[NSCustomTouchBarItem alloc] initWithIdentifier:identifier];
NSSlider *slider = [NSSlider sliderWithTarget:self action:@selector(seekbarChanged:)];
slider.minValue = 0.0f;
slider.maxValue = 100.0f;
tbItem.view = slider;
tbItem.customizationLabel = self.touchbarItems[identifier][@"name"];
[self.touchbarItems[identifier] setObject:slider forKey:@"view"];
[self.touchbarItems[identifier] setObject:tbItem forKey:@"tbItem"];
[tbItem addObserver:self forKeyPath:@"visible" options:0 context:nil];
return tbItem;
} else if ([self.touchbarItems[identifier][@"type"] isEqualToString:@"button"]) {
NSCustomTouchBarItem *tbItem = [[NSCustomTouchBarItem alloc] initWithIdentifier:identifier];
NSImage *tbImage = self.touchbarItems[identifier][@"image"];
NSButton *tbButton = [NSButton buttonWithImage:tbImage target:self action:@selector(buttonAction:)];
tbItem.view = tbButton;
tbItem.customizationLabel = self.touchbarItems[identifier][@"name"];
[self.touchbarItems[identifier] setObject:tbButton forKey:@"view"];
[self.touchbarItems[identifier] setObject:tbItem forKey:@"tbItem"];
[tbItem addObserver:self forKeyPath:@"visible" options:0 context:nil];
return tbItem;
} else if ([self.touchbarItems[identifier][@"type"] isEqualToString:@"text"]) {
NSCustomTouchBarItem *tbItem = [[NSCustomTouchBarItem alloc] initWithIdentifier:identifier];
NSTextField *tbText = [NSTextField labelWithString:@"0:00"];
tbText.alignment = NSTextAlignmentCenter;
tbItem.view = tbText;
tbItem.customizationLabel = self.touchbarItems[identifier][@"name"];
[self.touchbarItems[identifier] setObject:tbText forKey:@"view"];
[self.touchbarItems[identifier] setObject:tbItem forKey:@"tbItem"];
[tbItem addObserver:self forKeyPath:@"visible" options:0 context:nil];
return tbItem;
}
return nil;
}
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary<NSKeyValueChangeKey,id> *)change
context:(void *)context {
if ([keyPath isEqualToString:@"visible"]) {
NSNumber *visible = [object valueForKey:@"visible"];
if (visible.boolValue) {
[self updateTouchBarTimeItems];
[self updatePlayButton];
}
}
}
- (void)updateTouchBarTimeItems
{
if (!self.isVisible)
return;
[self updateSlider];
[self updateTimeLeft];
[self updateCurrentPosition];
}
- (void)updateSlider
{
NSCustomTouchBarItem *tbItem = self.touchbarItems[seekBar][@"tbItem"];
if (!tbItem.visible)
return;
NSSlider *seekSlider = self.touchbarItems[seekBar][@"view"];
if (self.duration <= 0) {
seekSlider.enabled = NO;
seekSlider.doubleValue = 0;
} else {
seekSlider.enabled = YES;
if (!seekSlider.highlighted)
seekSlider.doubleValue = (self.position/self.duration)*100;
}
}
- (void)updateTimeLeft
{
NSCustomTouchBarItem *tbItem = self.touchbarItems[timeLeft][@"tbItem"];
if (!tbItem.visible)
return;
NSTextField *timeLeftItem = self.touchbarItems[timeLeft][@"view"];
[self removeConstraintForIdentifier:timeLeft];
if (self.duration <= 0) {
timeLeftItem.stringValue = @"";
} else {
int left = (int)(floor(self.duration)-floor(self.position));
NSString *leftFormat = [self formatTime:left];
NSString *durFormat = [self formatTime:self.duration];
timeLeftItem.stringValue = [NSString stringWithFormat:@"-%@", leftFormat];
[self applyConstraintFromString:[NSString stringWithFormat:@"-%@", durFormat]
forIdentifier:timeLeft];
}
}
- (void)updateCurrentPosition
{
NSCustomTouchBarItem *tbItem = self.touchbarItems[currentPosition][@"tbItem"];
if (!tbItem.visible)
return;
NSTextField *curPosItem = self.touchbarItems[currentPosition][@"view"];
NSString *posFormat = [self formatTime:(int)floor(self.position)];
curPosItem.stringValue = posFormat;
[self removeConstraintForIdentifier:currentPosition];
if (self.duration <= 0) {
[self applyConstraintFromString:[self formatTime:self.position]
forIdentifier:currentPosition];
} else {
NSString *durFormat = [self formatTime:self.duration];
[self applyConstraintFromString:durFormat forIdentifier:currentPosition];
}
}
- (void)updatePlayButton
{
NSCustomTouchBarItem *tbItem = self.touchbarItems[play][@"tbItem"];
if (!self.isVisible || !tbItem.visible)
return;
NSButton *playButton = self.touchbarItems[play][@"view"];
if (self.pause) {
playButton.image = self.touchbarItems[play][@"imageAlt"];
} else {
playButton.image = self.touchbarItems[play][@"image"];
}
}
- (void)buttonAction:(NSButton *)sender
{
NSString *identifier = [self getIdentifierFromView:sender];
[self.app queueCommand:(char *)[self.touchbarItems[identifier][@"cmd"] UTF8String]];
}
- (void)seekbarChanged:(NSSlider *)slider
{
NSString *identifier = [self getIdentifierFromView:slider];
NSString *seek = [NSString stringWithFormat:
self.touchbarItems[identifier][@"cmd"], slider.doubleValue];
[self.app queueCommand:(char *)[seek UTF8String]];
}
- (NSString *)formatTime:(int)time
{
int seconds = time % 60;
int minutes = (time / 60) % 60;
int hours = time / (60 * 60);
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];
return stime;
}
- (void)removeConstraintForIdentifier:(NSTouchBarItemIdentifier)identifier
{
NSTextField *field = self.touchbarItems[identifier][@"view"];
[field removeConstraint:self.touchbarItems[identifier][@"constrain"]];
}
- (void)applyConstraintFromString:(NSString *)string
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;
NSLayoutConstraint *con =
[NSLayoutConstraint constraintWithItem:field
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1.0
constant:(int)ceil(size.width*1.1)];
[field addConstraint:con];
[self.touchbarItems[identifier] setObject:con forKey:@"constrain"];
}
}
- (NSString *)getIdentifierFromView:(id)view
{
NSString *identifier;
for (identifier in self.touchbarItems)
if([self.touchbarItems[identifier][@"view"] isEqual:view])
break;
return identifier;
}
- (void)processEvent:(struct mpv_event *)event
{
switch (event->event_id) {
case MPV_EVENT_END_FILE: {
self.position = 0;
self.duration = 0;
break;
}
case MPV_EVENT_PROPERTY_CHANGE: {
[self handlePropertyChange:(mpv_event_property *)event->data];
break;
}
}
}
- (void)handlePropertyChange:(struct mpv_event_property *)property
{
NSString *name = [NSString stringWithUTF8String:property->name];
mpv_format format = property->format;
if ([name isEqualToString:@"time-pos"] && format == MPV_FORMAT_DOUBLE) {
double newPosition = *(double *)property->data;
newPosition = newPosition < 0 ? 0 : newPosition;
if ((int)(floor(newPosition) - floor(self.position)) != 0) {
self.position = newPosition;
[self updateTouchBarTimeItems];
}
} else if ([name isEqualToString:@"duration"] && format == MPV_FORMAT_DOUBLE) {
self.duration = *(double *)property->data;
[self updateTouchBarTimeItems];
} else if ([name isEqualToString:@"pause"] && format == MPV_FORMAT_FLAG) {
self.pause = *(int *)property->data;
[self updatePlayButton];
}
}
@end