Skip to content
This repository has been archived by the owner on Sep 4, 2018. It is now read-only.

Some fixes #246

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
6 changes: 6 additions & 0 deletions Alcatraz.xcodeproj/project.pbxproj
Expand Up @@ -12,6 +12,7 @@
0C7AF650186B4E890064EE7B /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0C7AF64F186B4E890064EE7B /* QuartzCore.framework */; };
32BDEBC3173A46A900513A7C /* eye_icon.png in Resources */ = {isa = PBXBuildFile; fileRef = 32BDEBC2173A46A900513A7C /* eye_icon.png */; };
32D86505173A4555009C724B /* eye_icon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 32D86503173A4555009C724B /* eye_icon@2x.png */; };
636C388F1ABCB1B7004DECCF /* ATZScreenshotsStorage.m in Sources */ = {isa = PBXBuildFile; fileRef = 636C388E1ABCB1B7004DECCF /* ATZScreenshotsStorage.m */; };
64C28A5518136FF30025C0B1 /* toolbar_colors.png in Resources */ = {isa = PBXBuildFile; fileRef = 64C28A4918136FF30025C0B1 /* toolbar_colors.png */; };
64C28A5618136FF30025C0B1 /* toolbar_colors@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 64C28A4A18136FF30025C0B1 /* toolbar_colors@2x.png */; };
64C28A5918136FF30025C0B1 /* toolbar_plugins.png in Resources */ = {isa = PBXBuildFile; fileRef = 64C28A4D18136FF30025C0B1 /* toolbar_plugins.png */; };
Expand Down Expand Up @@ -83,6 +84,8 @@
0C7AF64F186B4E890064EE7B /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
32BDEBC2173A46A900513A7C /* eye_icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = eye_icon.png; sourceTree = "<group>"; };
32D86503173A4555009C724B /* eye_icon@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "eye_icon@2x.png"; sourceTree = "<group>"; };
636C388D1ABCB1B7004DECCF /* ATZScreenshotsStorage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ATZScreenshotsStorage.h; sourceTree = "<group>"; };
636C388E1ABCB1B7004DECCF /* ATZScreenshotsStorage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ATZScreenshotsStorage.m; sourceTree = "<group>"; };
6449530218CE826C009494BD /* ATZVersion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ATZVersion.h; sourceTree = "<group>"; };
64C28A4918136FF30025C0B1 /* toolbar_colors.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = toolbar_colors.png; sourceTree = "<group>"; };
64C28A4A18136FF30025C0B1 /* toolbar_colors@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "toolbar_colors@2x.png"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -353,6 +356,8 @@
8917DA171726B63B00F0B2D2 /* ATZShell.m */,
89B4F2BC172FF5AC001FD2E3 /* ATZPBXProjParser.h */,
89B4F2BD172FF5AC001FD2E3 /* ATZPBXProjParser.m */,
636C388D1ABCB1B7004DECCF /* ATZScreenshotsStorage.h */,
636C388E1ABCB1B7004DECCF /* ATZScreenshotsStorage.m */,
);
path = Helpers;
sourceTree = "<group>";
Expand Down Expand Up @@ -509,6 +514,7 @@
8917D9F31726B4EE00F0B2D2 /* ATZColorScheme.m in Sources */,
8917D9F41726B4EE00F0B2D2 /* ATZFileTemplate.m in Sources */,
8917D9F51726B4EE00F0B2D2 /* ATZPackage.m in Sources */,
636C388F1ABCB1B7004DECCF /* ATZScreenshotsStorage.m in Sources */,
8917D9F61726B4EE00F0B2D2 /* ATZPlugin.m in Sources */,
8ADC22341A2AD5B800DB7BCA /* ATZPreviewImageButton.m in Sources */,
8917D9F71726B4EE00F0B2D2 /* ATZProjectTemplate.m in Sources */,
Expand Down
10 changes: 5 additions & 5 deletions Alcatraz/ATZPluginWindowController.xib
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6254" systemVersion="14B25" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6751" systemVersion="14C1514" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="6254"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="6751"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="ATZPluginWindowController">
Expand All @@ -23,7 +23,7 @@
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES" unifiedTitleAndToolbar="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="196" y="240" width="545" height="449"/>
<rect key="screenRect" x="0.0" y="0.0" width="1280" height="777"/>
<rect key="screenRect" x="0.0" y="0.0" width="1440" height="877"/>
<view key="contentView" id="EiT-Mj-1SZ">
<rect key="frame" x="0.0" y="0.0" width="545" height="449"/>
<autoresizingMask key="autoresizingMask"/>
Expand Down Expand Up @@ -228,14 +228,14 @@
<windowStyleMask key="styleMask" titled="YES" closable="YES" resizable="YES" utility="YES" HUD="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="139" y="81" width="276" height="378"/>
<rect key="screenRect" x="0.0" y="0.0" width="1280" height="777"/>
<rect key="screenRect" x="0.0" y="0.0" width="1440" height="877"/>
<view key="contentView" id="WQe-dv-pdE">
<rect key="frame" x="0.0" y="0.0" width="276" height="378"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="47h-q5-ax2">
<rect key="frame" x="0.0" y="0.0" width="276" height="378"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" id="Pbz-gX-fEe"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" animates="YES" imageScaling="proportionallyDown" id="Pbz-gX-fEe"/>
</imageView>
</subviews>
<constraints>
Expand Down
3 changes: 3 additions & 0 deletions Alcatraz/Controllers/ATZPackageTableViewDelegate.h
Expand Up @@ -31,4 +31,7 @@
- (void)filterUsingPredicate:(NSPredicate*)predicate;

