From c8088d801a99109b213a416da20a9e5d712ca492 Mon Sep 17 00:00:00 2001 From: der richter <der.richter@gmx.de> Date: Sun, 17 Mar 2024 17:12:09 +0100 Subject: [PATCH] mac/touchbar: rewrite touch bar in swift --- meson.build | 2 +- osdep/mac/application.m | 12 +- osdep/mac/touch_bar.swift | 343 ++++++++++++++++++++++++++++++++++++++ osdep/mac/touchbar.h | 44 ----- osdep/mac/touchbar.m | 342 ------------------------------------- 5 files changed, 345 insertions(+), 398 deletions(-) create mode 100644 osdep/mac/touch_bar.swift delete mode 100644 osdep/mac/touchbar.h delete mode 100644 osdep/mac/touchbar.m diff --git a/meson.build b/meson.build index 159d1feebc..40c5551fe0 100644 --- a/meson.build +++ b/meson.build @@ -1556,7 +1556,7 @@ macos_touchbar = get_option('macos-touchbar').require( ) features += {'macos-touchbar': macos_touchbar.allowed()} if features['macos-touchbar'] - sources += files('osdep/mac/touchbar.m') + swift_sources += files('osdep/mac/touch_bar.swift') endif if features['swift'] and swift_sources.length() > 0 diff --git a/osdep/mac/application.m b/osdep/mac/application.m index bd71b6042c..5eef3a9fa3 100644 --- a/osdep/mac/application.m +++ b/osdep/mac/application.m @@ -30,9 +30,6 @@ #include "osdep/threads.h" #include "osdep/main-fn.h" -#if HAVE_MACOS_TOUCHBAR -#import "osdep/mac/touchbar.h" -#endif #if HAVE_SWIFT #include "osdep/mac/swift.h" #endif @@ -161,14 +158,7 @@ static const char mac_icon[] = #if HAVE_MACOS_TOUCHBAR - (NSTouchBar *)makeTouchBar { - TouchBar *tBar = [[TouchBar alloc] init]; - tBar.delegate = tBar; - tBar.customizationIdentifier = customID; - tBar.defaultItemIdentifiers = @[play, previousItem, nextItem, seekBar]; - tBar.customizationAllowedItemIdentifiers = @[play, seekBar, previousItem, - nextItem, previousChapter, nextChapter, cycleAudio, cycleSubtitle, - currentPosition, timeLeft]; - return tBar; + return [[TouchBar alloc] init]; } #endif diff --git a/osdep/mac/touch_bar.swift b/osdep/mac/touch_bar.swift new file mode 100644 index 0000000000..dcebb24970 --- /dev/null +++ b/osdep/mac/touch_bar.swift @@ -0,0 +1,343 @@ +/* + * 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/>. + */ + +extension NSTouchBar.CustomizationIdentifier { + public static let customId: NSTouchBar.CustomizationIdentifier = "io.mpv.touchbar" +} + +extension NSTouchBarItem.Identifier { + public static let seekBar = NSTouchBarItem.Identifier(custom: ".seekbar") + public static let play = NSTouchBarItem.Identifier(custom: ".play") + public static let nextItem = NSTouchBarItem.Identifier(custom: ".nextItem") + public static let previousItem = NSTouchBarItem.Identifier(custom: ".previousItem") + public static let nextChapter = NSTouchBarItem.Identifier(custom: ".nextChapter") + public static let previousChapter = NSTouchBarItem.Identifier(custom: ".previousChapter") + public static let cycleAudio = NSTouchBarItem.Identifier(custom: ".cycleAudio") + public static let cycleSubtitle = NSTouchBarItem.Identifier(custom: ".cycleSubtitle") + public static let currentPosition = NSTouchBarItem.Identifier(custom: ".currentPosition") + public static let timeLeft = NSTouchBarItem.Identifier(custom: ".timeLeft") + + init(custom: String) { + self.init(NSTouchBar.CustomizationIdentifier.customId + custom) + } +} + +extension TouchBar { + enum `Type` { + case button + case text + case slider + } + + struct Config { + let name: String + let type: Type + let command: String + var view: NSView? + var item: NSCustomTouchBarItem? + var constraint: NSLayoutConstraint? + let image: NSImage + let imageAlt: NSImage + + init( + name: String = "", + type: Type = .button, + command: String = "", + view: NSView? = nil, + item: NSCustomTouchBarItem? = nil, + constraint: NSLayoutConstraint? = nil, + image: NSImage? = nil, + imageAlt: NSImage? = nil + ) { + self.name = name + self.type = type + self.command = command + self.view = view + self.item = item + self.constraint = constraint + self.image = image ?? NSImage(size: NSSize(width: 1, height: 1)) + self.imageAlt = imageAlt ?? NSImage(size: NSSize(width: 1, height: 1)) + } + } +} + +class TouchBar: NSTouchBar, NSTouchBarDelegate { + var configs: [NSTouchBarItem.Identifier:Config] = [:] + var isPaused: Bool = false + var position: Double = 0 + var duration: Double = 0 + + override init() { + super.init() + + configs = [ + .seekBar: Config(name: "Seek Bar", type: .slider, command: "seek %f absolute-percent"), + .currentPosition: Config(name: "Current Position", type: .text), + .timeLeft: Config(name: "Time Left", type: .text), + .play: Config( + name: "Play Button", + type: .button, + command: "cycle pause", + image: .init(named: NSImage.touchBarPauseTemplateName), + imageAlt: .init(named: NSImage.touchBarPlayTemplateName) + ), + .previousItem: Config( + name: "Previous Playlist Item", + type: .button, + command: "playlist-prev", + image: .init(named: NSImage.touchBarGoBackTemplateName) + ), + .nextItem: Config( + name: "Next Playlist Item", + type: .button, + command: "playlist-next", + image: .init(named: NSImage.touchBarGoForwardTemplateName) + ), + .previousChapter: Config( + name: "Previous Chapter", + type: .button, + command: "add chapter -1", + image: .init(named: NSImage.touchBarSkipBackTemplateName) + ), + .nextChapter: Config( + name: "Next Chapter", + type: .button, + command: "add chapter 1", + image: .init(named: NSImage.touchBarSkipAheadTemplateName) + ), + .cycleAudio: Config( + name: "Cycle Audio", + type: .button, + command: "cycle audio", + image: .init(named: NSImage.touchBarAudioInputTemplateName) + ), + .cycleSubtitle: Config( + name: "Cycle Subtitle", + type: .button, + command: "cycle sub", + image: .init(named: NSImage.touchBarComposeTemplateName) + ) + ] + + delegate = self + customizationIdentifier = .customId; + defaultItemIdentifiers = [.play, .previousItem, .nextItem, .seekBar] + customizationAllowedItemIdentifiers = [.play, .seekBar, .previousItem, .nextItem, + .previousChapter, .nextChapter, .cycleAudio, .cycleSubtitle, .currentPosition, .timeLeft] + addObserver(self, forKeyPath: "visible", options: [.new], context: nil) + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + } + + func touchBar(_ touchBar: NSTouchBar, makeItemForIdentifier identifier: NSTouchBarItem.Identifier) -> NSTouchBarItem? { + guard let config = configs[identifier] else { return nil } + + switch config.type { + case .button: + let item = NSCustomTouchBarItem(identifier: identifier) + let image = config.image + let button = NSButton(image: image, target: self, action: #selector(buttonAction(_:))) + item.view = button; + item.customizationLabel = config.name + configs[identifier]?.view = button + configs[identifier]?.item = item + item.addObserver(self, forKeyPath: "visible", options: [.new], context: nil) + return item + case .text: + let item = NSCustomTouchBarItem(identifier: identifier) + let text = NSTextField(labelWithString: "0:00") + text.alignment = .center + item.view = text; + item.customizationLabel = config.name + configs[identifier]?.view = text + configs[identifier]?.item = item + item.addObserver(self, forKeyPath: "visible", options: [.new], context: nil) + return item + case .slider: + let item = NSCustomTouchBarItem(identifier: identifier) + let slider = NSSlider(target: self, action: #selector(seekbarChanged(_:))) + slider.minValue = 0 + slider.maxValue = 100 + item.view = slider; + item.customizationLabel = config.name + configs[identifier]?.view = slider + configs[identifier]?.item = item + item.addObserver(self, forKeyPath: "visible", options: [.new], context: nil) + return item + } + } + + override func observeValue( + forKeyPath keyPath: String?, + of object: Any?, + change: [NSKeyValueChangeKey:Any]?, + context: UnsafeMutableRawPointer? + ) { + guard let visible = change?[.newKey] as? Bool else { return } + if keyPath == "isVisible" && visible { + updateTouchBarTimeItems() + updatePlayButton() + } + } + + func updateTouchBarTimeItems() { + if !isVisible { return } + updateSlider() + updateTimeLeft() + updateCurrentPosition() + } + + func updateSlider() { + guard let config = configs[.seekBar], let slider = config.view as? NSSlider else { return } + if !(config.item?.isVisible ?? false) { return } + + if duration <= 0 { + slider.isEnabled = false + slider.doubleValue = 0 + } else { + slider.isEnabled = true + if (!slider.isHighlighted) { + slider.doubleValue = (position / duration) * 100 + } + } + } + + func updateTimeLeft() { + guard let config = configs[.timeLeft], let text = config.view as? NSTextField else { return } + if !(config.item?.isVisible ?? false) { return } + + removeConstraintFor(identifier: .timeLeft) + if duration <= 0 { + text.stringValue = "" + } else { + let left = Int(floor(duration) - floor(position)) + let leftFormat = format(time: left) + let durationFormat = format(time: Int(duration)) + text.stringValue = "-\(leftFormat)" + applyConstraintFrom(string: "-\(durationFormat)", identifier: .timeLeft) + } + } + + func updateCurrentPosition() { + guard let config = configs[.currentPosition], let text = config.view as? NSTextField else { return } + if !(config.item?.isVisible ?? false) { return } + + text.stringValue = format(time: Int(floor(position))) + removeConstraintFor(identifier: .currentPosition) + if duration <= 0 { + applyConstraintFrom(string: format(time: Int(position)), identifier: .currentPosition) + } else { + applyConstraintFrom(string: format(time: Int(duration)), identifier: .currentPosition) + } + } + + func updatePlayButton() { + guard let config = configs[.play], let button = config.view as? NSButton else { return } + if !isVisible || !(config.item?.isVisible ?? false) { return } + + if isPaused { + button.image = configs[.play]?.imageAlt + } else { + button.image = configs[.play]?.image + } + } + + @objc func buttonAction(_ button: NSButton) { + guard let identifier = getIdentifierFrom(view: button), let command = configs[identifier]?.command else { return } + EventsResponder.sharedInstance().inputHelper.command(command) + } + + @objc func seekbarChanged(_ slider: NSSlider) { + guard let identifier = getIdentifierFrom(view: slider), let command = configs[identifier]?.command else { return } + EventsResponder.sharedInstance().inputHelper.command(String(format: command, slider.doubleValue)) + } + + func format(time: Int) -> String { + let seconds = time % 60 + let minutes = (time / 60) % 60 + let hours = time / (60 * 60) + + var stime = hours > 0 ? "\(hours):" : "" + stime = (stime.count > 0 || minutes > 9) ? String(format: "%@%02d:", stime, minutes) : "\(minutes):" + stime = String(format: "%@%02d", stime, seconds) + + return stime + } + + func removeConstraintFor(identifier: NSTouchBarItem.Identifier) { + guard let text = configs[identifier]?.view as? NSTextField, + let constraint = configs[identifier]?.constraint as? NSLayoutConstraint else { return } + text.removeConstraint(constraint) + } + + func applyConstraintFrom(string: String, identifier: NSTouchBarItem.Identifier) { + guard let text = configs[identifier]?.view as? NSTextField else { return } + let fString = string.components(separatedBy: .decimalDigits).joined(separator: "0") + let textField = NSTextField(labelWithString: fString) + let size = textField.frame.size + + let con = NSLayoutConstraint(item: text, attribute: .width, relatedBy: .equal, toItem: nil, + attribute: .notAnAttribute, multiplier: 1.0, constant: ceil(size.width * 1.1)) + text.addConstraint(con) + configs[identifier]?.constraint = con + } + + func getIdentifierFrom(view: NSView) -> NSTouchBarItem.Identifier? { + for (identifier, config) in configs { + if config.view == view { + return identifier + } + } + return nil + } + + @objc func processEvent(_ event: UnsafeMutablePointer<mpv_event>) { + switch event.pointee.event_id { + case MPV_EVENT_END_FILE: + position = 0 + duration = 0 + case MPV_EVENT_PROPERTY_CHANGE: + handlePropertyChange(event) + default: + break + } + } + + func handlePropertyChange(_ event: UnsafeMutablePointer<mpv_event>) { + let pData = OpaquePointer(event.pointee.data) + guard let property = UnsafePointer<mpv_event_property>(pData)?.pointee else { return } + + switch String(cString: property.name) { + case "time-pos" where property.format == MPV_FORMAT_DOUBLE: + let newPosition = max(LibmpvHelper.mpvDoubleToDouble(property.data) ?? 0, 0) + if Int(floor(newPosition) - floor(position)) != 0 { + position = newPosition + updateTouchBarTimeItems() + } + case "duration" where property.format == MPV_FORMAT_DOUBLE: + duration = LibmpvHelper.mpvDoubleToDouble(property.data) ?? 0 + updateTouchBarTimeItems() + case "pause" where property.format == MPV_FORMAT_FLAG: + isPaused = LibmpvHelper.mpvFlagToBool(property.data) ?? false + updatePlayButton() + default: + break + } + } +} diff --git a/osdep/mac/touchbar.h b/osdep/mac/touchbar.h deleted file mode 100644 index 4cd711ffeb..0000000000 --- a/osdep/mac/touchbar.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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/>. - */ - -#import <Cocoa/Cocoa.h> - -#define BASE_ID @"io.mpv.touchbar" -static NSTouchBarCustomizationIdentifier customID = BASE_ID; -static NSTouchBarItemIdentifier seekBar = BASE_ID ".seekbar"; -static NSTouchBarItemIdentifier play = BASE_ID ".play"; -static NSTouchBarItemIdentifier nextItem = BASE_ID ".nextItem"; -static NSTouchBarItemIdentifier previousItem = BASE_ID ".previousItem"; -static NSTouchBarItemIdentifier nextChapter = BASE_ID ".nextChapter"; -static NSTouchBarItemIdentifier previousChapter = BASE_ID ".previousChapter"; -static NSTouchBarItemIdentifier cycleAudio = BASE_ID ".cycleAudio"; -static NSTouchBarItemIdentifier cycleSubtitle = BASE_ID ".cycleSubtitle"; -static NSTouchBarItemIdentifier currentPosition = BASE_ID ".currentPosition"; -static NSTouchBarItemIdentifier timeLeft = BASE_ID ".timeLeft"; - -struct mpv_event; - -@interface TouchBar : NSTouchBar <NSTouchBarDelegate> - --(void)processEvent:(struct mpv_event *)event; - -@property(nonatomic, retain) NSDictionary *touchbarItems; -@property(nonatomic, assign) double duration; -@property(nonatomic, assign) double position; -@property(nonatomic, assign) int pause; - -@end diff --git a/osdep/mac/touchbar.m b/osdep/mac/touchbar.m deleted file mode 100644 index 18fe896428..0000000000 --- a/osdep/mac/touchbar.m +++ /dev/null @@ -1,342 +0,0 @@ -/* - * 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 "osdep/mac/touchbar.h" -#import "osdep/mac/events_objc.h" - -#include "config.h" - -#if HAVE_SWIFT -#include "osdep/mac/swift.h" -#endif - -@implementation TouchBar - -@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]; - [[EventsResponder sharedInstance].inputHelper command:self.touchbarItems[identifier][@"cmd"]]; -} - -- (void)seekbarChanged:(NSSlider *)slider -{ - NSString *identifier = [self getIdentifierFromView:slider]; - NSString *seek = [NSString stringWithFormat: - self.touchbarItems[identifier][@"cmd"], slider.doubleValue]; - [[EventsResponder sharedInstance].inputHelper command:seek]; -} - -- (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; - } - default: - 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