]> granicus.if.org Git - handbrake/commitdiff
MacGui: add touch bars in the preview window, improve touch bars in the main and...
authorDamiano Galassi <damiog@gmail.com>
Wed, 10 Oct 2018 17:58:21 +0000 (19:58 +0200)
committerDamiano Galassi <damiog@gmail.com>
Wed, 10 Oct 2018 17:58:21 +0000 (19:58 +0200)
17 files changed:
macosx/Base.lproj/HBEncodingProgressHUDController.xib
macosx/HBController.h
macosx/HBController.m
macosx/HBEncodingProgressHUDController.m
macosx/HBImageUtilities.h
macosx/HBImageUtilities.m
macosx/HBPictureHUDController.h
macosx/HBPictureHUDController.m
macosx/HBPlayerHUDController.m
macosx/HBPreviewController.m
macosx/HBPreviewGenerator.h
macosx/HBPreviewGenerator.m
macosx/HBQueueController.m
macosx/HBThumbnailItemView.h [new file with mode: 0644]
macosx/HBThumbnailItemView.m [new file with mode: 0644]
macosx/HandBrake.xcodeproj/project.pbxproj
macosx/HandBrakeKit/HandBrakeKit.h

index 92885ea8f8ea50ad935e9f57fc3066e751e38f61..b284786545c243351f23124a72c4c42b6f65253b 100644 (file)
@@ -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"/>
index c3a1eba688e3545fc0f583e0eaee4833803f3f87..75494f1a0aa899ce2a39a561e16812050095cad9 100644 (file)
@@ -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;
index 62330608d79e57bbdc15f8cd873c78fe79e7c5d1..c4a0c70e5ac08c992c6c5b419460eacae170232d 100644 (file)
@@ -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
index da417bf029b7ec8b66f01e90c439f68bd9938867..a78ed85a164a031847d31d6d076ce7267c5e4696 100644 (file)
 }
 
 @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
index 67f8d00e4df613275e671a559d0ff0deee4a42e6..6258d5d4fdb9b63ab7610f5be5b268f026cd41da 100644 (file)
@@ -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);
index 07530d815e7b2461e992d71ee6bd53505c408a59..d5858c60d9751cddc2ffc0776a5c1d2e1b9ad93a 100644 (file)
@@ -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);
index b127347e8849c450e4f552b503f26af65baf1941..0aa14444322d3181b114c2bd33145f045ee79df2 100644 (file)
@@ -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
index 8db23767b63f352112d4ef82086ccb6a56ce0691..dcf4944e07578345f6b1267497f1cf33dabc0610 100644 (file)
@@ -6,6 +6,8 @@
 
 #import "HBPictureHUDController.h"
 
+#import "HBThumbnailItemView.h"
+
 @interface HBPictureHUDController ()
 
 @property (weak) IBOutlet NSTextField *scaleLabel;
 @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
     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
     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"];
 
 - (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;
 }
 
 
 - (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
     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
 {
     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
index 311a2b0d54a3f14ecb94d4be571ad1f12fdc7836..ab459aea80daae7414ab78ef9452efa9bc64e7c3 100644 (file)
 @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 {
         }];
 
         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];
     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
         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];
     }
 }
 
 }
 
 @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
index cd30f8161e4837156d9ba22bdcf77050db6708e8..643c1c28d478a0ecbaa7c71d09bf01afe9a7ab17 100644 (file)
     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];
 }
     }
 
     // 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;
index 88999b6a7ff687034262b4f80def5dc01fddc0b0..dcf5f6953b55ff8be060b5694c969e9e1584cd91 100644 (file)
@@ -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;
 
index 47eb0d5766187d2c92298fa4b4eed3f9a06b11bd..6ac4e76bd06a7ea0a504985f3de71fb64d192559 100644 (file)
 
 @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;
         _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];
     }
 - (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
  */
 - (void) purgeImageCache
 {
-    [self.picturePreviews removeAllObjects];
+    [self.previewsCache removeAllObjects];
 }
 
 - (CGSize)imageSize
     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.
     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
 /**
  * Cancels the encoding process
  */
-- (void) cancel
+- (void)cancel
 {
     if (self.core.state == HBStateWorking || self.core.state == HBStatePaused)
     {
index 78df3bcb4f1475c010510209664107f8b85c2b17..7fb3878c1e840b60ec5767a23c9a19e8e7c14d0d 100644 (file)
@@ -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 (file)
index 0000000..0e57ba3
--- /dev/null
@@ -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 (file)
index 0000000..9a96bda
--- /dev/null
@@ -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
index 85ca6ea03110703ae76455c4109e04267a35d9e1..ca90dc525fb095695a47630a0081ccf88a1b447a 100644 (file)
                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, ); }; };
                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>"; };
                                A941ACB91CD75B4E0029D06A /* HBHUD.h */,
                                A92B148020CA9F7600146FD8 /* HBHUDView.h */,
                                A92B148120CA9F7600146FD8 /* HBHUDView.m */,
+                               A973E10A216E74E800D498EC /* HBThumbnailItemView.h */,
+                               A973E10B216E74E900D498EC /* HBThumbnailItemView.m */,
                                A96664B21CCE48F700DA4A57 /* HBPictureHUDController.h */,
                                A96664B31CCE48F700DA4A57 /* HBPictureHUDController.m */,
                                A9A96B8420CAD2C200A39AFB /* HBPictureHUDController.xib */,
                                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 */,
                        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 */,
index de6e1dfa0de2a29e324147c90496bc6f472f01ae..d5a684d34a64492cbc43761e51f1d9b307f2c183 100644 (file)
@@ -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>