@property (nonatomic, strong, readonly) NSArray* packages;

- (void)loadImagesForVisibleRowsInTableView:(NSTableView *)tableView;

@end
98 changes: 19 additions & 79 deletions Alcatraz/Controllers/ATZPackageTableViewDelegate.m
Expand Up @@ -32,6 +32,7 @@
#import "ATZColorScheme.h"
#import "ATZDownloader.h"
#import "NSColor+Alcatraz.h"
#import "ATZScreenshotsStorage.h"

@interface ATZPackageTableViewDelegate ()

Expand Down Expand Up @@ -85,23 +86,12 @@ - (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn
[view.descriptionField setStringValue:package.summary];
[view.linkButton setImage:[self tableView:tableView websiteImageForTableColumn:tableColumn row:row]];
[view.linkButton setTitle:[self tableView:tableView displayWebsiteForTableColumn:tableColumn row:row]];
[view.typeImageView setImage:[self tableView:tableView packageTypeImageForTableColumn:tableColumn row:row]];
[view.installButton setTitle:([package isInstalled] ? @"REMOVE" : @"INSTALL")];
BOOL hasImage = package.screenshotPath != nil;
[view.previewButton setFullSize:hasImage];
[view.previewButton setHidden:!hasImage];
if (package.screenshotPath) {
[view.previewButton setImage:[[self class] cachedImageForPackage:package]];
if (!view.previewButton.image) {
__block NSButton* imageButton = view.previewButton;
[[self class] fetchAndCacheImageForPackage:package progress:NULL completion:^(NSImage *image) {
imageButton.image = image;
[tableView noteHeightOfRowsWithIndexesChanged:[NSIndexSet indexSetWithIndex:row]];
}];
}
} else {
[view.previewButton setImage:nil];
}
view.screenshotPath = package.screenshotPath;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Definitely should keep indentation consistent with the project; soft tabs, 4 spaces per tab.

view.screenshotImageView.image = [ATZScreenshotsStorage cachedImageForPackage:package];
ATZFillableButton* installButton = (ATZFillableButton*)view.installButton;
[installButton setButtonBorderStyle:ATZFillableButtonTypeInstall];
[installButton setFillRatio:([package isInstalled] ? 100 : 0) animated:NO];
Expand Down Expand Up @@ -156,73 +146,23 @@ - (NSImage *)tableView:(NSTableView*)tableView websiteImageForTableColumn:(NSTab
return [[Alcatraz sharedPlugin].bundle imageForResource:websiteImageName];
}

- (NSImage *)tableView:(NSTableView*)tableView packageTypeImageForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
NSString* typeImageName = nil;
ATZPackage* package = [self tableView:tableView objectValueForTableColumn:tableColumn row:row];
if ([package isKindOfClass:[ATZPlugin class]]) {
typeImageName = @"740-gear";
} else if ([package isKindOfClass:[ATZTemplate class]]) {
typeImageName = @"808-documents";
} else if ([package isKindOfClass:[ATZColorScheme class]]) {
typeImageName = @"837-palette";
} else {
return nil;
}
if ([package isInstalled])
typeImageName = [NSString stringWithFormat:@"%@-selected", typeImageName];

return [self selectionTemplateImageNamed:typeImageName];
- (void)loadImagesForVisibleRowsInTableView:(NSTableView *)tableView {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactor here appreciated (try to have max 3 levels of indentation)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh...actually I deleted these lines, as they were unused 😮
I don't like to keep dead code in project.

NSRange range = [tableView rowsInRect:tableView.visibleRect];
for (NSInteger row = range.location; row < range.location + range.length; ++row) {
ATZPackageListTableCellView *view = [tableView viewAtColumn:0 row:row makeIfNecessary:NO];
ATZPackage *package = self.filteredPackages[row];

if (package.screenshotPath && !view.screenshotImageView.image) {
NSImageView __weak *imageView = view.screenshotImageView;
[ATZScreenshotsStorage fetchAndCacheImageForPackage:package progress:NULL completion:^(ATZPackage *pkg, NSImage *image) {
if ([view.titleField.stringValue isEqualToString:pkg.name]) {
imageView.image = image;
[tableView noteHeightOfRowsWithIndexesChanged:[NSIndexSet indexSetWithIndex:row]];
}
}];
}
}
}

- (NSImage *)selectionTemplateImageNamed:(NSString *)imageName {
NSImage *template = [[Alcatraz sharedPlugin].bundle imageForResource:imageName];
NSSize size = [template size];

NSImage *copiedImage = [template copy];
[copiedImage setTemplate:NO];
[copiedImage lockFocus];
[[NSColor selectedItemColor] set];
NSRectFillUsingOperation(NSMakeRect(0, 0, size.width, size.height), NSCompositeSourceAtop);
[copiedImage unlockFocus];

return copiedImage;
}

#pragma mark - Image Previews

+ (NSCache *)imageCache {
static NSCache* cache = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
cache = [[NSCache alloc] init];
});
return cache;
}

