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

255 lines
6.7 KiB
Objective-C

//
// FLEXShortcut.m
// FLEX
//
// Created by Tanner Bennett on 12/10/19.
// Copyright © 2020 FLEX Team. All rights reserved.
//
#import "FLEXShortcut.h"
#import "FLEXProperty.h"
#import "FLEXPropertyAttributes.h"
#import "FLEXIvar.h"
#import "FLEXMethod.h"
#import "FLEXRuntime+UIKitHelpers.h"
#import "FLEXObjectExplorerFactory.h"
#import "FLEXFieldEditorViewController.h"
#import "FLEXMethodCallingViewController.h"
#import "FLEXMetadataSection.h"
#import "FLEXTableView.h"
#pragma mark - FLEXShortcut
@interface FLEXShortcut () {
id _item;
}
@property (nonatomic, readonly) FLEXMetadataKind metadataKind;
@property (nonatomic, readonly) FLEXProperty *property;
@property (nonatomic, readonly) FLEXMethod *method;
@property (nonatomic, readonly) FLEXIvar *ivar;
@property (nonatomic, readonly) id<FLEXRuntimeMetadata> metadata;
@end
@implementation FLEXShortcut
@synthesize defaults = _defaults;
+ (id<FLEXShortcut>)shortcutFor:(id)item {
if ([item conformsToProtocol:@protocol(FLEXShortcut)]) {
return item;
}
FLEXShortcut *shortcut = [self new];
shortcut->_item = item;
if ([item isKindOfClass:[FLEXProperty class]]) {
if (shortcut.property.isClassProperty) {
shortcut->_metadataKind = FLEXMetadataKindClassProperties;
} else {
shortcut->_metadataKind = FLEXMetadataKindProperties;
}
}
if ([item isKindOfClass:[FLEXIvar class]]) {
shortcut->_metadataKind = FLEXMetadataKindIvars;
}
if ([item isKindOfClass:[FLEXMethod class]]) {
// We don't care if it's a class method or not
shortcut->_metadataKind = FLEXMetadataKindMethods;
}
return shortcut;
}
- (id)propertyOrIvarValue:(id)object {
return [self.metadata currentValueWithTarget:object];
}
- (NSString *)titleWith:(id)object {
switch (self.metadataKind) {
case FLEXMetadataKindClassProperties:
case FLEXMetadataKindProperties:
// Since we're outside of the "properties" section, prepend @property for clarity.
return [@"@property " stringByAppendingString:[_item description]];
default:
return [_item description];
}
NSAssert(
[_item isKindOfClass:[NSString class]],
@"Unexpected type: %@", [_item class]
);
return _item;
}
- (NSString *)subtitleWith:(id)object {
if (self.metadataKind) {
return [self.metadata previewWithTarget:object];
}
// Item is probably a string; must return empty string since
// these will be gathered into an array. If the object is a
// just a string, it doesn't get a subtitle.
return @"";
}
- (void (^)(UIViewController *))didSelectActionWith:(id)object {
return nil;
}
- (UIViewController *)viewerWith:(id)object {
NSAssert(self.metadataKind, @"Static titles cannot be viewed");
return [self.metadata viewerWithTarget:object];
}
- (UIViewController *)editorWith:(id)object forSection:(FLEXTableViewSection *)section {
NSAssert(self.metadataKind, @"Static titles cannot be edited");
return [self.metadata editorWithTarget:object section:section];
}
- (UITableViewCellAccessoryType)accessoryTypeWith:(id)object {
if (self.metadataKind) {
return [self.metadata suggestedAccessoryTypeWithTarget:object];
}
return UITableViewCellAccessoryNone;
}
- (NSString *)customReuseIdentifierWith:(id)object {
if (self.metadataKind) {
return kFLEXCodeFontCell;
}
return kFLEXMultilineCell;
}
#pragma mark FLEXObjectExplorerDefaults
- (void)setDefaults:(FLEXObjectExplorerDefaults *)defaults {
_defaults = defaults;
if (_metadataKind) {
self.metadata.defaults = defaults;
}
}
- (BOOL)isEditable {
if (_metadataKind) {
return self.metadata.isEditable;
}
return NO;
}
- (BOOL)isCallable {
if (_metadataKind) {
return self.metadata.isCallable;
}
return NO;
}
#pragma mark - Helpers
- (FLEXProperty *)property { return _item; }
- (FLEXMethodBase *)method { return _item; }
- (FLEXIvar *)ivar { return _item; }
- (id<FLEXRuntimeMetadata>)metadata { return _item; }
@end
#pragma mark - FLEXActionShortcut
@interface FLEXActionShortcut ()
@property (nonatomic, readonly) NSString *title;
@property (nonatomic, readonly) NSString *(^subtitleFuture)(id);
@property (nonatomic, readonly) UIViewController *(^viewerFuture)(id);
@property (nonatomic, readonly) void (^selectionHandler)(UIViewController *, id);
@property (nonatomic, readonly) UITableViewCellAccessoryType (^accessoryTypeFuture)(id);
@end
@implementation FLEXActionShortcut
@synthesize defaults = _defaults;
+ (instancetype)title:(NSString *)title
subtitle:(NSString *(^)(id))subtitle
viewer:(UIViewController *(^)(id))viewer
accessoryType:(UITableViewCellAccessoryType (^)(id))type {
return [[self alloc] initWithTitle:title subtitle:subtitle viewer:viewer selectionHandler:nil accessoryType:type];
}
+ (instancetype)title:(NSString *)title
subtitle:(NSString * (^)(id))subtitle
selectionHandler:(void (^)(UIViewController *, id))tapAction
accessoryType:(UITableViewCellAccessoryType (^)(id))type {
return [[self alloc] initWithTitle:title subtitle:subtitle viewer:nil selectionHandler:tapAction accessoryType:type];
}
- (id)initWithTitle:(NSString *)title
subtitle:(id)subtitleFuture
viewer:(id)viewerFuture
selectionHandler:(id)tapAction
accessoryType:(id)accessoryTypeFuture {
NSParameterAssert(title.length);
self = [super init];
if (self) {
id nilBlock = ^id (id obj) { return nil; };
_title = title;
_subtitleFuture = subtitleFuture ?: nilBlock;
_viewerFuture = viewerFuture ?: nilBlock;
_selectionHandler = tapAction;
_accessoryTypeFuture = accessoryTypeFuture ?: nilBlock;
}
return self;
}
- (NSString *)titleWith:(id)object {
return self.title;
}
- (NSString *)subtitleWith:(id)object {
if (self.defaults.wantsDynamicPreviews) {
return self.subtitleFuture(object);
}
return nil;
}
- (void (^)(UIViewController *))didSelectActionWith:(id)object {
if (self.selectionHandler) {
return ^(UIViewController *host) {
self.selectionHandler(host, object);
};
}
return nil;
}
- (UIViewController *)viewerWith:(id)object {
return self.viewerFuture(object);
}
- (UITableViewCellAccessoryType)accessoryTypeWith:(id)object {
return self.accessoryTypeFuture(object);
}
- (NSString *)customReuseIdentifierWith:(id)object {
if (!self.subtitleFuture(object)) {
// The text is more centered with this style if there is no subtitle
return kFLEXDefaultCell;
}
return nil;
}
- (BOOL)isEditable { return NO; }
- (BOOL)isCallable { return NO; }
@end