Skip to content

Allow dragging a folder on the Welcome window to open a repository - Closes #468 and #247 #469

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions GitUp/Application/AppDelegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,5 +47,6 @@
- (BOOL)repository:(GCRepository*)repository requiresPlainTextAuthenticationForURL:(NSURL*)url user:(NSString*)user username:(NSString**)username password:(NSString**)password;
- (void)repository:(GCRepository*)repository didFinishTransferWithURL:(NSURL*)url success:(BOOL)success;

- (void)openRepositoryWithURL:(NSURL*)url;
- (void)handleDocumentCountChanged;
@end
4 changes: 4 additions & 0 deletions GitUp/Application/AppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,10 @@ + (void)savePlainTextAuthenticationToKeychainForURL:(NSURL*)url withUsername:(NS
}
}

- (void)openRepositoryWithURL:(NSURL*)url {
[self _openRepositoryWithURL:url withCloneMode:kCloneMode_None windowModeID:NSNotFound];
}

- (void)_setDocumentWindowModeID:(NSArray*)arguments {
[(Document*)arguments[0] setWindowModeID:[arguments[1] unsignedIntegerValue]];
}
Expand Down
20 changes: 20 additions & 0 deletions GitUp/Application/DragAndDropImageView.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright (C) 2015-2018 Pierre-Olivier Latour <info@pol-online.net>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

#import <Cocoa/Cocoa.h>

@interface DragAndDropImageView : NSImageView <NSDraggingDestination>

@end
107 changes: 107 additions & 0 deletions GitUp/Application/DragAndDropImageView.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// Copyright (C) 2015-2018 Pierre-Olivier Latour <info@pol-online.net>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

#import "DragAndDropImageView.h"
#import "AppDelegate.h"

@interface DragAndDropImageView()
@property (nonatomic, assign) BOOL isHighlighted;
@end

@implementation DragAndDropImageView

- (void)awakeFromNib {
[super awakeFromNib];
[self registerForDraggedTypes:@[NSFilenamesPboardType]];
Copy link
Contributor

Choose a reason for hiding this comment

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

On modern macOS, drag support should be registered either using one of the non-deprecated pasteboard types (post-10.14) or a UTI. Here, use @[ (__bridge id)kUTTypeFileURL ] or NSURL.readableTypeIdentifiersForItemProvider, whichever you prefer.

}

- (void)dealloc {
[self unregisterDraggedTypes];
}

- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender {
BOOL isDragOperationGeneric = (NSDragOperationGeneric & [sender draggingSourceOperationMask]) == NSDragOperationGeneric;
NSPasteboard *pasteboard = [sender draggingPasteboard];
if (isDragOperationGeneric && [self directoryURLFromPasteboard:pasteboard]) {
Copy link
Contributor

Choose a reason for hiding this comment

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

On modern macOS, drag operations should be validated using the multi-contents support of -[NSPasteboard canReadObjectForClasses:options:].

Here, that would be:

[pasteboard canReadObjectForClasses:@[ NSURL.class ] options:@{
  NSPasteboardURLReadingFileURLsOnlyKey: @YES,
  NSPasteboardURLReadingContentsConformToTypesKey: @[
    (__bridge id)kUTTypeDirectory
  ]
}]

self.isHighlighted = YES;
return NSDragOperationGeneric;
} else {
return NSDragOperationNone;
}
}

- (void)draggingExited:(id<NSDraggingInfo>)sender {
self.isHighlighted = NO;
}

- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender {
NSPasteboard *pasteboard = [sender draggingPasteboard];
NSURL *directoryURL = [self directoryURLFromPasteboard:pasteboard];
Copy link
Contributor

Choose a reason for hiding this comment

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

On modern macOS, drag operations should be performed using the multi-contents support of -[NSPasteboard readObjectsForClasses:options:].

Here, that would be:

NSArray* urls = [pasteboard readObjectsForClasses:@[ NSURL.class ] options:@{
  NSPasteboardURLReadingFileURLsOnlyKey: @YES,
  NSPasteboardURLReadingContentsConformToTypesKey: @[
    (__bridge id)kUTTypeDirectory
  ]
}]

if (directoryURL) {
AppDelegate *appDelegate = (AppDelegate *)[[NSApplication sharedApplication] delegate];
[appDelegate openRepositoryWithURL:directoryURL];
return YES;
} else {
return NO;
}
}

- (void)concludeDragOperation:(id<NSDraggingInfo>)sender {
self.isHighlighted = NO;
}

