diff --git a/Organismo.xcodeproj/project.pbxproj b/Organismo.xcodeproj/project.pbxproj index a0c7685..a11418c 100644 --- a/Organismo.xcodeproj/project.pbxproj +++ b/Organismo.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + 03007BB1232A1C3700DAF421 /* ORGElementPropertiesView.h in Headers */ = {isa = PBXBuildFile; fileRef = 03007BAF232A1C3700DAF421 /* ORGElementPropertiesView.h */; }; + 03007BB2232A1C3700DAF421 /* ORGElementPropertiesView.m in Sources */ = {isa = PBXBuildFile; fileRef = 03007BB0232A1C3700DAF421 /* ORGElementPropertiesView.m */; }; 0300F9D922EECA73007DF323 /* ORGUITreeNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 0300F9D722EECA73007DF323 /* ORGUITreeNode.m */; }; 0300F9DA22EECA73007DF323 /* ORGUITreeNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 0300F9D822EECA73007DF323 /* ORGUITreeNode.h */; }; 031F745F1D4015F400DDC876 /* UITextView+ORG.h in Headers */ = {isa = PBXBuildFile; fileRef = 031F745D1D4015F400DDC876 /* UITextView+ORG.h */; }; @@ -85,6 +87,8 @@ 03A06CC92033752D00751692 /* ORGRequestClassHierarchy.h in Headers */ = {isa = PBXBuildFile; fileRef = 03A06CC72033752D00751692 /* ORGRequestClassHierarchy.h */; }; 03A06CCA2033752D00751692 /* ORGRequestClassHierarchy.m in Sources */ = {isa = PBXBuildFile; fileRef = 03A06CC82033752D00751692 /* ORGRequestClassHierarchy.m */; }; 03A89A6F1D92CCE7000AB035 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 033677F31D3E6974009B86A4 /* UIKit.framework */; }; + 03AF319D232ABE2E00437482 /* ORGElementClassView.h in Headers */ = {isa = PBXBuildFile; fileRef = 03AF319B232ABE2E00437482 /* ORGElementClassView.h */; }; + 03AF319E232ABE2E00437482 /* ORGElementClassView.m in Sources */ = {isa = PBXBuildFile; fileRef = 03AF319C232ABE2E00437482 /* ORGElementClassView.m */; }; 03B588B21D04082D0083DF15 /* NSDate+ORG.h in Headers */ = {isa = PBXBuildFile; fileRef = 03B588B01D04082D0083DF15 /* NSDate+ORG.h */; }; 03B588B31D04082D0083DF15 /* NSDate+ORG.m in Sources */ = {isa = PBXBuildFile; fileRef = 03B588B11D04082D0083DF15 /* NSDate+ORG.m */; }; 03C2CD001E9395DB00508AD9 /* ORGRequestLongPress.h in Headers */ = {isa = PBXBuildFile; fileRef = 03C2CCFE1E9395DB00508AD9 /* ORGRequestLongPress.h */; }; @@ -193,6 +197,8 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 03007BAF232A1C3700DAF421 /* ORGElementPropertiesView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ORGElementPropertiesView.h; path = Organismo/ORGElementPropertiesView.h; sourceTree = ""; }; + 03007BB0232A1C3700DAF421 /* ORGElementPropertiesView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = ORGElementPropertiesView.m; path = Organismo/ORGElementPropertiesView.m; sourceTree = ""; }; 0300F9D722EECA73007DF323 /* ORGUITreeNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ORGUITreeNode.m; path = Organismo/Mac/ORGUITreeNode.m; sourceTree = ""; }; 0300F9D822EECA73007DF323 /* ORGUITreeNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ORGUITreeNode.h; path = Organismo/Mac/ORGUITreeNode.h; sourceTree = ""; }; 0300F9DD22EECB56007DF323 /* mac-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "mac-Info.plist"; path = "Organismo/Mac/mac-Info.plist"; sourceTree = ""; }; @@ -271,6 +277,8 @@ 039DA6D71D8EBC3000F6937A /* ORGRequestTap.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ORGRequestTap.m; sourceTree = ""; }; 03A06CC72033752D00751692 /* ORGRequestClassHierarchy.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ORGRequestClassHierarchy.h; sourceTree = ""; }; 03A06CC82033752D00751692 /* ORGRequestClassHierarchy.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ORGRequestClassHierarchy.m; sourceTree = ""; }; + 03AF319B232ABE2E00437482 /* ORGElementClassView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ORGElementClassView.h; path = Organismo/Mac/ORGElementClassView.h; sourceTree = ""; }; + 03AF319C232ABE2E00437482 /* ORGElementClassView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = ORGElementClassView.m; path = Organismo/Mac/ORGElementClassView.m; sourceTree = ""; }; 03B588B01D04082D0083DF15 /* NSDate+ORG.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDate+ORG.h"; sourceTree = ""; }; 03B588B11D04082D0083DF15 /* NSDate+ORG.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDate+ORG.m"; sourceTree = ""; }; 03C2CCFE1E9395DB00508AD9 /* ORGRequestLongPress.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ORGRequestLongPress.h; sourceTree = ""; }; @@ -450,10 +458,14 @@ 034B1B6422CFA7F300CFEDD0 /* ORGUITree.m */, 0300F9D822EECA73007DF323 /* ORGUITreeNode.h */, 0300F9D722EECA73007DF323 /* ORGUITreeNode.m */, - 0394907722D335BE0029BB9B /* ORGInspectorWindowController.h */, - 0394907822D335BE0029BB9B /* ORGInspectorWindowController.m */, 0394907F22D351790029BB9B /* ORGTableCellView.h */, 0394908022D351790029BB9B /* ORGTableCellView.m */, + 0394907722D335BE0029BB9B /* ORGInspectorWindowController.h */, + 0394907822D335BE0029BB9B /* ORGInspectorWindowController.m */, + 03007BAF232A1C3700DAF421 /* ORGElementPropertiesView.h */, + 03007BB0232A1C3700DAF421 /* ORGElementPropertiesView.m */, + 03AF319B232ABE2E00437482 /* ORGElementClassView.h */, + 03AF319C232ABE2E00437482 /* ORGElementClassView.m */, 033DCF9C22D4D69A00295555 /* Categories */, 034B1B6222CE4FA000CFEDD0 /* Resources */, 0300F9DC22EECB40007DF323 /* Supporting Files */, @@ -783,10 +795,12 @@ 039C338C22C7D63600E61DA8 /* Organismo.h in Headers */, 032C9FE822C8CC2600B2DBEA /* OrganismoPrefixHeader-mac.pch in Headers */, 033DCF9F22D4D6C800295555 /* NSWindow+ORG.h in Headers */, + 03AF319D232ABE2E00437482 /* ORGElementClassView.h in Headers */, 0300F9DA22EECA73007DF323 /* ORGUITreeNode.h in Headers */, 0394908122D351790029BB9B /* ORGTableCellView.h in Headers */, 034B1B6022CE4F9A00CFEDD0 /* ORGNSViewHierarchy.h in Headers */, 0394907A22D335BE0029BB9B /* ORGInspectorWindowController.h in Headers */, + 03007BB1232A1C3700DAF421 /* ORGElementPropertiesView.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -943,6 +957,7 @@ TargetAttributes = { 039C32F422C7D63600E61DA8 = { DevelopmentTeam = NRRF7234ZC; + LastSwiftMigration = 1100; ProvisioningStyle = Automatic; }; 03DA66861CBCEE4700E26CAA = { @@ -1095,9 +1110,11 @@ 033DCFA422D4D6EA00295555 /* NSControl+ORG.m in Sources */, 034B1B6622CFA7F300CFEDD0 /* ORGUITree.m in Sources */, 0394907B22D335BE0029BB9B /* ORGInspectorWindowController.m in Sources */, + 03AF319E232ABE2E00437482 /* ORGElementClassView.m in Sources */, 034B1B6122CE4F9A00CFEDD0 /* ORGNSViewHierarchy.m in Sources */, 033DCFA822D4D70200295555 /* NSButton+ORG.m in Sources */, 032C9FE622C8CA2700B2DBEA /* NSApplication+ORG.m in Sources */, + 03007BB2232A1C3700DAF421 /* ORGElementPropertiesView.m in Sources */, 033DCFA022D4D6C800295555 /* NSWindow+ORG.m in Sources */, 0394908222D351790029BB9B /* ORGTableCellView.m in Sources */, ); @@ -1203,6 +1220,7 @@ 039C339222C7D63600E61DA8 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = "Mac Developer"; CODE_SIGN_STYLE = Automatic; DEFINES_MODULE = YES; @@ -1241,6 +1259,8 @@ PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 0; }; name = Debug; @@ -1248,6 +1268,7 @@ 039C339322C7D63600E61DA8 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = "Mac Developer"; CODE_SIGN_STYLE = Automatic; DEFINES_MODULE = YES; @@ -1286,6 +1307,7 @@ PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 0; }; name = Release; diff --git a/Organismo.xcodeproj/xcshareddata/xcschemes/Organismo-iOS.xcscheme b/Organismo.xcodeproj/xcshareddata/xcschemes/Organismo-iOS.xcscheme new file mode 100644 index 0000000..b92de6e --- /dev/null +++ b/Organismo.xcodeproj/xcshareddata/xcschemes/Organismo-iOS.xcscheme @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Organismo/Mac/Media.xcassets/NSScrollView_16_Normal.imageset/Contents.json b/Organismo/Mac/Media.xcassets/NSScrollView_16_Normal.imageset/Contents.json new file mode 100644 index 0000000..ec99c64 --- /dev/null +++ b/Organismo/Mac/Media.xcassets/NSScrollView_16_Normal.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "NSScrollView.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "NSScrollView-1.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "NSScrollView-2.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Organismo/Mac/Media.xcassets/NSScrollView_16_Normal.imageset/NSScrollView-1.png b/Organismo/Mac/Media.xcassets/NSScrollView_16_Normal.imageset/NSScrollView-1.png new file mode 100644 index 0000000..015c526 Binary files /dev/null and b/Organismo/Mac/Media.xcassets/NSScrollView_16_Normal.imageset/NSScrollView-1.png differ diff --git a/Organismo/Mac/Media.xcassets/NSScrollView_16_Normal.imageset/NSScrollView-2.png b/Organismo/Mac/Media.xcassets/NSScrollView_16_Normal.imageset/NSScrollView-2.png new file mode 100644 index 0000000..015c526 Binary files /dev/null and b/Organismo/Mac/Media.xcassets/NSScrollView_16_Normal.imageset/NSScrollView-2.png differ diff --git a/Organismo/Mac/Media.xcassets/NSScrollView_16_Normal.imageset/NSScrollView.png b/Organismo/Mac/Media.xcassets/NSScrollView_16_Normal.imageset/NSScrollView.png new file mode 100644 index 0000000..015c526 Binary files /dev/null and b/Organismo/Mac/Media.xcassets/NSScrollView_16_Normal.imageset/NSScrollView.png differ diff --git a/Organismo/Mac/ORGElementClassView.h b/Organismo/Mac/ORGElementClassView.h new file mode 100644 index 0000000..31d3f27 --- /dev/null +++ b/Organismo/Mac/ORGElementClassView.h @@ -0,0 +1,21 @@ +// +// ORGElementClassView.h +// Organismo-mac +// +// Created by Jon Gabilondo on 12/09/2019. +// Copyright © 2019 organismo-mobile. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@class ORGUITreeNode; + +@interface ORGElementClassView : NSView + +- (void)showElement:(ORGUITreeNode*)node; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Organismo/Mac/ORGElementClassView.m b/Organismo/Mac/ORGElementClassView.m new file mode 100644 index 0000000..9923895 --- /dev/null +++ b/Organismo/Mac/ORGElementClassView.m @@ -0,0 +1,72 @@ +// +// ORGElementClassView.m +// Organismo-mac +// +// Created by Jon Gabilondo on 12/09/2019. +// Copyright © 2019 organismo-mobile. All rights reserved. +// + +#import "ORGElementClassView.h" +#import "ORGUITreeNode.h" +#import + +@interface ORGElementClassView() + +@property (weak) IBOutlet NSTableView *methodsTableView; +@property (strong) IBOutlet NSArrayController *methodsArrayController; + +@property (nonatomic) ORGUITreeNode *node; +@property (nonatomic) NSMutableArray *methods; +@property (nonatomic) NSArray *classHierarchy; + +- (NSArray*)buildClassHierarchy:(Class)class; +- (void)showClass:(Class)class; +@end + +@implementation ORGElementClassView + +- (void)showElement:(ORGUITreeNode*)node { + + self.node = node; + self.classHierarchy = [self buildClassHierarchy:node.uiElement.class]; + [self showClass:node.uiElement.class]; +} + +- (NSArray*)buildClassHierarchy:(Class)class { + NSMutableArray *hierarchy = [NSMutableArray array]; + for ( Class currentClass = class; currentClass; currentClass = currentClass.superclass) { + [hierarchy addObject:NSStringFromClass(currentClass)]; + } + return hierarchy; +} + +- (IBAction)hierarchyPathChanged:(NSPopUpButton *)sender { + + NSString *className = self.classHierarchy[sender.indexOfSelectedItem]; + if (!className) { + return; + } + [self showClass:NSClassFromString(className)]; +} + +- (void)showClass:(Class)class { + + if (self.methods) { + [self.methodsArrayController removeObjects:self.methods]; + } else { + self.methods = [NSMutableArray array]; + self.methodsArrayController.content = self.methods; + } + + unsigned int classCount; + Method *methodsList = class_copyMethodList(class, &classCount); + for (unsigned int i = 0; i < classCount; i++) { + SEL selector = method_getName(methodsList[i]); + [self.methodsArrayController addObject:NSStringFromSelector(selector)]; + } + free(methodsList); + + [self.methodsTableView reloadData]; +} + +@end diff --git a/Organismo/Mac/ORGInspectorWindowController.h b/Organismo/Mac/ORGInspectorWindowController.h index 1e2e1c5..04205ad 100644 --- a/Organismo/Mac/ORGInspectorWindowController.h +++ b/Organismo/Mac/ORGInspectorWindowController.h @@ -10,8 +10,15 @@ NS_ASSUME_NONNULL_BEGIN +@class ORGUITreeNode; + @interface ORGInspectorWindowController : NSWindowController +@property (nonatomic) NSString *classHierarchy; +@property (nonatomic, nullable) ORGUITreeNode *selectedNode; +@property (strong) IBOutlet NSArrayController *methodsArrayController; + + @end NS_ASSUME_NONNULL_END diff --git a/Organismo/Mac/ORGInspectorWindowController.m b/Organismo/Mac/ORGInspectorWindowController.m index 909aa71..02bf366 100644 --- a/Organismo/Mac/ORGInspectorWindowController.m +++ b/Organismo/Mac/ORGInspectorWindowController.m @@ -7,14 +7,22 @@ // #import "ORGInspectorWindowController.h" +#import "ORGElementPropertiesView.h" +#import "ORGElementClassView.h" #import "ORGUITree.h" #import "ORGUITreeNode.h" #import "ORGNSViewHierarchy.h" +#import "ORGTableCellView.h" +#import @interface ORGInspectorWindowController () +@property (weak) IBOutlet NSTabView *tabView; @property (weak) IBOutlet NSOutlineView *outlineView; -@property (strong) ORGUITree *tree; +@property (weak) IBOutlet ORGElementPropertiesView *propertiesView; +@property (nonatomic) ORGUITree *tree; +@property (nonatomic) CAShapeLayer *highlightLayer; +@property (weak) IBOutlet ORGElementClassView *classView; @end @@ -39,6 +47,55 @@ - (void)windowDidLoad { } [self.outlineView reloadData]; + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(highlightItem:) name:@"HIGHLIGHT-ELEMENT" object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(removeHighlight:) name:@"REMOVE-HIGHLIGHT" object:nil]; + + self.classHierarchy = @"NSObject"; + //[self.classHierarchy addObjectsFromArray:@[@"NSObject", @"NSObject", @"NSObject", @"NSObject"]]; +} + +- (void)removeHighlight:(NSNotification*)notitication { + if (_highlightLayer) { + [_highlightLayer removeFromSuperlayer]; + _highlightLayer = nil; + } +} + +- (void)highlightItem:(NSNotification*)notitication { + NSView *view; + + ORGTableCellView *cell = notitication.object; + NSAssert(cell, @"Missing object in notification."); + ORGUITreeNode *node = cell.treeNode; + NSAssert(node, @"Missing object in notification."); + + if ([node.uiElement isKindOfClass:NSWindow.class]) { + NSWindow *window = (NSWindow*)node.uiElement; + if (window.visible) { + view = window.contentView; + } + } else { + view = (NSView*)node.uiElement; + if (view.hidden) { + view = nil; + } + } + + if (view) { + _highlightLayer = [CAShapeLayer layer]; + _highlightLayer.bounds = view.bounds; + _highlightLayer.lineWidth = 4; + _highlightLayer.fillColor = CGColorCreateGenericRGB(0.0, 0.0, 0.0, 0.0); + _highlightLayer.strokeColor = CGColorCreateGenericRGB(1.0, 0.5, 0.5, 1.0); + _highlightLayer.anchorPoint = CGPointMake(0.0f, 0.0f); + + CGMutablePathRef path = CGPathCreateMutable(); + CGPathAddRect(path, nil, _highlightLayer.bounds); + _highlightLayer.path = path; + [view.layer addSublayer:_highlightLayer]; + [_highlightLayer display]; + } } #pragma mark - NSWindowDelegate @@ -54,7 +111,7 @@ - (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id return self.tree.windows.count; } else if ([item isKindOfClass:[ORGUITreeNode class]]) { ORGUITreeNode *node = item; - return node.subviews.count; + return node.children.count; } else { return 0; } @@ -65,7 +122,7 @@ - (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id return (self.tree.windows)[index]; } else if ([item isKindOfClass:[ORGUITreeNode class]]) { ORGUITreeNode *node = item; - return node.subviews[index]; + return node.children[index]; } else { return nil; } @@ -74,22 +131,13 @@ - (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id - (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item { if ([item isKindOfClass:[ORGUITreeNode class]]) { ORGUITreeNode *node = item; - return node.subviews.count; + return node.children.count; //return [node.view isKindOfClass:[NSWindow class]]; } return NO; } - (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item { - // Every regular view uses bindings to the item. The "Date Cell" needs to have the date extracted from the fileURL - // if ([tableColumn.identifier isEqualToString:@"DateCell"]) { - // id dateValue; - // if ([[item fileURL] getResourceValue:&dateValue forKey:NSURLContentModificationDateKey error:nil]) { - // return dateValue; - // } else { - // return nil; - // } - // } return item; } @@ -98,17 +146,37 @@ - (BOOL)outlineView:(NSOutlineView *)outlineView isGroupItem:(id)item { } - (NSView *)outlineView:(NSOutlineView *)outlineView viewForTableColumn:(NSTableColumn *)tableColumn item:(id)item { + ORGTableCellView *view; if ([item isKindOfClass:[ORGUITreeNode class]]) { ORGUITreeNode *node = item; - if ([node.view isKindOfClass:[NSWindow class]]) { - // Everything is setup in bindings - return [outlineView makeViewWithIdentifier:@"WindowCell" owner:self]; + if ([node.uiElement isKindOfClass:[NSWindow class]]) { + view = [outlineView makeViewWithIdentifier:@"WindowCell" owner:self]; } else { - return [outlineView makeViewWithIdentifier:@"MainCell" owner:self]; + view = [outlineView makeViewWithIdentifier:@"MainCell" owner:self]; } + view.treeNode = item; } - return nil; + return view; +} + +- (void)outlineViewSelectionDidChange:(NSNotification*)notification { + + NSOutlineView *table = notification.object; + + self.selectedNode = [table itemAtRow:[table selectedRow]]; +// if (self.selectedNode.isHidden) { +// self.selectedNode = nil; +// } + [self.propertiesView showElement:self.selectedNode]; + [self.classView showElement:self.selectedNode]; +} + +#pragma mark - Actions + + +- (IBAction)elementViewSelection:(NSSegmentedControl *)sender { + [self.tabView selectTabViewItemAtIndex:sender.selectedSegment]; } @end diff --git a/Organismo/Mac/ORGInspectorWindowController.xib b/Organismo/Mac/ORGInspectorWindowController.xib index 1d1b046..00eebb2 100644 --- a/Organismo/Mac/ORGInspectorWindowController.xib +++ b/Organismo/Mac/ORGInspectorWindowController.xib @@ -1,154 +1,590 @@ - + - + + + + + + + + + + + + + + + + - + - - + + - + - - + + - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + + + + + + + + + + NSIsNil + + + + + + NSIsNotNil + + + + + + + + + + + + + + NSIsNil + + + + + + NSIsNotNil + + + + + + + + + + + + + + + + + NSIsNil + + + + + + NSIsNotNil + + + + + + + + + + + + + + NSIsNil + + + + + + NSIsNotNil + + + + + + + + + + + + + + + + + NSIsNil + + + + + + NSIsNotNil + + + + + + + + + + + + + + NSIsNil + + + + + + NSIsNotNil + + + + + + + + + + + + + + + + NSIsNil + + + + + + + + + + + + + - + + + NSIsNil + + - - - - - - + + + + + + + + + + + + + NSIsNil + + + + + + + + + + + + + + + + NSIsNil + + + + + + + + + + + + - + - + + + NSIsNil + + + + + + NSIsNotNil + + + + + + + + + + + + + + NSIsNil + + + + + + NSIsNotNil + + + + + + + + + + + + + + + + NSIsNil + + - - + + + + - - - - + + + + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + - - - - - - - - + + + + + + + + - - - - - + + - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + - + + + + + + + + + + + - + - + + + + + + + + diff --git a/Organismo/Mac/ORGNSViewHierarchy.m b/Organismo/Mac/ORGNSViewHierarchy.m index 562124b..e034995 100644 --- a/Organismo/Mac/ORGNSViewHierarchy.m +++ b/Organismo/Mac/ORGNSViewHierarchy.m @@ -38,16 +38,16 @@ + (NSArray*)mainWindowElementTree:(NSDictionary*)properties skipPrivateClasses:( + (void)childNodes:(ORGUITreeNode*)node skipPrivateClasses:(BOOL)skipPrivate screenshots:(BOOL)takeScreenshots recursive:(BOOL)deep { NSView *startView; - if ([node.view isKindOfClass:[NSWindow class]]) { - NSWindow *window = node.view; + if ([node.uiElement isKindOfClass:[NSWindow class]]) { + NSWindow *window = (NSWindow*)node.uiElement; startView = window.contentView; } else { - startView = node.view; + startView = (NSView*)node.uiElement; } for (NSView *subView in startView.subviews) { ORGUITreeNode *childNode = [[ORGUITreeNode alloc] initWithView:subView]; - [node.subviews addObject:childNode]; + [node.children addObject:childNode]; if (deep) { [self childNodes:childNode skipPrivateClasses:skipPrivate screenshots:takeScreenshots recursive:deep]; } diff --git a/Organismo/Mac/ORGTableCellView.h b/Organismo/Mac/ORGTableCellView.h index 85b1921..83104a7 100644 --- a/Organismo/Mac/ORGTableCellView.h +++ b/Organismo/Mac/ORGTableCellView.h @@ -7,11 +7,14 @@ */ @import Cocoa; +@class ORGUITreeNode; @interface ORGTableCellView : NSTableCellView +@property (weak) ORGUITreeNode *treeNode; @property (weak) IBOutlet NSTextField *subTitleTextField; @property (weak) IBOutlet NSProgressIndicator *progessIndicator; +@property (assign) BOOL selected; - (void)layoutViewsForSmallSize:(BOOL)smallSize animated:(BOOL)animated; diff --git a/Organismo/Mac/ORGTableCellView.m b/Organismo/Mac/ORGTableCellView.m index 82d6c67..cbbb528 100644 --- a/Organismo/Mac/ORGTableCellView.m +++ b/Organismo/Mac/ORGTableCellView.m @@ -11,10 +11,11 @@ @interface ORGTableCellView () @property (assign) BOOL isSmallSize; +@property (nonatomic) BOOL mouseInside; +@property (nonatomic) NSTrackingArea * trackingArea; @end - #pragma mark - @implementation ORGTableCellView @@ -31,4 +32,45 @@ - (void)layoutViewsForSmallSize:(BOOL)smallSize animated:(BOOL)animated { } } +- (void)ensureTrackingArea { + if (_trackingArea == nil) { + _trackingArea = [[NSTrackingArea alloc] initWithRect:NSZeroRect options:NSTrackingInVisibleRect | NSTrackingActiveAlways | NSTrackingMouseEnteredAndExited owner:self userInfo:nil]; + } +} + +- (void)updateTrackingAreas { + [super updateTrackingAreas]; + [self ensureTrackingArea]; + if (![[self trackingAreas] containsObject:_trackingArea]) { + [self addTrackingArea:_trackingArea]; + } +} + +- (void)mouseEntered:(NSEvent *)theEvent { + self.mouseInside = YES; + self.needsDisplay = YES; + if (!_selected) { + [[NSNotificationCenter defaultCenter] postNotificationName:@"HIGHLIGHT-ELEMENT" object:self]; + } +} + +- (void)mouseExited:(NSEvent *)theEvent { + self.mouseInside = NO; + self.needsDisplay = YES; + if (!_selected) { + [[NSNotificationCenter defaultCenter] postNotificationName:@"REMOVE-HIGHLIGHT" object:nil]; + } +} + +- (void)drawRect:(NSRect)dirtyRect { + if (self.mouseInside) { + [NSGraphicsContext saveGraphicsState]; + [[NSColor lightGrayColor] set]; + NSRectFill([self bounds]); + [NSGraphicsContext restoreGraphicsState]; + } else { + [super drawRect:dirtyRect]; + } +} + @end diff --git a/Organismo/Mac/ORGUITreeNode.h b/Organismo/Mac/ORGUITreeNode.h index a9e697c..3cffd3b 100644 --- a/Organismo/Mac/ORGUITreeNode.h +++ b/Organismo/Mac/ORGUITreeNode.h @@ -13,13 +13,16 @@ NS_ASSUME_NONNULL_BEGIN @interface ORGUITreeNode : NSObject -@property (nonatomic, weak) NSView *view; -@property (nonatomic) NSMutableArray *subviews; +@property (nonatomic, weak) NSObject *uiElement; +@property (nonatomic) NSMutableArray *children; - (instancetype)initWithView:(nonnull NSView*)view; - (NSString*)title; - (NSString*)descriptor; +- (NSString*)className; - (NSImage*)thumbnailImage; +- (NSRect)rect; +- (BOOL)isHidden; @end diff --git a/Organismo/Mac/ORGUITreeNode.m b/Organismo/Mac/ORGUITreeNode.m index 9ae95af..14b012d 100644 --- a/Organismo/Mac/ORGUITreeNode.m +++ b/Organismo/Mac/ORGUITreeNode.m @@ -13,44 +13,67 @@ @implementation ORGUITreeNode - (instancetype)initWithView:(nonnull NSView*)view { self = [super init]; if (self) { - self.view = view; - self.subviews = [[NSMutableArray alloc] init]; + self.uiElement = view; + self.children = [[NSMutableArray alloc] init]; } return self; } - (NSString*)title { - if ([self.view isKindOfClass:[NSWindow class]]) { - NSWindow *window = (NSWindow*)self.view; + if ([self.uiElement isKindOfClass:[NSWindow class]]) { + NSWindow *window = (NSWindow*)self.uiElement; return window.title; } return @"Unknown"; } - (NSString*)descriptor { - if ([self.view respondsToSelector:@selector(ORG_description)]) { -// NSWindow *window = (NSWindow*)self.view; -// NSString *descriptor = [[NSString alloc] initWithFormat:@"%@ (%@)", NSStringFromClass(window.class), window.title]; - return [self.view performSelector:@selector(ORG_description)]; + if ([self.uiElement respondsToSelector:@selector(ORG_description)]) { + return [self.uiElement performSelector:@selector(ORG_description)]; } else { - NSString *descriptor = [[NSString alloc] initWithFormat:@"%@", NSStringFromClass(self.view.class)]; - return descriptor; + + return self.className; } - return self.view.description; +} + +- (NSRect)rect { + if ([self.uiElement isKindOfClass:[NSWindow class]]) { + NSWindow *window = (NSWindow*)self.uiElement; + return window.frame; + } else if ([self.uiElement isKindOfClass:[NSView class]]) { + NSView *view = (NSView*)self.uiElement; + return view.frame; + } + return NSZeroRect; +} + +- (BOOL)isHidden { + if ([self.uiElement isKindOfClass:[NSWindow class]]) { + NSWindow *window = (NSWindow*)self.uiElement; + return !window.isVisible; + } else if ([self.uiElement isKindOfClass:[NSView class]]) { + NSView *view = (NSView*)self.uiElement; + return view.isHidden; + } + return NO; +} + +- (NSString*)className { + return [[NSString alloc] initWithFormat:@"%@", NSStringFromClass(self.uiElement.class)]; } - (NSImage*)thumbnailImage { NSImage * image; NSBundle *orgFramework = [NSBundle bundleWithIdentifier: @"com.organismo-mobile.Organismo"]; - if ([self.view isKindOfClass:[NSWindow class]]) { + if ([self.uiElement isKindOfClass:[NSWindow class]]) { image = [orgFramework imageForResource:@"NSWindow_32_Normal"]; - } else if ([self.view isKindOfClass:[NSPopUpButton class]]) { + } else if ([self.uiElement isKindOfClass:[NSPopUpButton class]]) { image = [orgFramework imageForResource:@"NSPopUp-Push_32_Normal"]; - } else if ([self.view isKindOfClass:[NSButton class]]) { - NSButton *button = (NSButton*)self.view; + } else if ([self.uiElement isKindOfClass:[NSButton class]]) { + NSButton *button = (NSButton*)self.uiElement; NSString *buttonType = [button performSelector:@selector(ns_widgetType)]; - NSLog(@"widget type %@", buttonType); + //NSLog(@"widget type %@", buttonType); if ([buttonType isEqualToString:@"RadioButton"]) { image = [orgFramework imageForResource:@"NSButton-Radio_32_Normal"]; } else if ([buttonType isEqualToString:@"HelpButton"]) { @@ -70,30 +93,32 @@ - (NSImage*)thumbnailImage { } else { image = [orgFramework imageForResource:@"NSButton-Push_32_Normal"]; } - } else if ([self.view isKindOfClass:[NSTextField class]]) { + } else if ([self.uiElement isKindOfClass:[NSTextField class]]) { image = [orgFramework imageForResource:@"NSTextField_32_Normal"]; - } else if ([self.view isKindOfClass:[NSSecureTextField class]]) { + } else if ([self.uiElement isKindOfClass:[NSSecureTextField class]]) { image = [orgFramework imageForResource:@"NSSecureTextField_32_Normal"]; - } else if ([self.view isKindOfClass:[NSSwitch class]]) { + } else if ([self.uiElement isKindOfClass:[NSSwitch class]]) { image = [orgFramework imageForResource:@"NSSwitch_32_Normal"]; - } else if ([self.view isKindOfClass:[NSStepper class]]) { + } else if ([self.uiElement isKindOfClass:[NSStepper class]]) { image = [orgFramework imageForResource:@"NSStepper_32_Normal"]; - } else if ([self.view isKindOfClass:[NSDatePicker class]]) { + } else if ([self.uiElement isKindOfClass:[NSDatePicker class]]) { image = [orgFramework imageForResource:@"NSDatePicker_32_Normal"]; - } else if ([self.view isKindOfClass:[NSSegmentedControl class]]) { + } else if ([self.uiElement isKindOfClass:[NSSegmentedControl class]]) { image = [orgFramework imageForResource:@"NSSegmentedControl_32_Normal"]; - } else if ([self.view isKindOfClass:[NSColorWell class]]) { + } else if ([self.uiElement isKindOfClass:[NSColorWell class]]) { image = [orgFramework imageForResource:@"NSColorWell_32_Normal"]; - } else if ([self.view isKindOfClass:[NSPanel class]]) { + } else if ([self.uiElement isKindOfClass:[NSPanel class]]) { image = [orgFramework imageForResource:@"NSPanel_32_Normal"]; - } else if ([self.view isKindOfClass:[NSPathControl class]]) { + } else if ([self.uiElement isKindOfClass:[NSPathControl class]]) { image = [orgFramework imageForResource:@"NSPathControl_32_Normal"]; - } else if ([self.view isKindOfClass:[NSLevelIndicator class]]) { + } else if ([self.uiElement isKindOfClass:[NSLevelIndicator class]]) { image = [orgFramework imageForResource:@"NSLevelIndicator_32_Normal"]; - } else if ([self.view isKindOfClass:[NSSplitViewController class]]) { + } else if ([self.uiElement isKindOfClass:[NSSplitViewController class]]) { image = [orgFramework imageForResource:@"NSSplitViewControllerHorizontal_32_Normal"]; - } else if ([self.view isKindOfClass:[NSProgressIndicator class]]) { - NSProgressIndicator *progress = (NSProgressIndicator*)self.view; + } else if ([self.uiElement isKindOfClass:[NSScrollView class]]) { + image = [orgFramework imageForResource:@"NSScrollView_16_Normal"]; + } else if ([self.uiElement isKindOfClass:[NSProgressIndicator class]]) { + NSProgressIndicator *progress = (NSProgressIndicator*)self.uiElement; switch (progress.style) { case NSProgressIndicatorStyleBar: { if (progress.indeterminate) { @@ -110,8 +135,8 @@ - (NSImage*)thumbnailImage { } } break; } - } else if ([self.view isKindOfClass:[NSSlider class]]) { - NSSlider *slider = (NSSlider*)self.view; + } else if ([self.uiElement isKindOfClass:[NSSlider class]]) { + NSSlider *slider = (NSSlider*)self.uiElement; switch (slider.sliderType) { case NSSliderTypeLinear: { if (slider.vertical) { @@ -133,7 +158,7 @@ - (NSImage*)thumbnailImage { } break; } } else { - NSLog(@"Non handled class: %@", NSStringFromClass(self.view.class)); + NSLog(@"Non handled class: %@", NSStringFromClass(self.uiElement.class)); } return image; } diff --git a/Organismo/ORGElementPropertiesView.h b/Organismo/ORGElementPropertiesView.h new file mode 100644 index 0000000..2741587 --- /dev/null +++ b/Organismo/ORGElementPropertiesView.h @@ -0,0 +1,21 @@ +// +// ORGElementPropertiesView.h +// Organismo-mac +// +// Created by Jon Gabilondo on 12/09/2019. +// Copyright © 2019 organismo-mobile. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@class ORGUITreeNode; + +@interface ORGElementPropertiesView : NSView + +- (void)showElement:(ORGUITreeNode*)node; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Organismo/ORGElementPropertiesView.m b/Organismo/ORGElementPropertiesView.m new file mode 100644 index 0000000..b5d97f9 --- /dev/null +++ b/Organismo/ORGElementPropertiesView.m @@ -0,0 +1,90 @@ +// +// ORGElementPropertiesView.m +// Organismo-mac +// +// Created by Jon Gabilondo on 12/09/2019. +// Copyright © 2019 organismo-mobile. All rights reserved. +// + +#import "ORGElementPropertiesView.h" +#import "ORGUITreeNode.h" + +@interface ORGElementPropertiesView() + +@property (weak) IBOutlet NSTextField *rectX; +@property (weak) IBOutlet NSTextField *rectW; +@property (weak) IBOutlet NSTextField *rectY; +@property (weak) IBOutlet NSTextField *rectH; +@property (nonatomic) ORGUITreeNode *node; + +- (void)updateFrame; + +@end + + +@implementation ORGElementPropertiesView + +- (void)showElement:(ORGUITreeNode*)node { + self.node = node; + + if (node) { + NSRect rect = node.rect; + self.rectX.integerValue = rect.origin.x; + self.rectY.integerValue = rect.origin.y; + self.rectW.integerValue = rect.size.width; + self.rectH.integerValue = rect.size.height; + } else { + self.rectX.stringValue = @""; + self.rectY.stringValue = @""; + self.rectW.stringValue = @""; + self.rectH.stringValue = @""; + } +} + +- (void)drawRect:(NSRect)dirtyRect { + [super drawRect:dirtyRect]; + + // Drawing code here. +} + +- (IBAction)rectXChanged:(NSStepper *)sender { + [self updateFrame]; +} + +- (IBAction)rectYChanged:(NSStepper *)sender { + [self updateFrame]; +} + +- (IBAction)rectWChanged:(NSStepper *)sender { + [self updateFrame]; +} + +- (IBAction)rectHChanged:(NSStepper *)sender { + [self updateFrame]; +} + +- (void)updateFrame { + if ([self.node.uiElement isKindOfClass:NSWindow.class]) { + NSWindow *window = (NSWindow*)self.node.uiElement; + if (window.visible) { + [window setFrame:NSMakeRect(self.rectX.integerValue, self.rectY.integerValue, self.rectW.integerValue, self.rectH.integerValue) display:YES animate:YES]; + } + } else if ([self.node.uiElement isKindOfClass:NSView.class]) { + NSView *view = (NSView*)self.node.uiElement; + if (view.isHidden == NO) { + view.frame = NSMakeRect(self.rectX.integerValue, self.rectY.integerValue, self.rectW.integerValue, self.rectH.integerValue); + } + } +} + +#pragma mark NSTextFieldDelegate + +- (void)controlTextDidEndEditing:(NSNotification *)obj { + [self updateFrame]; +} + +- (void)controlTextDidChange:(NSNotification *)obj { + +} + +@end diff --git a/Organismo/ORGScreenshot.m b/Organismo/ORGScreenshot.m index 67eb9cd..02d2900 100644 --- a/Organismo/ORGScreenshot.m +++ b/Organismo/ORGScreenshot.m @@ -55,10 +55,10 @@ + (UIImage*)viewScreenshot:(UIView*)view { return nil; } - // hide subviews. Unless is some control that we are not diving in, (e.g. Pickers). + // hide children. Unless is some control that we are not diving in, (e.g. Pickers). NSMutableArray * hiddenSubviews = [NSMutableArray array]; if (![view ORG_ignoreSubviews]) { - for (UIView *subview in view.subviews) { + for (UIView *subview in view.children) { if (![subview isHidden]) { [subview setHidden:YES]; [hiddenSubviews addObject:subview]; diff --git a/Organismo/ORGUIViewHierarchy.m b/Organismo/ORGUIViewHierarchy.m index 9cf950b..1568aab 100644 --- a/Organismo/ORGUIViewHierarchy.m +++ b/Organismo/ORGUIViewHierarchy.m @@ -58,13 +58,13 @@ + (NSDictionary*)viewElementTree:(UIView*)view skipPrivateClasses:(BOOL)skipPriv } } - // Run subviews. Unless is some kind of control that there is no need to dive in. + // Run children. Unless is some kind of control that there is no need to dive in. if ([view ORG_ignoreSubviews]) { return node; } NSMutableArray * children = [NSMutableArray array]; - for (UIView *subView in view.subviews) { + for (UIView *subView in view.children) { NSDictionary * child = [self viewElementTree:subView skipPrivateClasses:skipPrivate viewScreenshots:viewScreenshots]; if (child) { [children addObject:child]; @@ -101,9 +101,9 @@ - (UIView*)findViewAtPoint:(CGPoint)pt rootView:(UIView*)rootView // Isn't better to use hitTest ? NO ! hitTest ignores if user labels etc. //- - UIView * view = rootView; // we assume that if no subviews hit the hit is in the rootview + UIView * view = rootView; // we assume that if no children hit the hit is in the rootview - for (UIView * runningView in [rootView.subviews reverseObjectEnumerator]) { + for (UIView * runningView in [rootView.children reverseObjectEnumerator]) { if ([runningView isHidden]) { continue; diff --git a/Organismo/UIView+ORG.m b/Organismo/UIView+ORG.m index 81794ec..b4ae2c3 100644 --- a/Organismo/UIView+ORG.m +++ b/Organismo/UIView+ORG.m @@ -152,7 +152,7 @@ - (id)ORG_subviewWithClass:(Class)subviewClass { UIView *theView; - for (UIView * view in [self subviews]) { + for (UIView * view in [self children]) { if ([view isKindOfClass:subviewClass]) { theView = view; } else {