+ (NSImage *)cachedImageForPackage:(ATZPackage*)package {
return [[self imageCache] objectForKey:package.name];
}

+ (void)cacheImage:(NSImage *)image forPackage:(ATZPackage *)package {
[[self imageCache] setObject:image forKey:package.name];
}

+ (void)fetchAndCacheImageForPackage:(ATZPackage*)package progress:(void(^)(CGFloat))progress completion:(void(^)(NSImage *))completion {
ATZDownloader *downloader = [ATZDownloader new];
[downloader downloadFileFromPath:package.screenshotPath
progress:progress
completion:^(NSData *responseData, NSError *error) {
if (error)
return;

NSImage *image = [[NSImage alloc] initWithData:responseData];
if (!image)
return;

[self cacheImage:image forPackage:package];
if (completion)
completion(image);
}];
}

@end
45 changes: 34 additions & 11 deletions Alcatraz/Controllers/ATZPluginWindowController.m
@@ -1,5 +1,5 @@
// PluginWindowController.m
//
//
// Copyright (c) 2014 Marin Usalj | supermar.in
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
Expand All @@ -8,10 +8,10 @@
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
Expand Down Expand Up @@ -51,10 +51,16 @@ typedef NS_ENUM(NSInteger, ATZFilterSegment) {
@interface ATZPluginWindowController ()
@property (nonatomic, assign) NSView *hoverButtonsContainer;
@property (nonatomic, strong) ATZPackageTableViewDelegate* tableViewDelegate;
@property (nonatomic, strong) NSTimer *loadImagesTimer;

@end

@implementation ATZPluginWindowController

- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}

- (id)init {
@throw [NSException exceptionWithName:@"There's a better initializer" reason:@"Use -initWithNibName:inBundle:" userInfo:nil];
}
Expand All @@ -73,8 +79,17 @@ - (id)initWithBundle:(NSBundle *)bundle {
- (void)windowDidLoad {
[super windowDidLoad];
[self addVersionToWindow];
if ([self.window respondsToSelector:@selector(setTitleVisibility:)])

typeof(self) __weak wSelf = self;
[[NSNotificationCenter defaultCenter] addObserverForName:NSScrollViewDidLiveScrollNotification object:self.tableView.enclosingScrollView queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
[wSelf.loadImagesTimer invalidate];
wSelf.loadImagesTimer = [NSTimer timerWithTimeInterval:0.2 target:wSelf selector:@selector(loadImagesInVisibleRows) userInfo:nil repeats:NO];
[[NSRunLoop mainRunLoop] addTimer:self.loadImagesTimer forMode:NSRunLoopCommonModes];
}];

if ([self.window respondsToSelector:@selector(setTitleVisibility:)]) {
self.window.titleVisibility = NSWindowTitleHidden;
}
}