- (void)drawRect:(NSRect)dirtyRect {
[super drawRect:dirtyRect];

if (self.isHighlighted) {
NSBezierPath *path = [NSBezierPath bezierPathWithRoundedRect:dirtyRect xRadius:10 yRadius:10];
Copy link
Contributor

Choose a reason for hiding this comment

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

dirtyRect can be called with subsections of self. You probably want to still use self.bounds.

// systemBlueColor from https://developer.apple.com/design/human-interface-guidelines/macos/visual-design/color/
NSColor *higlightColor = [NSColor colorWithRed:27.0/255.0

Choose a reason for hiding this comment

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

Typo in name, and also, perhaps better to use [NSColor highlightColor] or one of its siblings to have the color change appropriately with dark mode/user preferences?

Copy link
Contributor

Choose a reason for hiding this comment

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

controlAccentColor should be used when available before falling back to systemBlueColor. (systemBlueColor requires 10.10; see also #534.)

green:173.0/255.0
blue:248.0/255.0
alpha:0.2];
[higlightColor set];
[path fill];
}
}

#pragma mark -

// Returns first directory URL from the given pasteboard
- (NSURL *)directoryURLFromPasteboard:(NSPasteboard *)pasteboard {
NSString *desiredType = [pasteboard availableTypeFromArray:@[NSFilenamesPboardType]];
if (![desiredType isEqualToString:NSFilenamesPboardType]) {
return nil;
}
NSArray *filenames = [pasteboard propertyListForType:NSFilenamesPboardType];
NSURL *url = [NSURL fileURLWithPath:[filenames firstObject]];
NSFileManager *fileManager = [NSFileManager defaultManager];
BOOL isDirectory = NO;
if ([fileManager fileExistsAtPath:[url path] isDirectory:&isDirectory] && isDirectory) {
return url;
} else {
return nil;
}
}

- (void)setIsHighlighted:(BOOL)isHighlighted {
if (_isHighlighted != isHighlighted) {
_isHighlighted = isHighlighted;

[self setNeedsDisplay:YES];
}
}

@end
7 changes: 4 additions & 3 deletions GitUp/Application/en.lproj/MainMenu.xib
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
<?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" customObjectInstantitationMethod="direct">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14113" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" customObjectInstantitationMethod="direct">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14269.12"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14113"/>
<capability name="box content view" minToolsVersion="7.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
Expand Down Expand Up @@ -1125,7 +1126,7 @@ UI design by Wayne Fan</string>
<action selector="closeWelcomeWindow:" target="Voe-Tx-rLC" id="xWF-ok-YMZ"/>
</connections>
</button>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" id="Apd-kA-Fr2">
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" id="Apd-kA-Fr2" customClass="DragAndDropImageView">
<rect key="frame" x="76" y="180" width="128" height="128"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="NSApplicationIcon" id="hqR-yy-x5J"/>
Expand Down
6 changes: 6 additions & 0 deletions GitUp/GitUp.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
/* Begin PBXBuildFile section */
165C32B61B95739700D2F894 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 165C32B51B95739700D2F894 /* QuartzCore.framework */; };
31CD50E3203E2E2800360B3A /* ToolbarItemWrapperView.m in Sources */ = {isa = PBXBuildFile; fileRef = 31CD50E2203E2E2800360B3A /* ToolbarItemWrapperView.m */; };
B15B8C3620EC90B000E9312C /* DragAndDropImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = B15B8C3520EC90B000E9312C /* DragAndDropImageView.m */; };
E212A6DE1B92100E00F62B18 /* libiconv.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = E212A6DD1B92100E00F62B18 /* libiconv.dylib */; };
E21739F71A5080DD00EC6777 /* DocumentController.m in Sources */ = {isa = PBXBuildFile; fileRef = E21739F61A5080DD00EC6777 /* DocumentController.m */; };
E21DCAEB1B253847006424E8 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = E21DCAEA1B253847006424E8 /* main.m */; };
Expand Down Expand Up @@ -109,6 +110,8 @@
165C32B51B95739700D2F894 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
31CD50E1203E2E2800360B3A /* ToolbarItemWrapperView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ToolbarItemWrapperView.h; sourceTree = "<group>"; };
31CD50E2203E2E2800360B3A /* ToolbarItemWrapperView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ToolbarItemWrapperView.m; sourceTree = "<group>"; };
B15B8C3420EC90B000E9312C /* DragAndDropImageView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DragAndDropImageView.h; sourceTree = "<group>"; };
B15B8C3520EC90B000E9312C /* DragAndDropImageView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DragAndDropImageView.m; sourceTree = "<group>"; };
E212A6DD1B92100E00F62B18 /* libiconv.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libiconv.dylib; path = usr/lib/libiconv.dylib; sourceTree = SDKROOT; };
E21739F51A5080DD00EC6777 /* DocumentController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DocumentController.h; sourceTree = "<group>"; };
E21739F61A5080DD00EC6777 /* DocumentController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DocumentController.m; sourceTree = "<group>"; };
Expand Down Expand Up @@ -273,6 +276,8 @@
E2C5672C1A6D98BC00ECFE07 /* WindowController.m */,
31CD50E1203E2E2800360B3A /* ToolbarItemWrapperView.h */,
31CD50E2203E2E2800360B3A /* ToolbarItemWrapperView.m */,
B15B8C3420EC90B000E9312C /* DragAndDropImageView.h */,
B15B8C3520EC90B000E9312C /* DragAndDropImageView.m */,
);
path = Application;
sourceTree = "<group>";
Expand Down Expand Up @@ -426,6 +431,7 @@
E2C338B419F8562F00063D95 /* main.m in Sources */,
E2C5672D1A6D98BC00ECFE07 /* WindowController.m in Sources */,
E2C338B219F8562F00063D95 /* AppDelegate.m in Sources */,
B15B8C3620EC90B000E9312C /* DragAndDropImageView.m in Sources */,
E2C338B719F8562F00063D95 /* Document.m in Sources */,
31CD50E3203E2E2800360B3A /* ToolbarItemWrapperView.m in Sources */,
);
Expand Down