Added copy button to attribute editor to remove need for in-view drawing in NSTextField

This commit is contained in:
Michael Starke
2022-03-08 21:07:05 +01:00
parent 0572431a51
commit cea10e83b1
9 changed files with 131 additions and 46 deletions

View File

@@ -257,6 +257,7 @@
4CC59C2721AF0893005E8D6B /* MPPathControl.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CC59C2621AF0893005E8D6B /* MPPathControl.m */; };
4CC663E7216F7A7100E33965 /* MPPluginRepositoryBrowserViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CC663E5216F7A7100E33965 /* MPPluginRepositoryBrowserViewController.m */; };
4CC6DB7A17D23719002C6091 /* KPKNode+IconImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CC6DB7917D23719002C6091 /* KPKNode+IconImage.m */; };
4CC91B6B27D7E7E6001E9517 /* MPInspectorEditorView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CC91B6A27D7E7E6001E9517 /* MPInspectorEditorView.m */; };
4CCA8E9B18D91ED9001A6754 /* Quartz.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CCA8E9A18D91ED9001A6754 /* Quartz.framework */; };
4CCCE8011D75CA48006AA951 /* MPArrayController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CCCE8001D75CA48006AA951 /* MPArrayController.m */; };
4CCEDE2A179F203B008402BE /* MPOutlineView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CCEDE29179F203B008402BE /* MPOutlineView.m */; };
@@ -848,6 +849,8 @@
4CC663E5216F7A7100E33965 /* MPPluginRepositoryBrowserViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MPPluginRepositoryBrowserViewController.m; sourceTree = "<group>"; };
4CC6DB7817D23719002C6091 /* KPKNode+IconImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "KPKNode+IconImage.h"; sourceTree = "<group>"; };
4CC6DB7917D23719002C6091 /* KPKNode+IconImage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "KPKNode+IconImage.m"; sourceTree = "<group>"; };
4CC91B6927D7E7E6001E9517 /* MPInspectorEditorView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MPInspectorEditorView.h; sourceTree = "<group>"; };
4CC91B6A27D7E7E6001E9517 /* MPInspectorEditorView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MPInspectorEditorView.m; sourceTree = "<group>"; };
4CCA7EEC1797866F00B0B55E /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/GeneralPreferences.strings; sourceTree = "<group>"; };
4CCA8E9A18D91ED9001A6754 /* Quartz.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Quartz.framework; path = System/Library/Frameworks/Quartz.framework; sourceTree = SDKROOT; };
4CCCE7FF1D75CA48006AA951 /* MPArrayController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPArrayController.h; sourceTree = "<group>"; };
@@ -1275,6 +1278,8 @@
4CFC53BE16E94729007396BE /* MPShadowBox.m */,
4C4A100D176286FD00BBF2CA /* MPTableView.h */,
4C4A100E176286FD00BBF2CA /* MPTableView.m */,
4CC91B6927D7E7E6001E9517 /* MPInspectorEditorView.h */,
4CC91B6A27D7E7E6001E9517 /* MPInspectorEditorView.m */,
);
name = Views;
sourceTree = "<group>";
@@ -2266,6 +2271,7 @@
4CBA2ABA17074C07006D8139 /* MPSettingsHelper.m in Sources */,
4C77E37A15B84A240093A587 /* MPAppDelegate.m in Sources */,
3C0CDED821D28BF700B2A10B /* MPTouchBarButtonCreator.m in Sources */,
4CC91B6B27D7E7E6001E9517 /* MPInspectorEditorView.m in Sources */,
4C37A84015B8B474005EF8EE /* MPOutlineDataSource.m in Sources */,
4CA0B2F915BCAF6700654E32 /* MPGeneralPreferencesController.m in Sources */,
4C8F0C791FD05A6A00BE157F /* NSString+MPPrettyPasswordDisplay.m in Sources */,

View File

@@ -23,9 +23,9 @@ NS_ASSUME_NONNULL_BEGIN
@property (strong) IBOutlet HNHUISecureTextField *valueTextField;
@property (strong) IBOutlet NSButton *toggleProtectedButton;
@property (strong) IBOutlet NSButton *removeButton;
@property (strong) IBOutlet NSButton *actionButton;
- (void)updateValues;
- (void)updateEditing;
- (void)updateValuesAndEditing;
@end

View File