- (void)userNotificationCenter:(NSUserNotificationCenter *)center didActivateNotification:(NSUserNotification *)notification {
Expand All @@ -89,7 +104,7 @@ - (BOOL)userNotificationCenter:(NSUserNotificationCenter *)center shouldPresentN

- (IBAction)installPressed:(ATZFillableButton *)button {
ATZPackage *package = [self.tableViewDelegate tableView:self.tableView objectValueForTableColumn:0 row:[self.tableView rowForView:button]];

if (package.isInstalled)
[self removePackage:package andUpdateControl:button];
else
Expand Down Expand Up @@ -174,7 +189,6 @@ - (void)reloadTableView {
self.tableView.dataSource = self.tableViewDelegate;
[self.tableViewDelegate configureTableView:self.tableView];
[self updatePredicate];
[self.tableView reloadData];
}

#pragma mark - Private
Expand Down Expand Up @@ -209,7 +223,7 @@ - (void)installPackage:(ATZPackage *)package andUpdateControl:(ATZFillableButton

- (void)postNotificationForInstalledPackage:(ATZPackage *)package {
if (![NSUserNotificationCenter class] || !package.isInstalled) return;

NSUserNotification *notification = [NSUserNotification new];
notification.title = [NSString stringWithFormat:@"%@ installed", package.type];
NSString *restartText = package.requiresRestart ? @" Please restart Xcode to use it." : @"";
Expand Down Expand Up @@ -237,6 +251,11 @@ - (void)updatePredicate {

[self.tableViewDelegate filterUsingPredicate:[NSCompoundPredicate andPredicateWithSubpredicates:predicates]];
[self.tableView reloadData];

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// wait next run loop, otherwise there are no rows yet
[self loadImagesInVisibleRows];
});
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this an indication that something somewhere else is going wrong?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cells will be visible only in the next runloop, so this is a solution I know. If you can recommend something better - I'd appreciate that :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why load the table and then fetch images instead of loading the images when the cell is loaded? There's a bit more delay before all the images have been cached.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because I don't load images when cell loaded, only after slight delay when scroll ends. So if I scroll quickly through list, I wont get all them in loading queue.
Actually this part of code loads images in first visible cells after changing filtering predicate, because scroll event wont be fired at this point.

}

- (void)updatePackages {
Expand All @@ -250,7 +269,7 @@ - (void)openWebsite:(NSString *)address {
}

- (void)displayScreenshotWithPath:(NSString *)screenshotPath withTitle:(NSString *)title {

[self.previewPanel.animator setAlphaValue:0.f];
self.previewPanel.title = title;
[self retrieveImageViewForScreenshot:screenshotPath
Expand All @@ -276,22 +295,26 @@ - (void)displayImage:(NSImage *)image withTitle:(NSString*)title {
}

- (void)retrieveImageViewForScreenshot:(NSString *)screenshotPath progress:(void (^)(CGFloat))downloadProgress completion:(void (^)(NSImage *))completion {

ATZDownloader *downloader = [ATZDownloader new];
[downloader downloadFileFromPath:screenshotPath
progress:^(CGFloat progress) {
downloadProgress(progress);
}
completion:^(NSData *responseData, NSError *error) {

NSImage *image = [[NSImage alloc] initWithData:responseData];
completion(image);
}];

}

- (void)addVersionToWindow {
self.versionTextField.stringValue = @(ATZ_VERSION);
}

- (void)loadImagesInVisibleRows {
[self.tableViewDelegate loadImagesForVisibleRowsInTableView:self.tableView];
}

@end
21 changes: 21 additions & 0 deletions Alcatraz/Helpers/ATZScreenshotsStorage.h
@@ -0,0 +1,21 @@
//
// ATZScreenshotsStorage.h
// Alcatraz
//
// Created by Alex Antonyuk on 3/20/15.
// Copyright (c) 2015 supermar.in. All rights reserved.
//

#import <Foundation/Foundation.h>

@class ATZPackage;

typedef void(^ATZImagesStorageCompletion)(ATZPackage *pkg, NSImage *image);

@interface ATZScreenshotsStorage : NSObject

+ (NSImage *)cachedImageForPackage:(ATZPackage*)package;
+ (void)cacheImage:(NSImage *)image forPackage:(ATZPackage *)package;
+ (void)fetchAndCacheImageForPackage:(ATZPackage*)package progress:(void(^)(CGFloat))progress completion:(ATZImagesStorageCompletion)completion;

@end