FLEXShortcut.m 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. //
  2. // FLEXShortcut.m
  3. // FLEX
  4. //
  5. // Created by Tanner Bennett on 12/10/19.
  6. // Copyright © 2020 FLEX Team. All rights reserved.
  7. //
  8. #import "FLEXShortcut.h"
  9. #import "FLEXProperty.h"
  10. #import "FLEXPropertyAttributes.h"
  11. #import "FLEXIvar.h"
  12. #import "FLEXMethod.h"
  13. #import "FLEXRuntime+UIKitHelpers.h"
  14. #import "FLEXObjectExplorerFactory.h"
  15. #import "FLEXFieldEditorViewController.h"
  16. #import "FLEXMethodCallingViewController.h"
  17. #import "FLEXMetadataSection.h"
  18. #import "FLEXTableView.h"
  19. #pragma mark - FLEXShortcut
  20. @interface FLEXShortcut () {
  21. id _item;
  22. }
  23. @property (nonatomic, readonly) FLEXMetadataKind metadataKind;
  24. @property (nonatomic, readonly) FLEXProperty *property;
  25. @property (nonatomic, readonly) FLEXMethod *method;
  26. @property (nonatomic, readonly) FLEXIvar *ivar;
  27. @property (nonatomic, readonly) id<FLEXRuntimeMetadata> metadata;
  28. @end
  29. @implementation FLEXShortcut
  30. @synthesize defaults = _defaults;
  31. + (id<FLEXShortcut>)shortcutFor:(id)item {
  32. if ([item conformsToProtocol:@protocol(FLEXShortcut)]) {
  33. return item;
  34. }
  35. FLEXShortcut *shortcut = [self new];
  36. shortcut->_item = item;
  37. if ([item isKindOfClass:[FLEXProperty class]]) {
  38. if (shortcut.property.isClassProperty) {
  39. shortcut->_metadataKind = FLEXMetadataKindClassProperties;
  40. } else {
  41. shortcut->_metadataKind = FLEXMetadataKindProperties;
  42. }
  43. }
  44. if ([item isKindOfClass:[FLEXIvar class]]) {
  45. shortcut->_metadataKind = FLEXMetadataKindIvars;
  46. }
  47. if ([item isKindOfClass:[FLEXMethod class]]) {
  48. // We don't care if it's a class method or not
  49. shortcut->_metadataKind = FLEXMetadataKindMethods;
  50. }
  51. return shortcut;
  52. }
  53. - (id)propertyOrIvarValue:(id)object {
  54. return [self.metadata currentValueWithTarget:object];
  55. }
  56. - (NSString *)titleWith:(id)object {
  57. switch (self.metadataKind) {
  58. case FLEXMetadataKindClassProperties:
  59. case FLEXMetadataKindProperties:
  60. // Since we're outside of the "properties" section, prepend @property for clarity.
  61. return [@"@property " stringByAppendingString:[_item description]];
  62. default:
  63. return [_item description];
  64. }
  65. NSAssert(
  66. [_item isKindOfClass:[NSString class]],
  67. @"Unexpected type: %@", [_item class]
  68. );
  69. return _item;
  70. }
  71. - (NSString *)subtitleWith:(id)object {
  72. if (self.metadataKind) {
  73. return [self.metadata previewWithTarget:object];
  74. }
  75. // Item is probably a string; must return empty string since
  76. // these will be gathered into an array. If the object is a
  77. // just a string, it doesn't get a subtitle.
  78. return @"";
  79. }
  80. - (void (^)(UIViewController *))didSelectActionWith:(id)object {
  81. return nil;
  82. }
  83. - (UIViewController *)viewerWith:(id)object {
  84. NSAssert(self.metadataKind, @"Static titles cannot be viewed");
  85. return [self.metadata viewerWithTarget:object];
  86. }
  87. - (UIViewController *)editorWith:(id)object forSection:(FLEXTableViewSection *)section {
  88. NSAssert(self.metadataKind, @"Static titles cannot be edited");
  89. return [self.metadata editorWithTarget:object section:section];
  90. }
  91. - (UITableViewCellAccessoryType)accessoryTypeWith:(id)object {
  92. if (self.metadataKind) {
  93. return [self.metadata suggestedAccessoryTypeWithTarget:object];
  94. }
  95. return UITableViewCellAccessoryNone;
  96. }
  97. - (NSString *)customReuseIdentifierWith:(id)object {
  98. if (self.metadataKind) {
  99. return kFLEXCodeFontCell;
  100. }
  101. return kFLEXMultilineCell;
  102. }
  103. #pragma mark FLEXObjectExplorerDefaults
  104. - (void)setDefaults:(FLEXObjectExplorerDefaults *)defaults {
  105. _defaults = defaults;
  106. if (_metadataKind) {
  107. self.metadata.defaults = defaults;
  108. }
  109. }
  110. - (BOOL)isEditable {
  111. if (_metadataKind) {
  112. return self.metadata.isEditable;
  113. }
  114. return NO;
  115. }
  116. - (BOOL)isCallable {
  117. if (_metadataKind) {
  118. return self.metadata.isCallable;
  119. }
  120. return NO;
  121. }
  122. #pragma mark - Helpers
  123. - (FLEXProperty *)property { return _item; }
  124. - (FLEXMethodBase *)method { return _item; }
  125. - (FLEXIvar *)ivar { return _item; }
  126. - (id<FLEXRuntimeMetadata>)metadata { return _item; }
  127. @end
  128. #pragma mark - FLEXActionShortcut
  129. @interface FLEXActionShortcut ()
  130. @property (nonatomic, readonly) NSString *title;
  131. @property (nonatomic, readonly) NSString *(^subtitleFuture)(id);
  132. @property (nonatomic, readonly) UIViewController *(^viewerFuture)(id);
  133. @property (nonatomic, readonly) void (^selectionHandler)(UIViewController *, id);
  134. @property (nonatomic, readonly) UITableViewCellAccessoryType (^accessoryTypeFuture)(id);
  135. @end
  136. @implementation FLEXActionShortcut
  137. @synthesize defaults = _defaults;
  138. + (instancetype)title:(NSString *)title
  139. subtitle:(NSString *(^)(id))subtitle
  140. viewer:(UIViewController *(^)(id))viewer
  141. accessoryType:(UITableViewCellAccessoryType (^)(id))type {
  142. return [[self alloc] initWithTitle:title subtitle:subtitle viewer:viewer selectionHandler:nil accessoryType:type];
  143. }
  144. + (instancetype)title:(NSString *)title
  145. subtitle:(NSString * (^)(id))subtitle
  146. selectionHandler:(void (^)(UIViewController *, id))tapAction
  147. accessoryType:(UITableViewCellAccessoryType (^)(id))type {
  148. return [[self alloc] initWithTitle:title subtitle:subtitle viewer:nil selectionHandler:tapAction accessoryType:type];
  149. }
  150. - (id)initWithTitle:(NSString *)title
  151. subtitle:(id)subtitleFuture
  152. viewer:(id)viewerFuture
  153. selectionHandler:(id)tapAction
  154. accessoryType:(id)accessoryTypeFuture {
  155. NSParameterAssert(title.length);
  156. self = [super init];
  157. if (self) {
  158. id nilBlock = ^id (id obj) { return nil; };
  159. _title = title;
  160. _subtitleFuture = subtitleFuture ?: nilBlock;
  161. _viewerFuture = viewerFuture ?: nilBlock;
  162. _selectionHandler = tapAction;
  163. _accessoryTypeFuture = accessoryTypeFuture ?: nilBlock;
  164. }
  165. return self;
  166. }
  167. - (NSString *)titleWith:(id)object {
  168. return self.title;
  169. }
  170. - (NSString *)subtitleWith:(id)object {
  171. if (self.defaults.wantsDynamicPreviews) {
  172. return self.subtitleFuture(object);
  173. }
  174. return nil;
  175. }
  176. - (void (^)(UIViewController *))didSelectActionWith:(id)object {
  177. if (self.selectionHandler) {
  178. return ^(UIViewController *host) {
  179. self.selectionHandler(host, object);
  180. };
  181. }
  182. return nil;
  183. }
  184. - (UIViewController *)viewerWith:(id)object {
  185. return self.viewerFuture(object);
  186. }
  187. - (UITableViewCellAccessoryType)accessoryTypeWith:(id)object {
  188. return self.accessoryTypeFuture(object);
  189. }
  190. - (NSString *)customReuseIdentifierWith:(id)object {
  191. if (!self.subtitleFuture(object)) {
  192. // The text is more centered with this style if there is no subtitle
  193. return kFLEXDefaultCell;
  194. }
  195. return nil;
  196. }
  197. - (BOOL)isEditable { return NO; }
  198. - (BOOL)isCallable { return NO; }
  199. @end