@@ -10,6 +10,7 @@
#import <HNHUi/HNHUi.h>
#import <KeePassKit/KeePassKit.h>
#import "MPPasteBoardController.h"
#import "MPInspectorEditorView.h"
NSString *nameForDefaultKey(NSString *key) {
static NSDictionary *mapping;
@@ -51,6 +52,10 @@ NSString *nameForDefaultKey(NSString *key) {
- (void)viewDidLoad {
[super viewDidLoad];
[NSNotificationCenter.defaultCenter addObserver:self selector:@selector(_didEnterMouse:) name:MPInspectorEditorViewMouseEnteredNotification object:self.view];
[NSNotificationCenter.defaultCenter addObserver:self selector:@selector(_didExitMouse:) name:MPInspectorEditorViewMouseExitedNotification object:self.view];
NSString *placeHolder = NSLocalizedString(@"NONE", "Placeholder text for input fields if no entry or group is selected");
self.keyTextField.placeholderString = placeHolder;
self.valueTextField.placeholderString = placeHolder;
@@ -58,17 +63,11 @@ NSString *nameForDefaultKey(NSString *key) {
self.toggleProtectedButton.action = @selector(toggleDisplay:);
self.toggleProtectedButton.target = self.valueTextField;
[self updateValues];
[self updateEditing];
self.actionButton.action = @selector(_copyText:);
self.actionButton.target = self;
self.actionButton.hidden = YES;
__weak MPEntryAttributeViewController *welf = self;
self.valueTextField.buttonTitle = NSLocalizedString(@"COPY", "Button to copy the value of an Attribute");
self.valueTextField.buttonActionBlock = ^void(NSTextField *tf) {
NSText *text = [welf.view.window fieldEditor:NO forObject:welf.valueTextField];
if([text isKindOfClass:NSTextView.class]) {
[welf textField:welf.valueTextField textView:(NSTextView *)text performAction:@selector(copy:)];
}
};
[self updateValuesAndEditing];
}
- (KPKAttribute *)representedAttribute {
@@ -80,7 +79,7 @@ NSString *nameForDefaultKey(NSString *key) {
- (void)setIsEditor:(BOOL)isEditor {
_isEditor = isEditor;
[self updateEditing];
[self updateValuesAndEditing];
}
- (void)setRepresentedObject:(id)representedObject {
@@ -101,8 +100,14 @@ NSString *nameForDefaultKey(NSString *key) {
}
_isDefaultAttribute = self.representedAttribute.isDefault;
[self updateEditing];
[self updateValues];
[self updateValuesAndEditing];
}
- (void)_copyText:(id)sender {
NSText *text = [self.view.window fieldEditor:NO forObject:self.valueTextField];
if([text isKindOfClass:NSTextView.class]) {
[self textField:self.valueTextField textView:(NSTextView *)text performAction:@selector(copy:)];
}
}
- (BOOL)textField:(NSTextField *)textField textView:(NSTextView *)textView performAction:(SEL)action {
@@ -145,10 +150,11 @@ NSString *nameForDefaultKey(NSString *key) {
}
- (void)_didChangeAttribute:(NSNotification *)notification {
[self updateValues];
[self updateValuesAndEditing];
}
- (void)updateValues {
- (void)updateValuesAndEditing {
/* values */
self.view.hidden = self.isEditor ? NO : self.representedAttribute.value.length == 0;
NSString *localizedKey = nameForDefaultKey(self.representedAttribute.key);
@@ -162,15 +168,14 @@ NSString *nameForDefaultKey(NSString *key) {
self.valueTextField.stringValue = self.representedAttribute.value ? self.representedAttribute.value : @"";
self.valueTextField.showPassword = !self.representedAttribute.protect;
}
- (void)updateEditing {
self.view.hidden = self.isEditor ? NO : self.representedAttribute.value.length == 0;
/* editor */
self.keyTextField.editable = !_isDefaultAttribute && self.isEditor;
self.valueTextField.editable = self.isEditor;
self.keyTextField.selectable = YES;
self.valueTextField.selectable = YES;
self.toggleProtectedButton.hidden = _isDefaultAttribute;
self.removeButton.hidden = !self.isEditor ? YES : _isDefaultAttribute;
// set draws background first, since bezeld might have side effects
@@ -179,6 +184,14 @@ NSString *nameForDefaultKey(NSString *key) {
self.valueTextField.bezeled = self.isEditor;
}
- (void)_didEnterMouse:(NSNotification *)notification {
self.actionButton.hidden = self.isEditor;
}
- (void)_didExitMouse:(NSNotification *)notification {
self.actionButton.hidden = YES;
}
-(void)commitChanges {
if(!self.isEditor) {
// do not commit changes if we are no editor!

View File

@@ -8,6 +8,7 @@
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="MPEntryAttributeViewController">
<connections>
<outlet property="actionButton" destination="gab-tS-gBb" id="gvk-hn-Erc"/>
<outlet property="keyTextField" destination="m8q-FN-S8D" id="HzJ-cd-ifA"/>
<outlet property="removeButton" destination="Nmx-gC-8rG" id="eRy-l0-u0E"/>
<outlet property="toggleProtectedButton" destination="hAk-oD-dCj" id="js9-Hx-ycS"/>
@@ -17,7 +18,7 @@
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customView id="Hz6-mo-xeY">
<customView id="Hz6-mo-xeY" customClass="MPInspectorEditorView">
<rect key="frame" x="0.0" y="0.0" width="251" height="43"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
@@ -32,11 +33,11 @@
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<stackView distribution="fill" orientation="horizontal" alignment="top" spacing="5" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="LBt-0e-cUK">
<stackView distribution="fill" orientation="horizontal" alignment="centerY" spacing="5" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="LBt-0e-cUK">
<rect key="frame" x="0.0" y="0.0" width="251" height="21"/>
<subviews>
<textField horizontalHuggingPriority="249" verticalHuggingPriority="750" horizontalCompressionResistancePriority="249" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="HZM-H4-dB4" customClass="HNHUISecureTextField">
<rect key="frame" x="0.0" y="0.0" width="181" height="21"/>
<rect key="frame" x="0.0" y="0.0" width="132" height="21"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" truncatesLastVisibleLine="YES" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" drawsBackground="YES" usesSingleLineMode="YES" id="sO3-xr-VwO">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
@@ -47,29 +48,38 @@
</connections>
</textField>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="hAk-oD-dCj">
<rect key="frame" x="179" y="-6" width="44" height="32"/>
<rect key="frame" x="130" y="-6" width="44" height="32"/>
<buttonCell key="cell" type="push" bezelStyle="rounded" image="NSLockLockedTemplate" imagePosition="only" alignment="center" alternateImage="NSLockUnlockedTemplate" lineBreakMode="truncatingTail" borderStyle="border" inset="2" id="f80-K9-DO7">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES" changeBackground="YES" changeGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Nmx-gC-8rG">
<rect key="frame" x="214" y="-6" width="44" height="32"/>
<rect key="frame" x="165" y="-6" width="44" height="32"/>
<buttonCell key="cell" type="push" bezelStyle="rounded" image="NSRemoveTemplate" imagePosition="only" alignment="center" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="179-uk-89S">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="gab-tS-gBb">
<rect key="frame" x="201" y="-4" width="56" height="27"/>
<buttonCell key="cell" type="push" title="Copy" bezelStyle="rounded" alignment="center" controlSize="small" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="0GH-rx-Fg4">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="smallSystem"/>
</buttonCell>
</button>
</subviews>
<visibilityPriorities>
<integer value="1000"/>
<integer value="1000"/>
<integer value="1000"/>
<integer value="1000"/>
</visibilityPriorities>
<customSpacing>
<real value="3.4028234663852886e+38"/>
<real value="3.4028234663852886e+38"/>
<real value="3.4028234663852886e+38"/>
<real value="3.4028234663852886e+38"/>
</customSpacing>
</stackView>
</subviews>

View File

@@ -343,6 +343,7 @@ typedef NS_ENUM(NSUInteger, MPEntryTab) {
self.passwordEditorViewController.isEditor = !self.passwordEditorViewController.isEditor;
self.urlEditorViewController.isEditor = !self.urlEditorViewController.isEditor;
self.expiresEditorViewController.isEditor = !self.expiresEditorViewController.isEditor;
self.iconViewController.isEditor = !self.iconViewController.isEditor;
//self.totpViewController.isEditor = !self.totpViewController.isEditor;
}

View File

@@ -37,16 +37,13 @@
};
}
- (void)updateValues {
- (void)updateValuesAndEditing {
self.view.hidden = (!self.isEditor && self.representedAttribute.value.length == 0);
self.passwordTextField.stringValue = self.representedAttribute.value ? self.representedAttribute.value : @"";
}
- (void)updateEditing {
/* editor */
self.generatePasswordButton.hidden = !self.isEditor;
self.passwordTextField.editable = self.isEditor;
self.passwordTextField.selectable = YES;
}
@end

View File

@@ -0,0 +1,21 @@
//
// MPInspectorEditorView.h
// MacPass
//
// Created by Michael Starke on 08.03.22.
// Copyright © 2022 HicknHack Software GmbH. All rights reserved.
//
#import <Cocoa/Cocoa.h>
NS_ASSUME_NONNULL_BEGIN
FOUNDATION_EXPORT NSString *const MPInspectorEditorViewMouseEnteredNotification;
FOUNDATION_EXPORT NSString *const MPInspectorEditorViewMouseExitedNotification;
@interface MPInspectorEditorView : NSView
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,37 @@
//
// MPInspectorEditorView.m
// MacPass
//
// Created by Michael Starke on 08.03.22.
// Copyright © 2022 HicknHack Software GmbH. All rights reserved.
//
#import "MPInspectorEditorView.h"
NSString *const MPInspectorEditorViewMouseEnteredNotification = @"com.hicknhacksoftware.macpass.MPInspectorEditorViewMouseEnteredNotification";
NSString *const MPInspectorEditorViewMouseExitedNotification = @"com.hicknhacksoftware.macpass.MPInspectorEditorViewMouseExitedNotification";
@interface MPInspectorEditorView ()
@property (strong) NSTrackingArea *trackingArea;
@end
@implementation MPInspectorEditorView
- (void)mouseEntered:(NSEvent *)event {
[NSNotificationCenter.defaultCenter postNotificationName:MPInspectorEditorViewMouseEnteredNotification object:self];
}
- (void)mouseExited:(NSEvent *)event {
[NSNotificationCenter.defaultCenter postNotificationName:MPInspectorEditorViewMouseExitedNotification object:self];
}
- (void)updateTrackingAreas {
[super updateTrackingAreas];
[self removeTrackingArea:self.trackingArea];
self.trackingArea = [[NSTrackingArea alloc] initWithRect:self.bounds options:NSTrackingActiveAlways|NSTrackingMouseEnteredAndExited owner:self userInfo:nil];
[self addTrackingArea:self.trackingArea];
}
@end

View File

@@ -14,6 +14,8 @@
@interface MPNodeIconViewController ()
@property (strong) IBOutlet NSImageView *imageView;
@property (strong) IBOutlet NSTextField *textField;
@property (copy) NSUUID *iconUUID;
@property NSUInteger iconId;
@end
@implementation MPNodeIconViewController
@@ -27,7 +29,6 @@
}
- (void)setRepresentedObject:(id)representedObject {
// FIXME: register for correct notifications
if(self.representedNode) {
KPKNode *node = self.representedNode;
if(node.asEntry) {
@@ -57,7 +58,7 @@
NSLog(@"Inconsitant state for notification handling");
}
}
[self _updateValues];
[self _updateValueAndEditing];
}
- (KPKNode *)representedNode {
@@ -67,31 +68,30 @@
return nil;
}
- (void)_updateValues {
- (void)setIsEditor:(BOOL)isEditor {
_isEditor = isEditor;
[self _updateValueAndEditing];
}
- (void)_updateValueAndEditing {
self.imageView.enabled = self.isEditor;
self.iconUUID = self.representedNode.iconUUID;
self.iconId = self.representedNode.iconId;
self.imageView.image = self.representedNode.iconImage;
self.textField.stringValue = self.representedNode.title.length > 0 ? self.representedNode.title : @"";
}
- (void)commitChanges {
// fixme
self.representedNode.iconUUID = self.iconUUID;
self.representedNode.iconId = self.iconId;
}
- (void)_willChangeNode:(NSNotification *)notification {
}
- (void)_didChangeNode:(NSNotification *)notification {
[self _updateValues];
[self _updateValueAndEditing];
}
/*
- (BOOL)commitEditingAndReturnError:(NSError *__autoreleasing _Nullable * _Nullable)error {
<#code#>
}
- (void)encodeWithCoder:(nonnull NSCoder *)coder {
<#code#>
}
*/
@end