Files
main/Tweaks/FLEX/ObjectExplorers/Sections/FLEXMetadataSection.m
2023-06-27 09:54:41 +02:00

234 lines
7.4 KiB
Objective-C

//
// FLEXMetadataSection.m
// FLEX
//
// Created by Tanner Bennett on 9/19/19.
// Copyright © 2020 FLEX Team. All rights reserved.
//
#import "FLEXMetadataSection.h"
#import "FLEXTableView.h"
#import "FLEXTableViewCell.h"
#import "FLEXObjectExplorerFactory.h"
#import "FLEXFieldEditorViewController.h"
#import "FLEXMethodCallingViewController.h"
#import "FLEXIvar.h"
#import "NSArray+FLEX.h"
#import "FLEXRuntime+UIKitHelpers.h"
@interface FLEXMetadataSection ()
@property (nonatomic, readonly) FLEXObjectExplorer *explorer;
/// Filtered
@property (nonatomic, copy) NSArray<id<FLEXRuntimeMetadata>> *metadata;
/// Unfiltered
@property (nonatomic, copy) NSArray<id<FLEXRuntimeMetadata>> *allMetadata;
@end
@implementation FLEXMetadataSection
#pragma mark - Initialization
+ (instancetype)explorer:(FLEXObjectExplorer *)explorer kind:(FLEXMetadataKind)metadataKind {
return [[self alloc] initWithExplorer:explorer kind:metadataKind];
}
- (id)initWithExplorer:(FLEXObjectExplorer *)explorer kind:(FLEXMetadataKind)metadataKind {
self = [super init];
if (self) {
_explorer = explorer;
_metadataKind = metadataKind;
[self reloadData];
}
return self;
}
#pragma mark - Private
- (NSString *)titleWithBaseName:(NSString *)baseName {
unsigned long totalCount = self.allMetadata.count;
unsigned long filteredCount = self.metadata.count;
if (totalCount == filteredCount) {
return [baseName stringByAppendingFormat:@" (%lu)", totalCount];
} else {
return [baseName stringByAppendingFormat:@" (%lu of %lu)", filteredCount, totalCount];
}
}
- (UITableViewCellAccessoryType)accessoryTypeForRow:(NSInteger)row {
return [self.metadata[row] suggestedAccessoryTypeWithTarget:self.explorer.object];
}
#pragma mark - Public
- (void)setExcludedMetadata:(NSSet<NSString *> *)excludedMetadata {
_excludedMetadata = excludedMetadata;
[self reloadData];
}
#pragma mark - Overrides
- (NSString *)titleForRow:(NSInteger)row {
return [self.metadata[row] description];
}
- (NSString *)subtitleForRow:(NSInteger)row {
return [self.metadata[row] previewWithTarget:self.explorer.object];
}
- (NSString *)title {
switch (self.metadataKind) {
case FLEXMetadataKindProperties:
return [self titleWithBaseName:@"Properties"];
case FLEXMetadataKindClassProperties:
return [self titleWithBaseName:@"Class Properties"];
case FLEXMetadataKindIvars:
return [self titleWithBaseName:@"Ivars"];
case FLEXMetadataKindMethods:
return [self titleWithBaseName:@"Methods"];
case FLEXMetadataKindClassMethods:
return [self titleWithBaseName:@"Class Methods"];
case FLEXMetadataKindClassHierarchy:
return [self titleWithBaseName:@"Class Hierarchy"];
case FLEXMetadataKindProtocols:
return [self titleWithBaseName:@"Protocols"];
case FLEXMetadataKindOther:
return @"Miscellaneous";
}
}
- (NSInteger)numberOfRows {
return self.metadata.count;
}
- (void)setFilterText:(NSString *)filterText {
super.filterText = filterText;
if (!self.filterText.length) {
self.metadata = self.allMetadata;
} else {
self.metadata = [self.allMetadata flex_filtered:^BOOL(id<FLEXRuntimeMetadata> obj, NSUInteger idx) {
return [obj.description localizedCaseInsensitiveContainsString:self.filterText];
}];
}
}
- (void)reloadData {
switch (self.metadataKind) {
case FLEXMetadataKindProperties:
self.allMetadata = self.explorer.properties;
break;
case FLEXMetadataKindClassProperties:
self.allMetadata = self.explorer.classProperties;
break;
case FLEXMetadataKindIvars:
self.allMetadata = self.explorer.ivars;
break;
case FLEXMetadataKindMethods:
self.allMetadata = self.explorer.methods;
break;
case FLEXMetadataKindClassMethods:
self.allMetadata = self.explorer.classMethods;
break;
case FLEXMetadataKindProtocols:
self.allMetadata = self.explorer.conformedProtocols;
break;
case FLEXMetadataKindClassHierarchy:
self.allMetadata = self.explorer.classHierarchy;
break;
case FLEXMetadataKindOther:
self.allMetadata = @[self.explorer.instanceSize, self.explorer.imageName];
break;
}
// Remove excluded metadata
if (self.excludedMetadata.count) {
id filterBlock = ^BOOL(id<FLEXRuntimeMetadata> obj, NSUInteger idx) {
return ![self.excludedMetadata containsObject:obj.name];
};
// Filter exclusions and sort
self.allMetadata = [[self.allMetadata flex_filtered:filterBlock]
sortedArrayUsingSelector:@selector(compare:)
];
}
// Re-filter data
self.filterText = self.filterText;
}
- (BOOL)canSelectRow:(NSInteger)row {
UITableViewCellAccessoryType accessory = [self accessoryTypeForRow:row];
return accessory == UITableViewCellAccessoryDisclosureIndicator ||
accessory == UITableViewCellAccessoryDetailDisclosureButton;
}
- (NSString *)reuseIdentifierForRow:(NSInteger)row {
return [self.metadata[row] reuseIdentifierWithTarget:self.explorer.object] ?: kFLEXCodeFontCell;
}
- (UIViewController *)viewControllerToPushForRow:(NSInteger)row {
return [self.metadata[row] viewerWithTarget:self.explorer.object];
}
- (void (^)(__kindof UIViewController *))didPressInfoButtonAction:(NSInteger)row {
return ^(UIViewController *host) {
[host.navigationController pushViewController:[self editorForRow:row] animated:YES];
};
}
- (UIViewController *)editorForRow:(NSInteger)row {
return [self.metadata[row] editorWithTarget:self.explorer.object section:self];
}
- (void)configureCell:(__kindof FLEXTableViewCell *)cell forRow:(NSInteger)row {
cell.titleLabel.text = [self titleForRow:row];
cell.subtitleLabel.text = [self subtitleForRow:row];
cell.accessoryType = [self accessoryTypeForRow:row];
}
- (NSString *)menuSubtitleForRow:(NSInteger)row {
return [self.metadata[row] contextualSubtitleWithTarget:self.explorer.object];
}
- (NSArray<UIMenuElement *> *)menuItemsForRow:(NSInteger)row sender:(UIViewController *)sender {
NSArray<UIMenuElement *> *existingItems = [super menuItemsForRow:row sender:sender];
// These two metadata kinds don't any of the additional options below
switch (self.metadataKind) {
case FLEXMetadataKindClassHierarchy:
case FLEXMetadataKindOther:
return existingItems;
default: break;
}
id<FLEXRuntimeMetadata> metadata = self.metadata[row];
NSMutableArray<UIMenuElement *> *menuItems = [NSMutableArray new];
[menuItems addObject:[UIAction
actionWithTitle:@"Explore Metadata"
image:nil
identifier:nil
handler:^(__kindof UIAction *action) {
[sender.navigationController pushViewController:[FLEXObjectExplorerFactory
explorerViewControllerForObject:metadata
] animated:YES];
}
]];
[menuItems addObjectsFromArray:[metadata
additionalActionsWithTarget:self.explorer.object sender:sender
]];
[menuItems addObjectsFromArray:existingItems];
return menuItems.copy;
}
- (NSArray<NSString *> *)copyMenuItemsForRow:(NSInteger)row {
return [self.metadata[row] copiableMetadataWithTarget:self.explorer.object];
}
@end