summaryrefslogtreecommitdiffstats
path: root/macosx
diff options
context:
space:
mode:
authorDamiano Galassi <[email protected]>2018-10-10 19:58:21 +0200
committerDamiano Galassi <[email protected]>2018-10-10 19:58:21 +0200
commit2264d25308238210892840d7de3fa1edf9de105e (patch)
tree0285fd80ba9717f50219a45b8891d072f030c77f /macosx
parent210164589bd29a2b21cfd58a928aa11e603782eb (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.xib7
-rw-r--r--macosx/HBController.h1
-rw-r--r--macosx/HBController.m55
-rw-r--r--macosx/HBEncodingProgressHUDController.m38
-rw-r--r--macosx/HBImageUtilities.h4
-rw-r--r--macosx/HBImageUtilities.m53
-rw-r--r--macosx/HBPictureHUDController.h6
-rw-r--r--macosx/HBPictureHUDController.m227
-rw-r--r--macosx/HBPlayerHUDController.m201
-rw-r--r--macosx/HBPreviewController.m7
-rw-r--r--macosx/HBPreviewGenerator.h18
-rw-r--r--macosx/HBPreviewGenerator.m82
-rw-r--r--macosx/HBQueueController.m23
-rw-r--r--macosx/HBThumbnailItemView.h19
-rw-r--r--macosx/HBThumbnailItemView.m90
-rw-r--r--macosx/HandBrake.xcodeproj/project.pbxproj8
-rw-r--r--macosx/HandBrakeKit/HandBrakeKit.h1
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>