diff options
author | Damiano Galassi <[email protected]> | 2018-10-10 19:58:21 +0200 |
---|---|---|
committer | Damiano Galassi <[email protected]> | 2018-10-10 19:58:21 +0200 |
commit | 2264d25308238210892840d7de3fa1edf9de105e (patch) | |
tree | 0285fd80ba9717f50219a45b8891d072f030c77f /macosx | |
parent | 210164589bd29a2b21cfd58a928aa11e603782eb (diff) |
MacGui: add touch bars in the preview window, improve touch bars in the main and queue windows.
Diffstat (limited to 'macosx')
-rw-r--r-- | macosx/Base.lproj/HBEncodingProgressHUDController.xib | 7 | ||||
-rw-r--r-- | macosx/HBController.h | 1 | ||||
-rw-r--r-- | macosx/HBController.m | 55 | ||||
-rw-r--r-- | macosx/HBEncodingProgressHUDController.m | 38 | ||||
-rw-r--r-- | macosx/HBImageUtilities.h | 4 | ||||
-rw-r--r-- | macosx/HBImageUtilities.m | 53 | ||||
-rw-r--r-- | macosx/HBPictureHUDController.h | 6 | ||||
-rw-r--r-- | macosx/HBPictureHUDController.m | 227 | ||||
-rw-r--r-- | macosx/HBPlayerHUDController.m | 201 | ||||
-rw-r--r-- | macosx/HBPreviewController.m | 7 | ||||
-rw-r--r-- | macosx/HBPreviewGenerator.h | 18 | ||||
-rw-r--r-- | macosx/HBPreviewGenerator.m | 82 | ||||
-rw-r--r-- | macosx/HBQueueController.m | 23 | ||||
-rw-r--r-- | macosx/HBThumbnailItemView.h | 19 | ||||
-rw-r--r-- | macosx/HBThumbnailItemView.m | 90 | ||||
-rw-r--r-- | macosx/HandBrake.xcodeproj/project.pbxproj | 8 | ||||
-rw-r--r-- | macosx/HandBrakeKit/HandBrakeKit.h | 1 |
17 files changed, 736 insertions, 104 deletions
diff --git a/macosx/Base.lproj/HBEncodingProgressHUDController.xib b/macosx/Base.lproj/HBEncodingProgressHUDController.xib index 92885ea8f..b28478654 100644 --- a/macosx/Base.lproj/HBEncodingProgressHUDController.xib +++ b/macosx/Base.lproj/HBEncodingProgressHUDController.xib @@ -1,8 +1,8 @@ <?xml version="1.0" encoding="UTF-8"?> -<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14269.12" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES"> +<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14460.23.1" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES"> <dependencies> <deployment identifier="macosx"/> - <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14269.12"/> + <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14460.23.1"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> </dependencies> <objects> @@ -27,6 +27,9 @@ <buttonCell key="cell" type="push" title="Cancel" bezelStyle="rounded" alignment="center" controlSize="mini" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="Ha0-iE-RLa"> <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/> <font key="font" metaFont="miniSystem"/> + <string key="keyEquivalent" base64-UTF8="YES"> +Gw +</string> </buttonCell> <connections> <action selector="cancelEncoding:" target="-2" id="XDk-r6-Ihc"/> diff --git a/macosx/HBController.h b/macosx/HBController.h index c3a1eba68..75494f1a0 100644 --- a/macosx/HBController.h +++ b/macosx/HBController.h @@ -28,7 +28,6 @@ - (IBAction)addToQueue:(id)sender; - (IBAction)addAllTitlesToQueue:(id)sender; -- (void)setQueueState:(NSUInteger)count; - (void)setQueueInfo:(NSString *)info progress:(double)progress hidden:(BOOL)hidden; - (IBAction)rip:(id)sender; diff --git a/macosx/HBController.m b/macosx/HBController.m index 62330608d..c4a0c70e5 100644 --- a/macosx/HBController.m +++ b/macosx/HBController.m @@ -147,9 +147,9 @@ static void *HBControllerQueueCoreContext = &HBControllerQueueCoreContext; @end @interface HBController (TouchBar) <NSTouchBarProvider, NSTouchBarDelegate> -- (void)updateButtonsStateForScanCore:(HBState)state; -- (void)updateButtonsStateForQueueCore:(HBState)state; -- (void)validateTouchBarsItems; +- (void)_touchBar_updateButtonsStateForScanCore:(HBState)state; +- (void)_touchBar_updateButtonsStateForQueueCore:(HBState)state; +- (void)_touchBar_validateUserInterfaceItems; @end #define WINDOW_HEIGHT_OFFSET_INIT 48 @@ -362,8 +362,8 @@ static void *HBControllerQueueCoreContext = &HBControllerQueueCoreContext; [self updateToolbarButtonsStateForScanCore:state]; if (@available(macOS 10.12.2, *)) { - [self updateButtonsStateForScanCore:state]; - [self validateTouchBarsItems]; + [self _touchBar_updateButtonsStateForScanCore:state]; + [self _touchBar_validateUserInterfaceItems]; } } else if (context == HBControllerQueueCoreContext) @@ -373,9 +373,11 @@ static void *HBControllerQueueCoreContext = &HBControllerQueueCoreContext; [self.window.toolbar validateVisibleItems]; if (@available(macOS 10.12.2, *)) { - [self updateButtonsStateForQueueCore:state]; - [self validateTouchBarsItems]; + [self _touchBar_updateButtonsStateForQueueCore:state]; + [self _touchBar_validateUserInterfaceItems]; } + NSUInteger count = self.queue.pendingItemsCount; + self.showQueueToolbarItem.badgeValue = count ? @(count).stringValue : nil; } else { @@ -697,7 +699,7 @@ static void *HBControllerQueueCoreContext = &HBControllerQueueCoreContext; [self.window.toolbar validateVisibleItems]; if (@available(macOS 10.12.2, *)) { - [self validateTouchBarsItems]; + [self _touchBar_validateUserInterfaceItems]; } }]; } @@ -1069,11 +1071,6 @@ static void *HBControllerQueueCoreContext = &HBControllerQueueCoreContext; fRipIndicator.doubleValue = self.progress; } -- (void)setQueueState:(NSUInteger)count -{ - self.showQueueToolbarItem.badgeValue = count ? @(count).stringValue : nil; -} - - (void)setQueueInfo:(NSString *)info progress:(double)progress hidden:(BOOL)hidden { self.progressInfo = info; @@ -1541,6 +1538,8 @@ static void *HBControllerQueueCoreContext = &HBControllerQueueCoreContext; @implementation HBController (TouchBar) +@dynamic touchBar; + static NSTouchBarItemIdentifier HBTouchBarMain = @"fr.handbrake.mainWindowTouchBar"; static NSTouchBarItemIdentifier HBTouchBarOpen = @"fr.handbrake.openSource"; @@ -1557,7 +1556,7 @@ static NSTouchBarItemIdentifier HBTouchBarPreview = @"fr.handbrake.preview"; bar.defaultItemIdentifiers = @[HBTouchBarOpen, NSTouchBarItemIdentifierFixedSpaceSmall, HBTouchBarAddToQueue, NSTouchBarItemIdentifierFixedSpaceLarge, HBTouchBarRip, HBTouchBarPause, NSTouchBarItemIdentifierFixedSpaceLarge, HBTouchBarPreview]; bar.customizationIdentifier = HBTouchBarMain; - bar.customizationAllowedItemIdentifiers = @[HBTouchBarOpen, HBTouchBarAddToQueue, HBTouchBarRip, HBTouchBarPause, HBTouchBarPreview]; + bar.customizationAllowedItemIdentifiers = @[HBTouchBarOpen, HBTouchBarAddToQueue, HBTouchBarRip, HBTouchBarPause, HBTouchBarPreview, NSTouchBarItemIdentifierFixedSpaceSmall, NSTouchBarItemIdentifierFixedSpaceLarge, NSTouchBarItemIdentifierFlexibleSpace]; return bar; } @@ -1577,9 +1576,9 @@ static NSTouchBarItemIdentifier HBTouchBarPreview = @"fr.handbrake.preview"; else if ([identifier isEqualTo:HBTouchBarAddToQueue]) { NSCustomTouchBarItem *item = [[NSCustomTouchBarItem alloc] initWithIdentifier:identifier]; - item.customizationLabel = NSLocalizedString(@"Add to Queue", @"Touch bar"); + item.customizationLabel = NSLocalizedString(@"Add To Queue", @"Touch bar"); - NSButton *button = [NSButton buttonWithTitle:NSLocalizedString(@"Add to Queue", @"Touch bar") target:self action:@selector(addToQueue:)]; + NSButton *button = [NSButton buttonWithTitle:NSLocalizedString(@"Add To Queue", @"Touch bar") target:self action:@selector(addToQueue:)]; item.view = button; return item; @@ -1587,7 +1586,7 @@ static NSTouchBarItemIdentifier HBTouchBarPreview = @"fr.handbrake.preview"; else if ([identifier isEqualTo:HBTouchBarRip]) { NSCustomTouchBarItem *item = [[NSCustomTouchBarItem alloc] initWithIdentifier:identifier]; - item.customizationLabel = NSLocalizedString(@"Rip", @"Touch bar"); + item.customizationLabel = NSLocalizedString(@"Start/Stop Encoding", @"Touch bar"); NSButton *button = [NSButton buttonWithImage:[NSImage imageNamed:NSImageNameTouchBarPlayTemplate] target:self action:@selector(rip:)]; @@ -1597,7 +1596,7 @@ static NSTouchBarItemIdentifier HBTouchBarPreview = @"fr.handbrake.preview"; else if ([identifier isEqualTo:HBTouchBarPause]) { NSCustomTouchBarItem *item = [[NSCustomTouchBarItem alloc] initWithIdentifier:identifier]; - item.customizationLabel = NSLocalizedString(@"Pause", @"Touch bar"); + item.customizationLabel = NSLocalizedString(@"Pause Encoding", @"Touch bar"); NSButton *button = [NSButton buttonWithImage:[NSImage imageNamed:NSImageNameTouchBarPauseTemplate] target:self action:@selector(pause:)]; @@ -1607,7 +1606,7 @@ static NSTouchBarItemIdentifier HBTouchBarPreview = @"fr.handbrake.preview"; else if ([identifier isEqualTo:HBTouchBarPreview]) { NSCustomTouchBarItem *item = [[NSCustomTouchBarItem alloc] initWithIdentifier:identifier]; - item.customizationLabel = NSLocalizedString(@"Show Preview", @"Touch bar"); + item.customizationLabel = NSLocalizedString(@"Show Preview Window", @"Touch bar"); NSButton *button = [NSButton buttonWithImage:[NSImage imageNamed:NSImageNameTouchBarQuickLookTemplate] target:self action:@selector(showPreviewWindow:)]; @@ -1618,27 +1617,23 @@ static NSTouchBarItemIdentifier HBTouchBarPreview = @"fr.handbrake.preview"; return nil; } -- (void)updateButtonsStateForScanCore:(HBState)state +- (void)_touchBar_updateButtonsStateForScanCore:(HBState)state { NSButton *openButton = (NSButton *)[[self.touchBar itemForIdentifier:HBTouchBarOpen] view]; - NSButton *addToQueueButton = (NSButton *)[[self.touchBar itemForIdentifier:HBTouchBarAddToQueue] view]; - NSButton *previewButton = (NSButton *)[[self.touchBar itemForIdentifier:HBTouchBarPreview] view]; - if (state == HBStateIdle) { openButton.title = NSLocalizedString(@"Open Source", @"Touch bar"); - addToQueueButton.enabled = NO; - previewButton.enabled = NO; + openButton.bezelColor = nil; } else { - openButton.title = NSLocalizedString(@"Cancel scan", @"Touch bar"); - addToQueueButton.enabled = YES; + openButton.title = NSLocalizedString(@"Cancel Scan", @"Touch bar"); + openButton.bezelColor = [NSColor systemRedColor]; } } -- (void)updateButtonsStateForQueueCore:(HBState)state; +- (void)_touchBar_updateButtonsStateForQueueCore:(HBState)state; { NSButton *ripButton = (NSButton *)[[self.touchBar itemForIdentifier:HBTouchBarRip] view]; NSButton *pauseButton = (NSButton *)[[self.touchBar itemForIdentifier:HBTouchBarPause] view]; @@ -1660,7 +1655,7 @@ static NSTouchBarItemIdentifier HBTouchBarPreview = @"fr.handbrake.preview"; } } -- (void)validateTouchBarsItems +- (void)_touchBar_validateUserInterfaceItems { for (NSTouchBarItemIdentifier identifier in self.touchBar.itemIdentifiers) { NSTouchBarItem *item = [self.touchBar itemForIdentifier:identifier]; @@ -1673,6 +1668,4 @@ static NSTouchBarItemIdentifier HBTouchBarPreview = @"fr.handbrake.preview"; } } -@dynamic touchBar; - @end diff --git a/macosx/HBEncodingProgressHUDController.m b/macosx/HBEncodingProgressHUDController.m index da417bf02..a78ed85a1 100644 --- a/macosx/HBEncodingProgressHUDController.m +++ b/macosx/HBEncodingProgressHUDController.m @@ -51,3 +51,41 @@ } @end + +@interface HBEncodingProgressHUDController (TouchBar) <NSTouchBarProvider, NSTouchBarDelegate> +@end + +@implementation HBEncodingProgressHUDController (TouchBar) + +@dynamic touchBar; + +static NSTouchBarItemIdentifier HBTouchBarCancelEncoding = @"fr.handbrake.cancelEncoding"; + +- (NSTouchBar *)makeTouchBar +{ + NSTouchBar *bar = [[NSTouchBar alloc] init]; + bar.delegate = self; + + bar.escapeKeyReplacementItemIdentifier = HBTouchBarCancelEncoding; + + return bar; +} + +- (NSTouchBarItem *)touchBar:(NSTouchBar *)touchBar makeItemForIdentifier:(NSTouchBarItemIdentifier)identifier +{ + if ([identifier isEqualTo:HBTouchBarCancelEncoding]) + { + NSCustomTouchBarItem *item = [[NSCustomTouchBarItem alloc] initWithIdentifier:identifier]; + item.customizationLabel = NSLocalizedString(@"Cancel Encoding", @"Touch bar"); + + NSButton *button = [NSButton buttonWithTitle:NSLocalizedString(@"Cancel", @"Touch bar") target:self action:@selector(cancelEncoding:)]; + button.bezelColor = NSColor.systemRedColor; + + item.view = button; + return item; + } + + return nil; +} + +@end diff --git a/macosx/HBImageUtilities.h b/macosx/HBImageUtilities.h index 67f8d00e4..6258d5d4f 100644 --- a/macosx/HBImageUtilities.h +++ b/macosx/HBImageUtilities.h @@ -1,4 +1,4 @@ -/* HBColorUtilities.h $ +/* HBImageUtilities.h $ This file is part of the HandBrake source code. Homepage: <http://handbrake.fr/>. @@ -6,6 +6,6 @@ #import <Foundation/Foundation.h> +CGImageRef CreateScaledCGImageFromCGImage(CGImageRef image, CGFloat thumbnailHeight); CGImageRef CGImageRotated(CGImageRef imgRef, CGFloat angle, BOOL flipped) CF_RETURNS_RETAINED; CGColorSpaceRef copyColorSpace(int primaries, int transfer, int matrix); -CGImageRef jnw_decompressedImage(CGImageRef CGImage); diff --git a/macosx/HBImageUtilities.m b/macosx/HBImageUtilities.m index 07530d815..d5858c60d 100644 --- a/macosx/HBImageUtilities.m +++ b/macosx/HBImageUtilities.m @@ -8,6 +8,59 @@ #import <Cocoa/Cocoa.h> #include "hb.h" +CGImageRef CreateScaledCGImageFromCGImage(CGImageRef image, CGFloat thumbnailHeight) +{ + // Create the bitmap context + CGContextRef context = NULL; + void * bitmapData; + int bitmapByteCount; + int bitmapBytesPerRow; + + // Get image width, height. We'll use the entire image. + int width = (CGFloat)CGImageGetWidth(image) / (CGFloat)CGImageGetHeight(image) * thumbnailHeight; + int height = thumbnailHeight; + + // Declare the number of bytes per row. Each pixel in the bitmap in this + // example is represented by 4 bytes; 8 bits each of red, green, blue, and + // alpha. + bitmapBytesPerRow = (width * 4); + bitmapByteCount = (bitmapBytesPerRow * height); + + // Allocate memory for image data. This is the destination in memory + // where any drawing to the bitmap context will be rendered. + bitmapData = malloc(bitmapByteCount); + if (bitmapData == NULL) + { + return nil; + } + + // Create the bitmap context. We want pre-multiplied ARGB, 8-bits + // per component. Regardless of what the source image format is + // (CMYK, Grayscale, and so on) it will be converted over to the format + // specified here by CGBitmapContextCreate. + CGColorSpaceRef colorspace = CGImageGetColorSpace(image); + context = CGBitmapContextCreate (bitmapData,width,height,8,bitmapBytesPerRow, + colorspace,kCGImageAlphaNoneSkipFirst); + + if (context == NULL) + { + // error creating context + return nil; + } + + // Draw the image to the bitmap context. Once we draw, the memory + // allocated for the context for rendering will then contain the + // raw image data in the specified color space. + CGContextDrawImage(context, CGRectMake(0,0,width, height), image); + + CGImageRef imgRef = CGBitmapContextCreateImage(context); + CGContextRelease(context); + free(bitmapData); + + return imgRef; +} + + CGImageRef CGImageRotated(CGImageRef imgRef, CGFloat angle, BOOL flipped) CF_RETURNS_RETAINED { CGFloat angleInRadians = angle * (M_PI / 180); diff --git a/macosx/HBPictureHUDController.h b/macosx/HBPictureHUDController.h index b127347e8..0aa144443 100644 --- a/macosx/HBPictureHUDController.h +++ b/macosx/HBPictureHUDController.h @@ -6,15 +6,19 @@ // #import <Cocoa/Cocoa.h> + #import "HBHUD.h" +#import "HBPreviewGenerator.h" NS_ASSUME_NONNULL_BEGIN @protocol HBPictureHUDControllerDelegate <NSObject> - (void)displayPreviewAtIndex:(NSUInteger)idx; + - (void)toggleScaleToScreen; - (void)showPictureSettings; + - (void)createMoviePreviewWithPictureIndex:(NSUInteger)index duration:(NSUInteger)duration; @end @@ -26,7 +30,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, copy) NSString *info; @property (nonatomic, copy) NSString *scale; -@property (nonatomic) NSUInteger pictureCount; +@property (nonatomic, weak) HBPreviewGenerator *generator; @property (nonatomic) NSUInteger selectedIndex; @end diff --git a/macosx/HBPictureHUDController.m b/macosx/HBPictureHUDController.m index 8db23767b..dcf4944e0 100644 --- a/macosx/HBPictureHUDController.m +++ b/macosx/HBPictureHUDController.m @@ -6,6 +6,8 @@ #import "HBPictureHUDController.h" +#import "HBThumbnailItemView.h" + @interface HBPictureHUDController () @property (weak) IBOutlet NSTextField *scaleLabel; @@ -20,9 +22,17 @@ @property (weak) IBOutlet NSTextField *durationUnitLabel; @property (nonatomic) BOOL fitToView; +@property (nonatomic) BOOL ignoreUpdates; + +@end +@interface HBPictureHUDController (TouchBar) <NSTouchBarProvider, NSTouchBarDelegate, NSScrubberDataSource, NSScrubberDelegate> +- (void)_touchBar_reloadScrubberData; +- (void)_touchBar_updateScrubberSelectedIndex:(NSUInteger)selectedIndex; +- (void)_touchBar_updateFitToView:(BOOL)fitToView; @end + @implementation HBPictureHUDController - (NSString *)nibName @@ -56,25 +66,34 @@ return YES; } -- (void)setPictureCount:(NSUInteger)pictureCount +- (void)setGenerator:(HBPreviewGenerator *)generator { - self.slider.numberOfTickMarks = pictureCount; - self.slider.maxValue = pictureCount - 1; + _generator = generator; + NSUInteger imagesCount = generator.imagesCount; + + self.slider.numberOfTickMarks = imagesCount; + self.slider.maxValue = imagesCount - 1; - if (self.selectedIndex > pictureCount) + if (self.selectedIndex > imagesCount) { - self.selectedIndex = pictureCount - 1; + self.selectedIndex = imagesCount - 1; } -} -- (NSUInteger)selectedIndex -{ - return self.slider.integerValue; + if (@available(macOS 10.12.2, *)) + { + [self _touchBar_reloadScrubberData]; + } } - (void)setSelectedIndex:(NSUInteger)selectedIndex { + _selectedIndex = selectedIndex; self.slider.integerValue = selectedIndex; + if (@available(macOS 10.12.2, *)) + { + [self _touchBar_updateScrubberSelectedIndex:selectedIndex]; + } + [self.delegate displayPreviewAtIndex:self.selectedIndex]; } - (void)setInfo:(NSString *)info @@ -87,6 +106,23 @@ self.scaleLabel.stringValue = scale; } +- (void)setFitToView:(BOOL)fitToView +{ + _fitToView = fitToView; + if (fitToView == NO) + { + self.scaleToScreenButton.title = NSLocalizedString(@"Scale To Screen", @"Picture HUD -> scale button"); + } + else + { + self.scaleToScreenButton.title = NSLocalizedString(@"Actual Scale", @"Picture HUD -> scale button"); + } + if (@available(macOS 10.12.2, *)) + { + [self _touchBar_updateFitToView:fitToView]; + } +} + - (IBAction)previewDurationPopUpChanged:(id)sender { [[NSUserDefaults standardUserDefaults] setObject:self.durationPopUp.titleOfSelectedItem forKey:@"PreviewLength"]; @@ -94,20 +130,13 @@ - (IBAction)pictureSliderChanged:(id)sender { - [self.delegate displayPreviewAtIndex:self.slider.integerValue]; + NSUInteger index = self.slider.integerValue; + self.selectedIndex = index; } - (IBAction)toggleScaleToScreen:(id)sender { [self.delegate toggleScaleToScreen]; - if (self.fitToView == YES) - { - self.scaleToScreenButton.title = NSLocalizedString(@"Scale To Screen", @"Picture HUD -> scale button"); - } - else - { - self.scaleToScreenButton.title = NSLocalizedString(@"Actual Scale", @"Picture HUD -> scale button"); - } self.fitToView = !self.fitToView; } @@ -118,7 +147,7 @@ - (IBAction)createMoviePreview:(id)sender { - [self.delegate createMoviePreviewWithPictureIndex:self.slider.integerValue duration:self.durationPopUp.titleOfSelectedItem.intValue]; + [self.delegate createMoviePreviewWithPictureIndex:self.selectedIndex duration:self.durationPopUp.titleOfSelectedItem.intValue]; } - (BOOL)HB_keyDown:(NSEvent *)event @@ -126,14 +155,14 @@ unichar key = [event.charactersIgnoringModifiers characterAtIndex:0]; if (key == NSLeftArrowFunctionKey) { - self.slider.integerValue = self.selectedIndex > self.slider.minValue ? self.selectedIndex - 1 : self.selectedIndex; - [self.delegate displayPreviewAtIndex:self.slider.integerValue]; + self.ignoreUpdates = YES; + self.selectedIndex = self.selectedIndex > 0 ? self.selectedIndex - 1 : self.selectedIndex; return YES; } else if (key == NSRightArrowFunctionKey) { - self.slider.integerValue = self.selectedIndex < self.slider.maxValue ? self.selectedIndex + 1 : self.selectedIndex; - [self.delegate displayPreviewAtIndex:self.slider.integerValue]; + self.ignoreUpdates = YES; + self.selectedIndex = self.selectedIndex < self.generator.imagesCount - 1 ? self.selectedIndex + 1 : self.selectedIndex; return YES; } else @@ -146,15 +175,159 @@ { if (theEvent.deltaY < 0) { - self.slider.integerValue = self.selectedIndex < self.slider.maxValue ? self.selectedIndex + 1 : self.selectedIndex; - [self.delegate displayPreviewAtIndex:self.slider.integerValue]; + self.selectedIndex = self.selectedIndex < self.generator.imagesCount - 1 ? self.selectedIndex + 1 : self.selectedIndex; } else if (theEvent.deltaY > 0) { - self.slider.integerValue = self.selectedIndex > self.slider.minValue ? self.selectedIndex - 1 : self.selectedIndex; - [self.delegate displayPreviewAtIndex:self.slider.integerValue]; + self.selectedIndex = self.selectedIndex > 0 ? self.selectedIndex - 1 : self.selectedIndex; } return YES; } @end + +@implementation HBPictureHUDController (TouchBar) + +#pragma mark - NSScrubberDataSource + +NSString *thumbnailScrubberItemIdentifier = @"thumbnailItem"; + +- (NSInteger)numberOfItemsForScrubber:(NSScrubber *)scrubber +{ + return self.generator.imagesCount; +} + +- (NSScrubberItemView *)scrubber:(NSScrubber *)scrubber viewForItemAtIndex:(NSInteger)index +{ + HBThumbnailItemView *itemView = [scrubber makeItemWithIdentifier:thumbnailScrubberItemIdentifier owner:nil]; + itemView.generator = self.generator; + itemView.thumbnailIndex = index; + return itemView; +} + +#pragma mark - NSScrubberFlowLayoutDelegate + +// Scrubber is asking for the size for a particular item. +- (NSSize)scrubber:(NSScrubber *)scrubber layout:(NSScrubberFlowLayout *)layout sizeForItemAtIndex:(NSInteger)itemIndex +{ + NSInteger val = 50; + return NSMakeSize(val, 30); +} + +#pragma mark - NSScrubberDelegate + +- (void)scrubber:(NSScrubber *)scrubber didSelectItemAtIndex:(NSInteger)selectedIndex +{ + if (self.selectedIndex != selectedIndex && self.ignoreUpdates == NO) + { + self.selectedIndex = selectedIndex; + } + self.ignoreUpdates = NO; +} + +#pragma mark - NSTouchBar + +static NSTouchBarItemIdentifier HBTouchBarMain = @"fr.handbrake.previewWindowTouchBar"; + +static NSTouchBarItemIdentifier HBTouchBarRip = @"fr.handbrake.rip"; +static NSTouchBarItemIdentifier HBTouchBarScrubber = @"fr.handbrake.scrubbe"; +static NSTouchBarItemIdentifier HBTouchBarFitToScreen = @"fr.handbrake.fitToScreen"; + +@dynamic touchBar; + +- (NSTouchBar *)makeTouchBar +{ + NSTouchBar *bar = [[NSTouchBar alloc] init]; + bar.delegate = self; + + bar.defaultItemIdentifiers = @[HBTouchBarRip, NSTouchBarItemIdentifierFlexibleSpace, HBTouchBarScrubber, NSTouchBarItemIdentifierFlexibleSpace, HBTouchBarFitToScreen]; + + bar.customizationIdentifier = HBTouchBarMain; + bar.customizationAllowedItemIdentifiers = @[HBTouchBarRip, HBTouchBarScrubber, HBTouchBarFitToScreen, NSTouchBarItemIdentifierFixedSpaceSmall, NSTouchBarItemIdentifierFixedSpaceLarge, NSTouchBarItemIdentifierFlexibleSpace]; + + return bar; +} + +- (NSTouchBarItem *)touchBar:(NSTouchBar *)touchBar makeItemForIdentifier:(NSTouchBarItemIdentifier)identifier +{ + if ([identifier isEqualTo:HBTouchBarRip]) + { + NSCustomTouchBarItem *item = [[NSCustomTouchBarItem alloc] initWithIdentifier:identifier]; + item.customizationLabel = NSLocalizedString(@"Live Preview", @"Touch bar"); + + NSButton *button = [NSButton buttonWithImage:[NSImage imageNamed:NSImageNameTouchBarPlayTemplate] + target:self action:@selector(createMoviePreview:)]; + + item.view = button; + return item; + } + else if ([identifier isEqualTo:HBTouchBarScrubber]) + { + NSCustomTouchBarItem *item = [[NSCustomTouchBarItem alloc] initWithIdentifier:identifier]; + item.customizationLabel = NSLocalizedString(@"Previews", @"Touch bar"); + + NSScrubber *scrubber = [[NSScrubber alloc] init]; + scrubber.delegate = self; + scrubber.dataSource = self; + + [scrubber registerClass:[HBThumbnailItemView class] forItemIdentifier:thumbnailScrubberItemIdentifier]; + + NSScrubberLayout *scrubberLayout = [[NSScrubberFlowLayout alloc] init]; + scrubber.scrubberLayout = scrubberLayout; + scrubber.showsAdditionalContentIndicators = YES; + scrubber.selectedIndex = 0; + scrubber.selectionOverlayStyle = [NSScrubberSelectionStyle outlineOverlayStyle]; + scrubber.continuous = YES; + scrubber.mode = NSScrubberModeFree; + scrubber.itemAlignment = NSScrubberAlignmentCenter; + + // Set the layout constraints on this scrubber so that it's 400 pixels wide. + NSDictionary *items = NSDictionaryOfVariableBindings(scrubber); + NSArray *theConstraints = [NSLayoutConstraint constraintsWithVisualFormat:@"H:[scrubber(500)]" options:0 metrics:nil views:items]; + [NSLayoutConstraint activateConstraints:theConstraints]; + + item.view = scrubber; + return item; + } + else if ([identifier isEqualTo:HBTouchBarFitToScreen]) + { + NSCustomTouchBarItem *item = [[NSCustomTouchBarItem alloc] initWithIdentifier:identifier]; + item.customizationLabel = NSLocalizedString(@"Scale To Screen", @"Touch bar"); + + NSButton *button = [NSButton buttonWithImage:[NSImage imageNamed:NSImageNameTouchBarEnterFullScreenTemplate] + target:self action:@selector(toggleScaleToScreen:)]; + + item.view = button; + return item; + } + + return nil; +} + +- (void)_touchBar_reloadScrubberData +{ + NSScrubber *scrubber = (NSScrubber *)[[self.touchBar itemForIdentifier:HBTouchBarScrubber] view]; + [scrubber reloadData]; + scrubber.animator.selectedIndex = self.selectedIndex; +} + +- (void)_touchBar_updateScrubberSelectedIndex:(NSUInteger)selectedIndex +{ + NSScrubber *scrubber = (NSScrubber *)[[self.touchBar itemForIdentifier:HBTouchBarScrubber] view]; + scrubber.animator.selectedIndex = selectedIndex; +} + +- (void)_touchBar_updateFitToView:(BOOL)fitToView +{ + NSButton *button = (NSButton *)[[self.touchBar itemForIdentifier:HBTouchBarFitToScreen] view]; + if (fitToView == NO) + { + button.image = [NSImage imageNamed:NSImageNameTouchBarEnterFullScreenTemplate]; + } + else + { + button.image = [NSImage imageNamed:NSImageNameTouchBarExitFullScreenTemplate]; + } +} + +@end diff --git a/macosx/HBPlayerHUDController.m b/macosx/HBPlayerHUDController.m index 311a2b0d5..ab459aea8 100644 --- a/macosx/HBPlayerHUDController.m +++ b/macosx/HBPlayerHUDController.m @@ -19,12 +19,19 @@ @property (weak) IBOutlet NSPopUpButton *tracksSelection; @property (nonatomic, readonly) NSDictionary *monospacedAttr; +@property (nonatomic, readonly) NSDictionary *normalMonospacedAttr; @property (nonatomic, readwrite) id rateObserver; @property (nonatomic, readwrite) id periodicObserver; @end +@interface HBPlayerHUDController (TouchBar) <NSTouchBarProvider, NSTouchBarDelegate> +- (void)_touchBar_updatePlayState:(BOOL)playing; +- (void)_touchBar_updateMaxDuration:(NSTimeInterval)duration; +- (void)_touchBar_updateTime:(NSTimeInterval)currentTime duration:(NSTimeInterval)duration; +@end + @implementation HBPlayerHUDController - (NSString *)nibName @@ -37,6 +44,7 @@ [super viewDidLoad]; if ([[NSFont class] respondsToSelector:@selector(monospacedDigitSystemFontOfSize:weight:)]) { + _normalMonospacedAttr = @{NSFontAttributeName: [NSFont monospacedDigitSystemFontOfSize:15 weight:NSFontWeightRegular]}; _monospacedAttr = @{NSFontAttributeName: [NSFont monospacedDigitSystemFontOfSize:[NSFont smallSystemFontSize] weight:NSFontWeightRegular]}; } else { @@ -73,20 +81,19 @@ }]; self.rateObserver = [self.player addRateObserverUsingBlock:^{ - if (weakSelf.player.rate != 0.0) - { - weakSelf.playButton.image = [NSImage imageNamed:@"PauseTemplate"]; - } - else - { - weakSelf.playButton.image = [NSImage imageNamed:@"PlayTemplate"]; - } + [weakSelf _refreshPlayButtonState]; }]; + NSTimeInterval duration = self.player.duration; [self.slider setMinValue:0.0]; - [self.slider setMaxValue:self.player.duration]; + [self.slider setMaxValue:duration]; [self.slider setDoubleValue:0.0]; + if (@available(macOS 10.12.2, *)) + { + [self _touchBar_updateMaxDuration:duration]; + } + self.player.volume = self.volumeSlider.floatValue; [self.player play]; @@ -201,10 +208,9 @@ return [NSString stringWithFormat:@"%02d:%02d.%03d", minutes, seconds, milliseconds]; } -- (NSAttributedString *)_monospacedString:(NSString *)string +- (NSAttributedString *)_smallMonospacedString:(NSString *)string { return [[NSAttributedString alloc] initWithString:string attributes:self.monospacedAttr]; - } - (void)_refreshUI @@ -215,8 +221,31 @@ NSTimeInterval duration = self.player.duration; self.slider.doubleValue = currentTime; - self.currentTimeLabel.attributedStringValue = [self _monospacedString:[self _timeToTimecode:currentTime]]; - self.remaingTimeLabel.attributedStringValue = [self _monospacedString:[self _timeToTimecode:duration - currentTime]]; + self.currentTimeLabel.attributedStringValue = [self _smallMonospacedString:[self _timeToTimecode:currentTime]]; + self.remaingTimeLabel.attributedStringValue = [self _smallMonospacedString:[self _timeToTimecode:duration - currentTime]]; + + if (@available(macOS 10.12.2, *)) + { + [self _touchBar_updateTime:currentTime duration:duration]; + } + } +} + +- (void)_refreshPlayButtonState +{ + BOOL playing = self.player.rate != 0.0; + if (playing) + { + self.playButton.image = [NSImage imageNamed:@"PauseTemplate"]; + } + else + { + self.playButton.image = [NSImage imageNamed:@"PlayTemplate"]; + } + + if (@available(macOS 10.12.2, *)) + { + [self _touchBar_updatePlayState:playing]; } } @@ -343,3 +372,149 @@ } @end + +@implementation HBPlayerHUDController (TouchBar) + +static NSTouchBarItemIdentifier HBTouchBar = @"fr.handbrake.playerHUDTouchBar"; + +static NSTouchBarItemIdentifier HBTouchBarDone = @"fr.handbrake.done"; +static NSTouchBarItemIdentifier HBTouchBarPlayPause = @"fr.handbrake.playPause"; +static NSTouchBarItemIdentifier HBTouchBarCurrentTime = @"fr.handbrake.currentTime"; +static NSTouchBarItemIdentifier HBTouchBarRemainingTime = @"fr.handbrake.remainingTime"; +static NSTouchBarItemIdentifier HBTouchBarTimeSlider = @"fr.handbrake.timeSlider"; + +@dynamic touchBar; + +- (NSTouchBar *)makeTouchBar +{ + NSTouchBar *bar = [[NSTouchBar alloc] init]; + bar.delegate = self; + + bar.escapeKeyReplacementItemIdentifier = HBTouchBarDone; + + bar.defaultItemIdentifiers = @[HBTouchBarPlayPause, NSTouchBarItemIdentifierFixedSpaceSmall, HBTouchBarCurrentTime, NSTouchBarItemIdentifierFixedSpaceSmall, HBTouchBarTimeSlider, NSTouchBarItemIdentifierFixedSpaceSmall, HBTouchBarRemainingTime]; + + bar.customizationIdentifier = HBTouchBar; + bar.customizationAllowedItemIdentifiers = @[HBTouchBarPlayPause, HBTouchBarCurrentTime, HBTouchBarTimeSlider, HBTouchBarRemainingTime, NSTouchBarItemIdentifierFixedSpaceSmall, NSTouchBarItemIdentifierFixedSpaceLarge, NSTouchBarItemIdentifierFlexibleSpace]; + + return bar; +} + +- (NSTouchBarItem *)touchBar:(NSTouchBar *)touchBar makeItemForIdentifier:(NSTouchBarItemIdentifier)identifier +{ + if ([identifier isEqualTo:HBTouchBarDone]) + { + NSCustomTouchBarItem *item = [[NSCustomTouchBarItem alloc] initWithIdentifier:identifier]; + item.customizationLabel = NSLocalizedString(@"Done", @"Touch bar"); + + NSButton *button = [NSButton buttonWithTitle:NSLocalizedString(@"Done", @"Touch bar") target:self action:@selector(showPicturesPreview:)]; + button.bezelColor = NSColor.systemYellowColor; + + item.view = button; + return item; + } + else if ([identifier isEqualTo:HBTouchBarPlayPause]) + { + NSCustomTouchBarItem *item = [[NSCustomTouchBarItem alloc] initWithIdentifier:identifier]; + item.customizationLabel = NSLocalizedString(@"Play/Pause", @"Touch bar"); + + NSButton *button = [NSButton buttonWithImage:[NSImage imageNamed:NSImageNameTouchBarPlayTemplate] + target:self action:@selector(playPauseToggle:)]; + + item.view = button; + return item; + } + else if ([identifier isEqualTo:HBTouchBarCurrentTime]) + { + NSCustomTouchBarItem *item = [[NSCustomTouchBarItem alloc] initWithIdentifier:identifier]; + item.customizationLabel = NSLocalizedString(@"Current Time", @"Touch bar"); + + NSTextField *label = [NSTextField labelWithString:NSLocalizedString(@"--:--", @"")]; + + item.view = label; + return item; + } + else if ([identifier isEqualTo:HBTouchBarRemainingTime]) + { + NSCustomTouchBarItem *item = [[NSCustomTouchBarItem alloc] initWithIdentifier:identifier]; + item.customizationLabel = NSLocalizedString(@"Remaining Time", @"Touch bar"); + + NSTextField *label = [NSTextField labelWithString:NSLocalizedString(@"- --:--", @"")]; + + item.view = label; + return item; + } + else if ([identifier isEqualTo:HBTouchBarTimeSlider]) + { + NSSliderTouchBarItem *item = [[NSSliderTouchBarItem alloc] initWithIdentifier:identifier]; + item.customizationLabel = NSLocalizedString(@"Slider", @"Touch bar"); + + item.slider.minValue = 0.0f; + item.slider.maxValue = 100.0f; + item.slider.doubleValue = 0.0f; + item.slider.continuous = YES; + item.target = self; + item.action = @selector(touchBarSliderChanged:); + + return item; + } + return nil; +} + +- (void)touchBarSliderChanged:(NSSliderTouchBarItem *)sender +{ + [self sliderChanged:sender.slider]; +} + +- (void)_touchBar_updatePlayState:(BOOL)playing +{ + NSButton *playButton = (NSButton *)[[self.touchBar itemForIdentifier:HBTouchBarPlayPause] view]; + + if (playing) + { + playButton.image = [NSImage imageNamed:NSImageNameTouchBarPauseTemplate]; + } + else + { + playButton.image = [NSImage imageNamed:NSImageNameTouchBarPlayTemplate]; + } +} + +- (void)_touchBar_updateMaxDuration:(NSTimeInterval)duration +{ + NSSlider *slider = (NSSlider *)[[self.touchBar itemForIdentifier:HBTouchBarTimeSlider] slider]; + slider.maxValue = duration; +} + +- (NSString *)_timeToString:(NSTimeInterval)timeInSeconds negative:(BOOL)negative; +{ + UInt16 seconds = (UInt16)fmod(timeInSeconds, 60.0); + UInt16 minutes = (UInt16)fmod(timeInSeconds / 60.0, 60.0); + + if (negative) + { + return [NSString stringWithFormat:@"-%02d:%02d", minutes, seconds]; + } + else + { + return [NSString stringWithFormat:@"%02d:%02d", minutes, seconds]; + } +} + +- (NSAttributedString *)_monospacedString:(NSString *)string +{ + return [[NSAttributedString alloc] initWithString:string attributes:self.normalMonospacedAttr]; +} + +- (void)_touchBar_updateTime:(NSTimeInterval)currentTime duration:(NSTimeInterval)duration +{ + NSSlider *slider = (NSSlider *)[[self.touchBar itemForIdentifier:HBTouchBarTimeSlider] slider]; + NSTextField *currentTimeLabel = (NSTextField *)[[self.touchBar itemForIdentifier:HBTouchBarCurrentTime] view]; + NSTextField *remainingTimeLabel = (NSTextField *)[[self.touchBar itemForIdentifier:HBTouchBarRemainingTime] view]; + + slider.doubleValue = currentTime; + currentTimeLabel.attributedStringValue = [self _monospacedString:[self _timeToString:currentTime negative:NO]]; + remainingTimeLabel.attributedStringValue = [self _monospacedString:[self _timeToString:duration - currentTime negative:YES]]; +} + +@end diff --git a/macosx/HBPreviewController.m b/macosx/HBPreviewController.m index cd30f8161..643c1c28d 100644 --- a/macosx/HBPreviewController.m +++ b/macosx/HBPreviewController.m @@ -159,14 +159,13 @@ if (generator) { generator.delegate = self; - - // adjust the preview slider length - self.pictureHUD.pictureCount = generator.imagesCount; + self.pictureHUD.generator = generator; } else { self.previewView.image = nil; self.window.title = NSLocalizedString(@"Preview", @"Preview -> window title"); + self.pictureHUD.generator = nil; } [self switchStateToHUD:self.pictureHUD]; } @@ -298,7 +297,7 @@ } // Show the current hud - NSMutableArray *huds = [@[self.pictureHUD, self.encodingHUD, self.playerHUD] mutableCopy]; + NSMutableArray<NSViewController<HBHUD> *> *huds = [@[self.pictureHUD, self.encodingHUD, self.playerHUD] mutableCopy]; [huds removeObject:hud]; for (NSViewController *controller in huds) { controller.view.hidden = YES; diff --git a/macosx/HBPreviewGenerator.h b/macosx/HBPreviewGenerator.h index 88999b6a7..dcf5f6953 100644 --- a/macosx/HBPreviewGenerator.h +++ b/macosx/HBPreviewGenerator.h @@ -29,15 +29,29 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)init NS_UNAVAILABLE; - (instancetype)initWithCore:(HBCore *)core job:(HBJob *)job NS_DESIGNATED_INITIALIZER; -/* Still image generator */ +#pragma mark - Still image generator + +/** + * Returns the picture preview at the specified index + * + * @param index picture index in title. + */ - (nullable CGImageRef) copyImageAtIndex: (NSUInteger) index shouldCache: (BOOL) cache CF_RETURNS_RETAINED; + +/** + * Returns a small picture preview at the specified index asynchronously + * + * @param index picture index in title. + */ +- (void) copySmallImageAtIndex: (NSUInteger) index completionHandler:(void (^)(__nullable CGImageRef result))handler; + @property (nonatomic, readonly) NSUInteger imagesCount; @property (nonatomic, readonly) CGSize imageSize; - (void) purgeImageCache; @property (nonatomic, readonly, copy) NSString *info; -/* Video generator */ +#pragma mark - Video generator - (BOOL) createMovieAsyncWithImageAtIndex: (NSUInteger) index duration: (NSUInteger) seconds; - (void) cancel; diff --git a/macosx/HBPreviewGenerator.m b/macosx/HBPreviewGenerator.m index 47eb0d576..6ac4e76bd 100644 --- a/macosx/HBPreviewGenerator.m +++ b/macosx/HBPreviewGenerator.m @@ -11,10 +11,14 @@ @interface HBPreviewGenerator () -@property (nonatomic, readonly) NSCache *picturePreviews; @property (nonatomic, readonly, weak) HBCore *scanCore; @property (nonatomic, readonly, strong) HBJob *job; +@property (nonatomic, readonly) NSCache<NSNumber *, id> *previewsCache; +@property (nonatomic, readonly) NSCache<NSNumber *, id> *smallPreviewsCache; + +@property (nonatomic, readonly) dispatch_semaphore_t sem; + @property (nonatomic, strong) HBCore *core; @property (nonatomic) BOOL reloadInQueue; @@ -36,12 +40,17 @@ _scanCore = core; _job = job; - _picturePreviews = [[NSCache alloc] init]; + _previewsCache = [[NSCache alloc] init]; // Limit the cache to 60 1080p previews, the cost is in pixels - _picturePreviews.totalCostLimit = 60 * 1920 * 1080; + _previewsCache.totalCostLimit = 60 * 1920 * 1080; + + _smallPreviewsCache = [[NSCache alloc] init]; + _smallPreviewsCache.totalCostLimit = 60 * 320 * 180; _imagesCount = [_scanCore imagesCountForTitle:self.job.title]; + _sem = dispatch_semaphore_create(4); + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(imagesSettingsDidChange) name:HBPictureChangedNotification object:job.picture]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(imagesSettingsDidChange) name:HBFiltersChangedNotification object:job.filters]; } @@ -66,11 +75,13 @@ - (CGImageRef) copyImageAtIndex: (NSUInteger) index shouldCache: (BOOL) cache { if (index >= self.imagesCount) + { return nil; + } // The preview for the specified index may not currently exist, so this method // generates it if necessary. - CGImageRef theImage = (__bridge CGImageRef)([self.picturePreviews objectForKey:@(index)]); + CGImageRef theImage = (__bridge CGImageRef)([_previewsCache objectForKey:@(index)]); if (!theImage) { @@ -87,7 +98,7 @@ { // The cost is the number of pixels of the image NSUInteger previewCost = CGImageGetWidth(theImage) * CGImageGetHeight(theImage); - [self.picturePreviews setObject:(__bridge id)(theImage) forKey:@(index) cost:previewCost]; + [self.previewsCache setObject:(__bridge id)(theImage) forKey:@(index) cost:previewCost]; } } else @@ -104,7 +115,7 @@ */ - (void) purgeImageCache { - [self.picturePreviews removeAllObjects]; + [self.previewsCache removeAllObjects]; } - (CGSize)imageSize @@ -112,7 +123,7 @@ return CGSizeMake(self.job.picture.displayWidth, self.job.picture.height); } -- (void) imagesSettingsDidChange +- (void)imagesSettingsDidChange { // Purge the existing picture previews so they get recreated the next time // they are needed. @@ -138,12 +149,65 @@ return self.job.picture.info; } +#pragma mark - Small previews + +- (void)copySmallImageAtIndex:(NSUInteger)index completionHandler:(void (^)(__nullable CGImageRef result))handler +{ + dispatch_semaphore_wait(_sem, DISPATCH_TIME_FOREVER); + if (index >= self.imagesCount) + { + handler(NULL); + dispatch_semaphore_signal(_sem); + return; + } + + CGImageRef image; + + // First try to look in the small previews cache + image = (__bridge CGImageRef)([_smallPreviewsCache objectForKey:@(index)]); + + if (image != NULL) + { + handler(image); + dispatch_semaphore_signal(_sem); + return; + } + + // Else try the normal cache + image = (__bridge CGImageRef)([_previewsCache objectForKey:@(index)]); + + if (image == NULL) + { + image = (CGImageRef)[self.scanCore copyImageAtIndex:index + forTitle:self.job.title + pictureFrame:self.job.picture + deinterlace:NO + rotate:self.job.filters.rotate + flipped:self.job.filters.flip]; + CFAutorelease(image); + } + + if (image != NULL) + { + CGImageRef scaledImage = CreateScaledCGImageFromCGImage(image, 30); + // The cost is the number of pixels of the image + NSUInteger previewCost = CGImageGetWidth(scaledImage) * CGImageGetHeight(scaledImage); + [self.smallPreviewsCache setObject:(__bridge id)(scaledImage) forKey:@(index) cost:previewCost]; + handler(scaledImage); + dispatch_semaphore_signal(_sem); + return; + } + + handler(NULL); + dispatch_semaphore_signal(_sem); +} + #pragma mark - #pragma mark Preview movie + (NSURL *) generateFileURLForType:(NSString *) type { - NSURL *previewDirectory = [[HBUtilities appSupportURL] URLByAppendingPathComponent:[NSString stringWithFormat:@"/Previews/%d", getpid()] isDirectory:YES]; + NSURL *previewDirectory = [[HBUtilities appSupportURL] URLByAppendingPathComponent:[NSString stringWithFormat:@"/Previews/%d", getpid()] isDirectory:YES]; if (![[NSFileManager defaultManager] createDirectoryAtPath:previewDirectory.path withIntermediateDirectories:YES @@ -242,7 +306,7 @@ /** * Cancels the encoding process */ -- (void) cancel +- (void)cancel { if (self.core.state == HBStateWorking || self.core.state == HBStatePaused) { diff --git a/macosx/HBQueueController.m b/macosx/HBQueueController.m index 78df3bcb4..7fb3878c1 100644 --- a/macosx/HBQueueController.m +++ b/macosx/HBQueueController.m @@ -65,8 +65,8 @@ static void *HBControllerQueueCoreContext = &HBControllerQueueCoreContext; @end @interface HBQueueController (TouchBar) <NSTouchBarProvider, NSTouchBarDelegate> -- (void)updateButtonsStateForQueueCore:(HBState)state; -- (void)validateTouchBarsItems; +- (void)_touchBar_updateButtonsStateForQueueCore:(HBState)state; +- (void)_touchBar_validateUserInterfaceItems; @end @implementation HBQueueController @@ -134,8 +134,8 @@ static void *HBControllerQueueCoreContext = &HBControllerQueueCoreContext; [self.window.toolbar validateVisibleItems]; if (@available(macOS 10.12.2, *)) { - [self updateButtonsStateForQueueCore:state]; - [self validateTouchBarsItems]; + [self _touchBar_updateButtonsStateForQueueCore:state]; + [self _touchBar_validateUserInterfaceItems]; } } else @@ -629,7 +629,6 @@ static void *HBControllerQueueCoreContext = &HBControllerQueueCoreContext; } self.countTextField.stringValue = string; - [self.controller setQueueState:pendingCount]; self.pendingItemsCount = pendingCount; self.completedItemsCount = completedCount; @@ -1370,6 +1369,7 @@ static void *HBControllerQueueCoreContext = &HBControllerQueueCoreContext; if (job != self.currentJob) { job.state = HBJobStateWorking; + [self updateQueueStats]; [self.controller openJob:[job copy] completionHandler:^(BOOL result) { [self.jobs beginTransaction]; if (result) @@ -1716,6 +1716,8 @@ static void *HBControllerQueueCoreContext = &HBControllerQueueCoreContext; @implementation HBQueueController (TouchBar) +@dynamic touchBar; + static NSTouchBarItemIdentifier HBTouchBarMain = @"fr.handbrake.queueWindowTouchBar"; static NSTouchBarItemIdentifier HBTouchBarRip = @"fr.handbrake.rip"; @@ -1739,7 +1741,7 @@ static NSTouchBarItemIdentifier HBTouchBarPause = @"fr.handbrake.pause"; if ([identifier isEqualTo:HBTouchBarRip]) { NSCustomTouchBarItem *item = [[NSCustomTouchBarItem alloc] initWithIdentifier:identifier]; - item.customizationLabel = NSLocalizedString(@"Rip", @"Touch bar"); + item.customizationLabel = NSLocalizedString(@"Start/Stop Encoding", @"Touch bar"); NSButton *button = [NSButton buttonWithImage:[NSImage imageNamed:NSImageNameTouchBarPlayTemplate] target:self action:@selector(toggleStartCancel:)]; @@ -1749,7 +1751,7 @@ static NSTouchBarItemIdentifier HBTouchBarPause = @"fr.handbrake.pause"; else if ([identifier isEqualTo:HBTouchBarPause]) { NSCustomTouchBarItem *item = [[NSCustomTouchBarItem alloc] initWithIdentifier:identifier]; - item.customizationLabel = NSLocalizedString(@"Pause", @"Touch bar"); + item.customizationLabel = NSLocalizedString(@"Pause/Resume Encoding", @"Touch bar"); NSButton *button = [NSButton buttonWithImage:[NSImage imageNamed:NSImageNameTouchBarPauseTemplate] target:self action:@selector(togglePauseResume:)]; @@ -1760,7 +1762,7 @@ static NSTouchBarItemIdentifier HBTouchBarPause = @"fr.handbrake.pause"; return nil; } -- (void)updateButtonsStateForQueueCore:(HBState)state; +- (void)_touchBar_updateButtonsStateForQueueCore:(HBState)state; { NSButton *ripButton = (NSButton *)[[self.touchBar itemForIdentifier:HBTouchBarRip] view]; NSButton *pauseButton = (NSButton *)[[self.touchBar itemForIdentifier:HBTouchBarPause] view]; @@ -1782,7 +1784,7 @@ static NSTouchBarItemIdentifier HBTouchBarPause = @"fr.handbrake.pause"; } } -- (void)validateTouchBarsItems +- (void)_touchBar_validateUserInterfaceItems { for (NSTouchBarItemIdentifier identifier in self.touchBar.itemIdentifiers) { NSTouchBarItem *item = [self.touchBar itemForIdentifier:identifier]; @@ -1795,7 +1797,4 @@ static NSTouchBarItemIdentifier HBTouchBarPause = @"fr.handbrake.pause"; } } -@dynamic touchBar; - @end - diff --git a/macosx/HBThumbnailItemView.h b/macosx/HBThumbnailItemView.h new file mode 100644 index 000000000..0e57ba344 --- /dev/null +++ b/macosx/HBThumbnailItemView.h @@ -0,0 +1,19 @@ +/* + Copyright (C) 2017 Apple Inc. All Rights Reserved. + See LICENSE.txt for this sample’s licensing information + + Abstract: + Custom NSScrubberItemView used to display an image. + */ + +#import <Cocoa/Cocoa.h> +#import "HBPreviewGenerator.h" + +@interface HBThumbnailItemView : NSScrubberItemView + +@property (nonatomic) NSImage *thumbnail; +@property (nonatomic) NSUInteger thumbnailIndex; +@property (nonatomic, weak) HBPreviewGenerator *generator; + + +@end diff --git a/macosx/HBThumbnailItemView.m b/macosx/HBThumbnailItemView.m new file mode 100644 index 000000000..9a96bda0f --- /dev/null +++ b/macosx/HBThumbnailItemView.m @@ -0,0 +1,90 @@ +/* + Copyright (C) 2017 Apple Inc. All Rights Reserved. + See LICENSE.txt for this sample’s licensing information + + Abstract: + Custom NSScrubberItemView used to display an image. + */ + +#import "HBThumbnailItemView.h" + +@interface HBThumbnailItemView () + +@property (strong) NSImageView *imageView; + +@end + +#pragma mark - + +@implementation HBThumbnailItemView + +@synthesize thumbnail = _thumbnail; + +- (instancetype)initWithFrame:(NSRect)frameRect +{ + self = [super initWithFrame:frameRect]; + if (self != nil) + { + _thumbnail = [[NSImage alloc] initWithSize:frameRect.size]; + _imageView = [NSImageView imageViewWithImage:_thumbnail]; + [_imageView setAutoresizingMask:(NSAutoresizingMaskOptions)(NSViewWidthSizable | NSViewHeightSizable)]; + + [self addSubview:_imageView]; + } + + return self; +} + +- (void)updateLayer +{ + self.layer.backgroundColor = NSColor.controlColor.CGColor; +} + +- (void)layout +{ + [super layout]; + _imageView.frame = self.bounds; +} + +- (NSImage *)thumbnail +{ + return _imageView.image; +} + +- (void)setThumbnail:(NSImage *)thumbnail +{ + _imageView.hidden = NO; + _imageView.image = thumbnail; +} + +- (void)setThumbnailIndex:(NSUInteger)thumbnailIndex +{ + _thumbnailIndex = thumbnailIndex; + + _imageView.hidden = YES; + + dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0), ^{ + HBPreviewGenerator *generator = self.generator; + + [generator copySmallImageAtIndex:thumbnailIndex completionHandler:^(CGImageRef _Nullable result) + { + if (result != NULL) + { + NSSize size = NSMakeSize(CGImageGetWidth(result), CGImageGetHeight(result)); + NSImage *thumbnail = [[NSImage alloc] initWithCGImage:result size:size]; + + dispatch_async(dispatch_get_main_queue(), ^{ + [self setThumbnail:thumbnail]; + }); + } + else + { + dispatch_async(dispatch_get_main_queue(), ^{ + [self setThumbnail:nil]; + }); + } + }]; + }); +} + +@end diff --git a/macosx/HandBrake.xcodeproj/project.pbxproj b/macosx/HandBrake.xcodeproj/project.pbxproj index 85ca6ea03..ca90dc525 100644 --- a/macosx/HandBrake.xcodeproj/project.pbxproj +++ b/macosx/HandBrake.xcodeproj/project.pbxproj @@ -199,6 +199,8 @@ A9736F171C7DA5FE008F1D18 /* HandBrakeKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A9736F021C7DA5FE008F1D18 /* HandBrakeKit.framework */; }; A9736F181C7DA5FE008F1D18 /* HandBrakeKit.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = A9736F021C7DA5FE008F1D18 /* HandBrakeKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; A9736F1F1C7DA667008F1D18 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 273F204014ADBC210021BE6D /* Foundation.framework */; }; + A973E109216E74AC00D498EC /* HBImageUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = A9CE0A931F57EC4600724577 /* HBImageUtilities.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A973E10C216E74E900D498EC /* HBThumbnailItemView.m in Sources */ = {isa = PBXBuildFile; fileRef = A973E10B216E74E900D498EC /* HBThumbnailItemView.m */; }; A975B02220F7AF29004675CC /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = A975B02020F7AF29004675CC /* Localizable.strings */; }; A98036CD1CCA91DD007661AA /* HBAVPlayer.m in Sources */ = {isa = PBXBuildFile; fileRef = A98036CC1CCA91DD007661AA /* HBAVPlayer.m */; }; A98B8E241C7DD2A200B810C9 /* HBPresetCoding.h in Headers */ = {isa = PBXBuildFile; fileRef = A997D8EB1A4ABB0900E19B6F /* HBPresetCoding.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -524,6 +526,8 @@ A9736F061C7DA5FE008F1D18 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; A9736F0B1C7DA5FE008F1D18 /* HandBrakeKitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = HandBrakeKitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; A9736F141C7DA5FE008F1D18 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; + A973E10A216E74E800D498EC /* HBThumbnailItemView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HBThumbnailItemView.h; sourceTree = "<group>"; }; + A973E10B216E74E900D498EC /* HBThumbnailItemView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HBThumbnailItemView.m; sourceTree = "<group>"; }; A975B02120F7AF29004675CC /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = "<group>"; }; A975C08C1AE8C5270061870D /* HBStateFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HBStateFormatter.h; sourceTree = "<group>"; }; A975C08D1AE8C5270061870D /* HBStateFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HBStateFormatter.m; sourceTree = "<group>"; }; @@ -1295,6 +1299,8 @@ A941ACB91CD75B4E0029D06A /* HBHUD.h */, A92B148020CA9F7600146FD8 /* HBHUDView.h */, A92B148120CA9F7600146FD8 /* HBHUDView.m */, + A973E10A216E74E800D498EC /* HBThumbnailItemView.h */, + A973E10B216E74E900D498EC /* HBThumbnailItemView.m */, A96664B21CCE48F700DA4A57 /* HBPictureHUDController.h */, A96664B31CCE48F700DA4A57 /* HBPictureHUDController.m */, A9A96B8420CAD2C200A39AFB /* HBPictureHUDController.xib */, @@ -1350,6 +1356,7 @@ A91CE2EB1C7DAEEE0068F46F /* HBDVDDetector.h in Headers */, A91CE2EC1C7DAEEE0068F46F /* HBStateFormatter.h in Headers */, A91CE2ED1C7DAEEE0068F46F /* HBUtilities.h in Headers */, + A973E109216E74AC00D498EC /* HBImageUtilities.h in Headers */, A91CE2FB1C7DB99D0068F46F /* HBPresetsManager.h in Headers */, A91CE2FC1C7DB99D0068F46F /* HBPreset.h in Headers */, A91CE2FD1C7DB99D0068F46F /* HBMutablePreset.h in Headers */, @@ -1590,6 +1597,7 @@ files = ( A916C99B1C844A0800C7B560 /* HBQueueOutlineView.m in Sources */, A916C9991C8449E200C7B560 /* main.mm in Sources */, + A973E10C216E74E900D498EC /* HBThumbnailItemView.m in Sources */, A916C9981C8449DB00C7B560 /* HBTitleSelectionController.m in Sources */, A903C5601CCE78060026B0ED /* NSWindow+HBAdditions.m in Sources */, A9A0CBE81CCEA3670045B3DF /* HBPlayerTrack.m in Sources */, diff --git a/macosx/HandBrakeKit/HandBrakeKit.h b/macosx/HandBrakeKit/HandBrakeKit.h index de6e1dfa0..d5a684d34 100644 --- a/macosx/HandBrakeKit/HandBrakeKit.h +++ b/macosx/HandBrakeKit/HandBrakeKit.h @@ -38,6 +38,7 @@ FOUNDATION_EXPORT const unsigned char HandBrakeKitVersionString[]; #import <HandBrakeKit/HBStateFormatter.h> #import <HandBrakeKit/HBDistributedArray.h> #import <HandBrakeKit/HBUtilities.h> +#import <HandBrakeKit/HBImageUtilities.h> #import <HandBrakeKit/HBPresetsManager.h> #import <HandBrakeKit/HBPreset.h> |