Преглед изворни кода

Improve runtime browser accessory view behavior

- Buttons are right-aligned when they don't fill the toolbar
- Keyboard now has period key, giving more space to other buttons in the toolbar
- Suggestions for classes and bundles are now added to the toolbar
- When nothing has been typed, the default suggestion is the bundle for the current app
- Make the colors closer to the real keyboard colors
Tanner Bennett пре 6 година
родитељ
комит
c3da7e10f7

+ 7 - 3
Classes/GlobalStateExplorers/RuntimeBrowser/FLEXObjcRuntimeViewController.m

@@ -48,9 +48,13 @@
     UISearchBar *searchBar = self.searchController.searchBar;
     UISearchBar *searchBar = self.searchController.searchBar;
     TBKeyPathSearchController *keyPathController = [TBKeyPathSearchController delegate:self];
     TBKeyPathSearchController *keyPathController = [TBKeyPathSearchController delegate:self];
     _keyPathController = keyPathController;
     _keyPathController = keyPathController;
-    _keyPathController.toolbar = [TBKeyPathToolbar toolbarWithHandler:^(NSString *buttonTitle) {
-        [keyPathController didPressButton:buttonTitle insertInto:searchBar];
-    }];
+    _keyPathController.toolbar = [TBKeyPathToolbar toolbarWithHandler:^(NSString *text, BOOL suggestion) {
+        if (suggestion) {
+            [keyPathController didSelectKeyPathOption:text];
+        } else {
+            [keyPathController didPressButton:text insertInto:searchBar];
+        }
+    } suggestions:keyPathController.suggestions];
 }
 }
 
 
 - (void)viewWillAppear:(BOOL)animated {
 - (void)viewWillAppear:(BOOL)animated {

+ 3 - 0
Classes/GlobalStateExplorers/RuntimeBrowser/TBKeyPathSearchController.h

@@ -29,6 +29,9 @@
 
 
 @property (nonatomic) TBKeyPathToolbar *toolbar;
 @property (nonatomic) TBKeyPathToolbar *toolbar;
 
 
+/// Suggestions for the toolbar
+@property (nonatomic, readonly) NSArray<NSString *> *suggestions;
+
 - (void)didSelectKeyPathOption:(NSString *)text;
 - (void)didSelectKeyPathOption:(NSString *)text;
 - (void)didPressButton:(NSString *)text insertInto:(UISearchBar *)searchBar;
 - (void)didPressButton:(NSString *)text insertInto:(UISearchBar *)searchBar;
 
 

+ 44 - 5
Classes/GlobalStateExplorers/RuntimeBrowser/TBKeyPathSearchController.m

@@ -20,9 +20,15 @@
 @interface TBKeyPathSearchController ()
 @interface TBKeyPathSearchController ()
 @property (nonatomic, readonly, weak) id<TBKeyPathSearchControllerDelegate> delegate;
 @property (nonatomic, readonly, weak) id<TBKeyPathSearchControllerDelegate> delegate;
 @property (nonatomic) NSTimer *timer;
 @property (nonatomic) NSTimer *timer;
-@property (nonatomic) NSArray<NSString*> *bundlesOrClasses;
+/// If \c keyPath is \c nil or if it only has a \c bundleKey, this is
+/// a list of bundle key path components like \c UICatalog or \c UIKit\.framework
+/// If \c keyPath has more than a \c bundleKey then it is a list of class names.
+@property (nonatomic) NSArray<NSString *> *bundlesOrClasses;
+/// nil when search bar is empty
 @property (nonatomic) TBKeyPath *keyPath;
 @property (nonatomic) TBKeyPath *keyPath;
 
 
+@property (nonatomic, readonly) NSString *emptySuggestion;
+
 /// Used to track which methods go with which classes. This is used in
 /// Used to track which methods go with which classes. This is used in
 /// two scenarios: (1) when the target class is absolute and has classes,
 /// two scenarios: (1) when the target class is absolute and has classes,
 /// (this list will include the "leaf" class as well as parent classes in this case)
 /// (this list will include the "leaf" class as well as parent classes in this case)
@@ -43,13 +49,15 @@
     TBKeyPathSearchController *controller = [self new];
     TBKeyPathSearchController *controller = [self new];
     controller->_bundlesOrClasses = [TBRuntimeController allBundleNames];
     controller->_bundlesOrClasses = [TBRuntimeController allBundleNames];
     controller->_delegate         = delegate;
     controller->_delegate         = delegate;
+    controller->_emptySuggestion  = NSBundle.mainBundle.executablePath.lastPathComponent;
 
 
     NSParameterAssert(delegate.tableView);
     NSParameterAssert(delegate.tableView);
     NSParameterAssert(delegate.searchController);
     NSParameterAssert(delegate.searchController);
 
 
     delegate.tableView.delegate   = controller;
     delegate.tableView.delegate   = controller;
     delegate.tableView.dataSource = controller;
     delegate.tableView.dataSource = controller;
-    delegate.searchController.searchBar.delegate = controller;    
+    delegate.searchController.searchBar.delegate = controller;   
+    delegate.searchController.searchBar.keyboardType = UIKeyboardTypeWebSearch;
 
 
     return controller;
     return controller;
 }
 }
@@ -112,6 +120,8 @@
 }
 }
 
 
 - (void)didPressButton:(NSString *)text insertInto:(UISearchBar *)searchBar {
 - (void)didPressButton:(NSString *)text insertInto:(UISearchBar *)searchBar {
+    [self.toolbar setKeyPath:self.keyPath suggestions:nil];
+    
     // Available since at least iOS 9, still present in iOS 13
     // Available since at least iOS 9, still present in iOS 13
     UITextField *field = [searchBar valueForKey:@"_searchBarTextField"];
     UITextField *field = [searchBar valueForKey:@"_searchBarTextField"];
 
 
@@ -120,6 +130,31 @@
     }
     }
 }
 }
 
 
+- (NSArray<NSString *> *)suggestions {
+    if (self.bundlesOrClasses) {
+        if (self.classes) {
+            if (self.classesToMethods) {
+                // We have selected a class and are searching metadata
+                return nil;
+            }
+            
+            // We are currently searching classes
+            return [self.filteredClasses flex_subArrayUpto:10];
+        }
+        
+        if (!self.keyPath) {
+            // Search bar is empty
+            return @[self.emptySuggestion];
+        }
+        
+        // We are currently searching bundles
+        return [self.bundlesOrClasses flex_subArrayUpto:10];
+    }
+    
+    // We have nothing at all to even search
+    return nil;
+}
+
 #pragma mark - Filtering + UISearchBarDelegate
 #pragma mark - Filtering + UISearchBarDelegate
 
 
 - (void)updateTable {
 - (void)updateTable {
@@ -161,11 +196,17 @@
         
         
         // Finally, reload the table on the main thread
         // Finally, reload the table on the main thread
         dispatch_async(dispatch_get_main_queue(), ^{
         dispatch_async(dispatch_get_main_queue(), ^{
+            [self updateToolbarButtons];
             [self.delegate.tableView reloadData];
             [self.delegate.tableView reloadData];
         });
         });
     });
     });
 }
 }
 
 
+- (void)updateToolbarButtons {
+    // Update toolbar buttons
+    [self.toolbar setKeyPath:self.keyPath suggestions:self.suggestions];
+}
+
 /// Assign assign .filteredClasses and .classesToMethods after removing empty sections
 /// Assign assign .filteredClasses and .classesToMethods after removing empty sections
 - (void)setNonEmptyMethodLists:(NSMutableArray<NSArray *> *)methods withClasses:(NSMutableArray *)classes {
 - (void)setNonEmptyMethodLists:(NSMutableArray<NSArray *> *)methods withClasses:(NSMutableArray *)classes {
     // Remove sections with no methods
     // Remove sections with no methods
@@ -208,9 +249,6 @@
 - (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
 - (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
     [_timer invalidate];
     [_timer invalidate];
 
 
-    // Update toolbar buttons
-    [self.toolbar setKeyPath:self.keyPath animated:YES];
-
     // Schedule update timer
     // Schedule update timer
     if (searchText.length) {
     if (searchText.length) {
         if (!self.keyPath.methodKey) {
         if (!self.keyPath.methodKey) {
@@ -228,6 +266,7 @@
         _classesToMethods = nil;
         _classesToMethods = nil;
         _classes = nil;
         _classes = nil;
         _keyPath = nil;
         _keyPath = nil;
+        [self updateToolbarButtons];
         [self.delegate.tableView reloadData];
         [self.delegate.tableView reloadData];
     }
     }
 }
 }

+ 2 - 2
Classes/GlobalStateExplorers/RuntimeBrowser/TBKeyPathToolbar.h

@@ -12,8 +12,8 @@
 
 
 @interface TBKeyPathToolbar : TBKeyboardToolbar
 @interface TBKeyPathToolbar : TBKeyboardToolbar
 
 
-+ (instancetype)toolbarWithHandler:(TBToolbarAction)tapHandler;
++ (instancetype)toolbarWithHandler:(TBToolbarAction)tapHandler suggestions:(NSArray<NSString *> *)suggestions;
 
 
-- (void)setKeyPath:(TBKeyPath *)keyPath animated:(BOOL)animated;
+- (void)setKeyPath:(TBKeyPath *)keyPath suggestions:(NSArray<NSString *> *)suggestions;
 
 
 @end
 @end

+ 17 - 11
Classes/GlobalStateExplorers/RuntimeBrowser/TBKeyPathToolbar.m

@@ -16,16 +16,19 @@
 
 
 @implementation TBKeyPathToolbar
 @implementation TBKeyPathToolbar
 
 
-+ (instancetype)toolbarWithHandler:(TBToolbarAction)tapHandler {
-    TBKeyPath *emptyKeyPath = [TBKeyPathTokenizer tokenizeString:@""];
-    NSArray *buttons = [self buttonsForKeyPath:emptyKeyPath handler:tapHandler];
++ (instancetype)toolbarWithHandler:(TBToolbarAction)tapHandler suggestions:(NSArray<NSString *> *)suggestions {
+    NSArray *buttons = [self
+        buttonsForKeyPath:TBKeyPath.empty suggestions:suggestions handler:tapHandler
+    ];
 
 
     TBKeyPathToolbar *me = [self toolbarWithButtons:buttons];
     TBKeyPathToolbar *me = [self toolbarWithButtons:buttons];
     me.tapHandler = tapHandler;
     me.tapHandler = tapHandler;
     return me;
     return me;
 }
 }
 
 
-+ (NSArray<TBToolbarButton*> *)buttonsForKeyPath:(TBKeyPath *)keyPath handler:(TBToolbarAction)handler {
++ (NSArray<TBToolbarButton*> *)buttonsForKeyPath:(TBKeyPath *)keyPath
+                                     suggestions:(NSArray<NSString *> *)suggestions
+                                         handler:(TBToolbarAction)handler {
     NSMutableArray *buttons = [NSMutableArray array];
     NSMutableArray *buttons = [NSMutableArray array];
     TBToken *lastKey = nil;
     TBToken *lastKey = nil;
     BOOL lastKeyIsMethod = NO;
     BOOL lastKeyIsMethod = NO;
@@ -47,9 +50,8 @@
                 }
                 }
                 [buttons addObject:[TBToolbarButton buttonWithTitle:@"*" action:handler]];
                 [buttons addObject:[TBToolbarButton buttonWithTitle:@"*" action:handler]];
             } else {
             } else {
-                [buttons addObject:[TBToolbarButton buttonWithTitle:@"*." action:handler]];
                 [buttons addObject:[TBToolbarButton buttonWithTitle:@"*" action:handler]];
                 [buttons addObject:[TBToolbarButton buttonWithTitle:@"*" action:handler]];
-                [buttons addObject:[TBToolbarButton buttonWithTitle:@"." action:handler]];
+                [buttons addObject:[TBToolbarButton buttonWithTitle:@"*." action:handler]];
             }
             }
             break;
             break;
 
 
@@ -63,25 +65,29 @@
                     if (lastKey.string.length) {
                     if (lastKey.string.length) {
                         [buttons addObject:[TBToolbarButton buttonWithTitle:@"*." action:handler]];
                         [buttons addObject:[TBToolbarButton buttonWithTitle:@"*." action:handler]];
                     }
                     }
-                    [buttons addObject:[TBToolbarButton buttonWithTitle:@"." action:handler]];
                 }
                 }
             }
             }
 
 
             else if (lastKey.options & TBWildcardOptionsSuffix) {
             else if (lastKey.options & TBWildcardOptionsSuffix) {
                 if (!lastKeyIsMethod) {
                 if (!lastKeyIsMethod) {
+                    [buttons addObject:[TBToolbarButton buttonWithTitle:@"*" action:handler]];
                     [buttons addObject:[TBToolbarButton buttonWithTitle:@"*." action:handler]];
                     [buttons addObject:[TBToolbarButton buttonWithTitle:@"*." action:handler]];
-                    [buttons addObject:[TBToolbarButton buttonWithTitle:@"." action:handler]];
                 }
                 }
             }
             }
         }
         }
     }
     }
+    
+    for (NSString *suggestion in suggestions) {
+        [buttons addObject:[TBToolbarSuggestedButton buttonWithTitle:suggestion action:handler]];
+    }
 
 
     return buttons;
     return buttons;
 }
 }
 
 
-- (void)setKeyPath:(TBKeyPath *)keyPath animated:(BOOL)animated {
-    NSArray *buttons = [TBKeyPathToolbar buttonsForKeyPath:keyPath handler:self.tapHandler];
-    [self setButtons:buttons animated:animated];
+- (void)setKeyPath:(TBKeyPath *)keyPath suggestions:(NSArray<NSString *> *)suggestions {
+    self.buttons = [self.class
+        buttonsForKeyPath:keyPath suggestions:suggestions handler:self.tapHandler
+    ];
 }
 }
 
 
 @end
 @end

+ 0 - 2
Classes/GlobalStateExplorers/RuntimeBrowser/TBKeyboardToolbar.h

@@ -14,8 +14,6 @@
 
 
 + (instancetype)toolbarWithButtons:(NSArray *)buttons;
 + (instancetype)toolbarWithButtons:(NSArray *)buttons;
 
 
-- (void)setButtons:(NSArray<TBToolbarButton*> *)buttons animated:(BOOL)animated;
-
 @property (nonatomic) NSArray<TBToolbarButton*> *buttons;
 @property (nonatomic) NSArray<TBToolbarButton*> *buttons;
 @property (nonatomic) UIKeyboardAppearance appearance;
 @property (nonatomic) UIKeyboardAppearance appearance;
 
 

+ 75 - 75
Classes/GlobalStateExplorers/RuntimeBrowser/TBKeyboardToolbar.m

@@ -8,6 +8,8 @@
 #import "FLEXUtility.h"
 #import "FLEXUtility.h"
 
 
 #define kToolbarHeight 44
 #define kToolbarHeight 44
+#define kButtonSpacing 6
+#define kScrollViewHorizontalMargins 3
 
 
 @interface TBKeyboardToolbar ()
 @interface TBKeyboardToolbar ()
 
 
@@ -59,98 +61,134 @@
 - (void)layoutSubviews {
 - (void)layoutSubviews {
     [super layoutSubviews];
     [super layoutSubviews];
     
     
+    // Layout top border
     CGRect frame = _toolbarView.bounds;
     CGRect frame = _toolbarView.bounds;
-    frame.size.height = 0.5f;
-    
+    frame.size.height = 0.5;
     _topBorder.frame = frame;
     _topBorder.frame = frame;
+    
+    // Scroll view //
+    
+    frame = CGRectMake(0, 0, self.bounds.size.width, kToolbarHeight);
+    CGSize contentSize = self.scrollView.contentSize;
+    CGFloat scrollViewWidth = frame.size.width;
+    
+    // If our content size is smaller than the scroll view,
+    // we want to right-align all the content
+    if (contentSize.width < scrollViewWidth) {
+        // Compute the content size to scroll view size difference
+        UIEdgeInsets insets = self.scrollView.contentInset;
+        CGFloat margin = insets.left + insets.right;
+        CGFloat difference = scrollViewWidth - contentSize.width - margin;
+        // Update the content size to be the full width of the scroll view
+        contentSize.width += difference;
+        self.scrollView.contentSize = contentSize;
+        
+        // Offset every button by the difference above
+        // so that every button appears right-aligned
+        for (UIView *button in self.scrollView.subviews) {
+            CGRect f = button.frame;
+            f.origin.x += difference;
+            button.frame = f;
+        }
+    }
 }
 }
 
 
 - (UIView *)inputAccessoryView {
 - (UIView *)inputAccessoryView {
     _topBorder       = [CALayer layer];
     _topBorder       = [CALayer layer];
-    _topBorder.frame = CGRectMake(0.0f, 0.0f, self.bounds.size.width, 0.5f);
+    _topBorder.frame = CGRectMake(0.0, 0.0, self.bounds.size.width, 0.5);
+    [self makeScrollView];
     
     
-    UIColor *borderColor = nil;
-    UIBlurEffectStyle style;
+    UIColor *borderColor = nil, *backgroundColor = nil;
+    UIColor *lightColor = [UIColor colorWithHue:216.0/360.0 saturation:0.05 brightness:0.85 alpha:1];
+    UIColor *darkColor = [UIColor colorWithHue:220.0/360.0 saturation:0.07 brightness:0.16 alpha:1];
     
     
     switch (_appearance) {
     switch (_appearance) {
         case UIKeyboardAppearanceDefault:
         case UIKeyboardAppearanceDefault:
             #if FLEX_AT_LEAST_IOS13_SDK
             #if FLEX_AT_LEAST_IOS13_SDK
             if (@available(iOS 13, *)) {
             if (@available(iOS 13, *)) {
-                borderColor = [UIColor systemBackgroundColor];
+                borderColor = UIColor.systemBackgroundColor;
                 
                 
                 if (self.usingDarkMode) {
                 if (self.usingDarkMode) {
-                    style = UIBlurEffectStyleSystemThickMaterial;
-                    self.backgroundColor = nil;
+                    // style = UIBlurEffectStyleSystemThickMaterial;
+                    backgroundColor = darkColor;
                 } else {
                 } else {
-                    style = UIBlurEffectStyleSystemUltraThinMaterialLight;
-                    self.backgroundColor = [UIColor colorWithWhite:0.700 alpha:0.750];
+                    // style = UIBlurEffectStyleSystemUltraThinMaterialLight;
+                    backgroundColor = lightColor;
                 }
                 }
                 break;
                 break;
             }
             }
             #endif
             #endif
         case UIKeyboardAppearanceLight: {
         case UIKeyboardAppearanceLight: {
-            style = UIBlurEffectStyleLight;
-            borderColor = [UIColor clearColor];
+            borderColor = UIColor.clearColor;
+            backgroundColor = lightColor;
             break;
             break;
         }
         }
         case UIKeyboardAppearanceDark: {
         case UIKeyboardAppearanceDark: {
-            style = UIBlurEffectStyleDark;
             borderColor = [UIColor colorWithWhite:0.100 alpha:1.000];
             borderColor = [UIColor colorWithWhite:0.100 alpha:1.000];
+            backgroundColor = darkColor;
             break;
             break;
         }
         }
     }
     }
     
     
-    UIVisualEffect *blur = [UIBlurEffect effectWithStyle:style];
-    self.blurView = [[UIVisualEffectView alloc] initWithEffect:blur];
-    [self.blurView.contentView.layer addSublayer:self.topBorder];
-    [self.blurView.contentView addSubview:[self fakeToolbar]];
-    
-    self.toolbarView = self.blurView;
+    self.toolbarView = [UIView new];
+    [self.toolbarView addSubview:self.scrollView];
+    [self.toolbarView.layer addSublayer:self.topBorder];
     self.toolbarView.frame = CGRectMake(0, 0, self.bounds.size.width, kToolbarHeight);
     self.toolbarView.frame = CGRectMake(0, 0, self.bounds.size.width, kToolbarHeight);
     self.toolbarView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
     self.toolbarView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
     
     
+    self.backgroundColor = backgroundColor;
     self.topBorder.backgroundColor = borderColor.CGColor;
     self.topBorder.backgroundColor = borderColor.CGColor;
     
     
     return self.toolbarView;
     return self.toolbarView;
 }
 }
 
 
-- (UIScrollView *)fakeToolbar {
-    _scrollView                  = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, self.bounds.size.width, kToolbarHeight)];
-    _scrollView.backgroundColor  = [UIColor clearColor];
-    _scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
-    _scrollView.contentInset     = UIEdgeInsetsMake(8.0f, 0.0f, 4.0f, 6.0f);
-    _scrollView.showsHorizontalScrollIndicator = NO;
+- (UIScrollView *)makeScrollView {
+    UIScrollView *scrollView = [UIScrollView new];
+    scrollView.backgroundColor  = UIColor.clearColor;
+    scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
+    scrollView.contentInset     = UIEdgeInsetsMake(
+        8.f, kScrollViewHorizontalMargins, 4.f, kScrollViewHorizontalMargins
+    );
+    scrollView.showsHorizontalScrollIndicator = NO;
     
     
+    self.scrollView = scrollView;
     [self addButtons];
     [self addButtons];
     
     
-    return _scrollView;
+    return scrollView;
 }
 }
 
 
 - (void)addButtons {
 - (void)addButtons {
-    NSUInteger spacing = 6;
-    NSUInteger originX = spacing;
+    NSUInteger originX = 0.f;
     
     
     CGRect originFrame;
     CGRect originFrame;
-    CGFloat top    = _scrollView.contentInset.top;
-    CGFloat bottom = _scrollView.contentInset.bottom;
+    CGFloat top    = self.scrollView.contentInset.top;
+    CGFloat bottom = self.scrollView.contentInset.bottom;
     
     
-    for (TBToolbarButton *button in _buttons) {
+    for (TBToolbarButton *button in self.buttons) {
         button.appearance = self.appearance;
         button.appearance = self.appearance;
         
         
         originFrame             = button.frame;
         originFrame             = button.frame;
         originFrame.origin.x    = originX;
         originFrame.origin.x    = originX;
-        originFrame.origin.y    = 0;
+        originFrame.origin.y    = 0.f;
         originFrame.size.height = kToolbarHeight - (top + bottom);
         originFrame.size.height = kToolbarHeight - (top + bottom);
         button.frame            = originFrame;
         button.frame            = originFrame;
         
         
-        [_scrollView addSubview:button];
+        [self.scrollView addSubview:button];
         
         
-        originX += button.bounds.size.width + spacing;
+        // originX tracks the origin of the next button to be added,
+        // so at the end of each iteration of this loop we increment
+        // it by the size of the last button with some padding
+        originX += button.bounds.size.width + kButtonSpacing;
     }
     }
     
     
-    CGSize contentSize = _scrollView.contentSize;
-    contentSize.width  = originX - spacing;
-    _scrollView.contentSize = contentSize;
+    // Update contentSize,
+    // set to the max x value of the last button added
+    CGSize contentSize = self.scrollView.contentSize;
+    contentSize.width  = originX - kButtonSpacing;
+    self.scrollView.contentSize = contentSize;
+    
+    // Needed to potentially right-align buttons
+    [self setNeedsLayout];
 }
 }
 
 
 - (void)setButtons:(NSArray<TBToolbarButton *> *)buttons {
 - (void)setButtons:(NSArray<TBToolbarButton *> *)buttons {
@@ -160,44 +198,6 @@
     [self addButtons];
     [self addButtons];
 }
 }
 
 
-- (void)setButtons:(NSArray<TBToolbarButton *> *)buttons animated:(BOOL)animated {
-    if (!animated) {
-        self.buttons = buttons;
-        return;
-    }
-    
-    NSMutableSet *buttonstoRemove = [NSMutableSet setWithArray:_buttons];
-    [buttonstoRemove minusSet:[NSSet setWithArray:buttons]];
-
-    NSMutableSet *buttonsToAdd = [NSMutableSet setWithArray:buttons];
-    [buttonsToAdd minusSet:[NSSet setWithArray:_buttons]];
-
-    if (!buttonstoRemove.count && !buttonsToAdd.count) {
-        return;
-    }
-
-    // New buttons are invisible at first
-    for (TBToolbarButton *button in buttons) {
-        button.alpha = 0;
-    }
-
-    [UIView animateWithDuration:0.1 animations:^{
-        // Fade out old buttons
-        for (TBToolbarButton *button in _buttons) {
-            button.alpha = 0;
-        }
-    } completion:^(BOOL finished) {
-        // Remove old, add new
-        self.buttons = buttons;
-        [UIView animateWithDuration:0.1 animations:^{
-            // Fade in new buttons
-            for (TBToolbarButton *button in buttons) {
-                button.alpha = 1;
-            }
-        }];
-    }];
-}
-
 - (BOOL)useSystemAppearance {
 - (BOOL)useSystemAppearance {
     return self.appearance == UIKeyboardAppearanceDefault;
     return self.appearance == UIKeyboardAppearanceDefault;
 }
 }

+ 2 - 0
Classes/GlobalStateExplorers/RuntimeBrowser/TBToken.h

@@ -30,5 +30,7 @@ typedef NS_OPTIONS(NSUInteger, TBWildcardOptions) {
 /// Opposite of "is ambiguous"
 /// Opposite of "is ambiguous"
 @property (nonatomic, readonly) BOOL isAbsolute;
 @property (nonatomic, readonly) BOOL isAbsolute;
 @property (nonatomic, readonly) BOOL isAny;
 @property (nonatomic, readonly) BOOL isAny;
+/// Still \c isAny, but checks that the string is empty
+@property (nonatomic, readonly) BOOL isEmpty;
 
 
 @end
 @end

+ 4 - 0
Classes/GlobalStateExplorers/RuntimeBrowser/TBToken.m

@@ -40,6 +40,10 @@
     return _options == TBWildcardOptionsAny;
     return _options == TBWildcardOptionsAny;
 }
 }
 
 
+- (BOOL)isEmpty {
+    return self.isAny && self.string.length == 0;
+}
+
 - (NSString *)description {
 - (NSString *)description {
     if (tb_description) {
     if (tb_description) {
         return tb_description;
         return tb_description;

+ 3 - 1
Classes/GlobalStateExplorers/RuntimeBrowser/TBToolbarButton.h

@@ -6,7 +6,7 @@
 
 
 #import <UIKit/UIKit.h>
 #import <UIKit/UIKit.h>
 
 
-typedef void (^TBToolbarAction)(NSString *buttonTitle);
+typedef void (^TBToolbarAction)(NSString *buttonTitle, BOOL isSuggestion);
 
 
 
 
 @interface TBToolbarButton : UIButton
 @interface TBToolbarButton : UIButton
@@ -25,3 +25,5 @@ typedef void (^TBToolbarAction)(NSString *buttonTitle);
 - (void)addEventHandler:(TBToolbarAction)eventHandler forControlEvents:(UIControlEvents)controlEvents;
 - (void)addEventHandler:(TBToolbarAction)eventHandler forControlEvents:(UIControlEvents)controlEvents;
 
 
 @end
 @end
+
+@interface TBToolbarSuggestedButton : TBToolbarButton @end

+ 33 - 38
Classes/GlobalStateExplorers/RuntimeBrowser/TBToolbarButton.m

@@ -7,11 +7,11 @@
 #import "TBToolbarButton.h"
 #import "TBToolbarButton.h"
 #import "UIFont+FLEX.h"
 #import "UIFont+FLEX.h"
 #import "FLEXUtility.h"
 #import "FLEXUtility.h"
+#import "CALayer+FLEX.h"
 
 
 @interface TBToolbarButton ()
 @interface TBToolbarButton ()
 @property (nonatomic      ) NSString *title;
 @property (nonatomic      ) NSString *title;
 @property (nonatomic, copy) TBToolbarAction buttonPressBlock;
 @property (nonatomic, copy) TBToolbarAction buttonPressBlock;
-@property (nonatomic      ) UIView *backgroundView;
 /// YES if appearance is set to `default`
 /// YES if appearance is set to `default`
 @property (nonatomic, readonly) BOOL useSystemAppearance;
 @property (nonatomic, readonly) BOOL useSystemAppearance;
 /// YES if the current trait collection is set to dark mode and \c useSystemAppearance is YES
 /// YES if the current trait collection is set to dark mode and \c useSystemAppearance is YES
@@ -25,7 +25,7 @@
 }
 }
 
 
 + (instancetype)buttonWithTitle:(NSString *)title action:(TBToolbarAction)eventHandler forControlEvents:(UIControlEvents)controlEvent {
 + (instancetype)buttonWithTitle:(NSString *)title action:(TBToolbarAction)eventHandler forControlEvents:(UIControlEvents)controlEvent {
-    TBToolbarButton *newButton = [TBToolbarButton buttonWithTitle:title];
+    TBToolbarButton *newButton = [self buttonWithTitle:title];
     [newButton addEventHandler:eventHandler forControlEvents:controlEvent];
     [newButton addEventHandler:eventHandler forControlEvents:controlEvent];
     return newButton;
     return newButton;
 }
 }
@@ -39,12 +39,12 @@
     if (self) {
     if (self) {
         _title = title;
         _title = title;
         self.layer.shadowOffset = CGSizeMake(0, 1);
         self.layer.shadowOffset = CGSizeMake(0, 1);
-        self.layer.shadowOpacity = 0.25f;
-        self.layer.shadowRadius  = 0.f;
-        self.layer.cornerRadius  = 5.f;
-        self.layer.borderWidth   = 1.f;
+        self.layer.shadowOpacity = 0.35;
+        self.layer.shadowRadius  = 0;
+        self.layer.cornerRadius  = 5;
         self.clipsToBounds       = NO;
         self.clipsToBounds       = NO;
-        self.titleLabel.font     = [UIFont flex_codeFont];
+        self.titleLabel.font     = [UIFont systemFontOfSize:18.0];
+        self.layer.flex_continuousCorners = YES;
         [self setTitle:self.title forState:UIControlStateNormal];
         [self setTitle:self.title forState:UIControlStateNormal];
         [self sizeToFit];
         [self sizeToFit];
         
         
@@ -55,7 +55,7 @@
         }
         }
         
         
         CGRect frame = self.frame;
         CGRect frame = self.frame;
-        frame.size.width  += 40;
+        frame.size.width  += title.length < 3 ? 30 : 15;
         frame.size.height += 10;
         frame.size.height += 10;
         self.frame = frame;
         self.frame = frame;
     }
     }
@@ -69,61 +69,47 @@
 }
 }
 
 
 - (void)buttonPressed {
 - (void)buttonPressed {
-    self.buttonPressBlock(self.title);
+    self.buttonPressBlock(self.title, NO);
 }
 }
 
 
 - (void)setAppearance:(UIKeyboardAppearance)appearance {
 - (void)setAppearance:(UIKeyboardAppearance)appearance {
     _appearance = appearance;
     _appearance = appearance;
     
     
-    if (self.backgroundView.superview) {
-        [self.backgroundView removeFromSuperview];
-    }
-    
-    UIColor *titleColor = nil, *borderColor = nil;
-    UIBlurEffectStyle style;
+    UIColor *titleColor = nil, *backgroundColor = nil;
+    UIColor *lightColor = [UIColor colorWithRed:253.0/255.0 green:253.0/255.0 blue:254.0/255.0 alpha:1];
+    UIColor *darkColor = [UIColor colorWithRed:101.0/255.0 green:102.0/255.0 blue:104.0/255.0 alpha:1];
     
     
     switch (_appearance) {
     switch (_appearance) {
         default:
         default:
         case UIKeyboardAppearanceDefault:
         case UIKeyboardAppearanceDefault:
             #if FLEX_AT_LEAST_IOS13_SDK
             #if FLEX_AT_LEAST_IOS13_SDK
             if (@available(iOS 13, *)) {
             if (@available(iOS 13, *)) {
-                borderColor = [UIColor clearColor];
-                titleColor = [UIColor labelColor];
+                titleColor = UIColor.labelColor;
                 
                 
                 if (self.usingDarkMode) {
                 if (self.usingDarkMode) {
-                    style = UIBlurEffectStyleSystemUltraThinMaterialLight;
+                    // style = UIBlurEffectStyleSystemUltraThinMaterialLight;
+                    backgroundColor = darkColor;
                 } else {
                 } else {
-                    style = UIBlurEffectStyleSystemMaterialLight;
+                    // style = UIBlurEffectStyleSystemMaterialLight;
+                    backgroundColor = lightColor;
                 }
                 }
                 break;
                 break;
             }
             }
             #endif
             #endif
         case UIKeyboardAppearanceLight:
         case UIKeyboardAppearanceLight:
-            borderColor = [UIColor colorWithWhite:1.000 alpha:0.500];
-            titleColor = [UIColor blackColor];
-            style = UIBlurEffectStyleRegular;
+            titleColor = UIColor.blackColor;
+            backgroundColor = lightColor;
+            // style = UIBlurEffectStyleExtraLight;
             break;
             break;
         case UIKeyboardAppearanceDark:
         case UIKeyboardAppearanceDark:
-            borderColor = [UIColor clearColor];
-            titleColor = [UIColor whiteColor];
-            style = UIBlurEffectStyleDark;
+            titleColor = UIColor.whiteColor;
+            backgroundColor = darkColor;
+            // style = UIBlurEffectStyleDark;
             break;
             break;
     }
     }
     
     
-    self.layer.borderColor = borderColor.CGColor;
+    self.backgroundColor = backgroundColor;
     [self setTitleColor:titleColor forState:UIControlStateNormal];
     [self setTitleColor:titleColor forState:UIControlStateNormal];
-    
-    UIVisualEffect *blur = [UIBlurEffect effectWithStyle:style];
-    self.backgroundView = [[UIVisualEffectView alloc] initWithEffect:blur];
-    self.backgroundView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
-    self.backgroundView.layer.cornerRadius = self.layer.cornerRadius;
-    self.backgroundView.clipsToBounds = YES;
-    // Without these, the background view blocks the button's touches
-    self.backgroundView.userInteractionEnabled = NO;
-    self.backgroundView.exclusiveTouch = NO;
-    
-    [self insertSubview:self.backgroundView atIndex:0];
-    self.backgroundView.frame = self.bounds;
 }
 }
 
 
 - (BOOL)isEqual:(id)object {
 - (BOOL)isEqual:(id)object {
@@ -163,3 +149,12 @@
 }
 }
 
 
 @end
 @end
+
+
+@implementation TBToolbarSuggestedButton
+
+- (void)buttonPressed {
+    self.buttonPressBlock(self.title, YES);
+}
+
+@end

+ 15 - 0
Classes/Utility/Categories/CALayer+FLEX.h

@@ -0,0 +1,15 @@
+//
+//  CALayer+FLEX.h
+//  FLEX
+//
+//  Created by Tanner on 2/28/20.
+//  Copyright © 2020 Flipboard. All rights reserved.
+//
+
+#import <QuartzCore/QuartzCore.h>
+
+@interface CALayer (FLEX)
+
+@property (nonatomic) BOOL flex_continuousCorners;
+
+@end

+ 46 - 0
Classes/Utility/Categories/CALayer+FLEX.m

@@ -0,0 +1,46 @@
+//
+//  CALayer+FLEX.m
+//  FLEX
+//
+//  Created by Tanner on 2/28/20.
+//  Copyright © 2020 Flipboard. All rights reserved.
+//
+
+#import "CALayer+FLEX.h"
+
+@interface CALayer (Private)
+@property (nonatomic) BOOL continuousCorners;
+@end
+
+@implementation CALayer (FLEX)
+
+static BOOL respondsToContinuousCorners = NO;
+
++ (void)load {
+    respondsToContinuousCorners = [CALayer
+        instancesRespondToSelector:@selector(setContinuousCorners:)
+    ];
+}
+
+- (BOOL)flex_continuousCorners {
+    if (respondsToContinuousCorners) {
+        return self.continuousCorners;
+    }
+    
+    return NO;
+}
+
+- (void)setFlex_continuousCorners:(BOOL)enabled {
+    if (respondsToContinuousCorners) {
+        if (@available(iOS 13, *)) {
+            self.cornerCurve = kCACornerCurveContinuous;
+        } else {
+            self.continuousCorners = enabled;
+//            self.masksToBounds = NO;
+    //        self.allowsEdgeAntialiasing = YES;
+    //        self.edgeAntialiasingMask = kCALayerLeftEdge | kCALayerRightEdge | kCALayerTopEdge | kCALayerBottomEdge;
+        }
+    }
+}
+
+@end

+ 5 - 0
Classes/Utility/Categories/NSArray+Functional.h

@@ -19,6 +19,11 @@
 - (instancetype)flex_filtered:(BOOL(^)(T obj, NSUInteger idx))filterFunc;
 - (instancetype)flex_filtered:(BOOL(^)(T obj, NSUInteger idx))filterFunc;
 - (void)flex_forEach:(void(^)(T obj, NSUInteger idx))block;
 - (void)flex_forEach:(void(^)(T obj, NSUInteger idx))block;
 
 
+/// Unlike \c subArrayWithRange: this will not throw an exception if \c maxLength
+/// is greater than the size of the array. If the array has one element and
+/// \c maxLength is greater than 1, you get an array with 1 element back.
+- (instancetype)flex_subArrayUpto:(NSUInteger)maxLength;
+
 + (instancetype)flex_forEachUpTo:(NSUInteger)bound map:(T(^)(NSUInteger))block;
 + (instancetype)flex_forEachUpTo:(NSUInteger)bound map:(T(^)(NSUInteger))block;
 
 
 - (instancetype)sortedUsingSelector:(SEL)selector;
 - (instancetype)sortedUsingSelector:(SEL)selector;

+ 12 - 0
Classes/Utility/Categories/NSArray+Functional.m

@@ -56,6 +56,18 @@
     }];
     }];
 }
 }
 
 
+- (instancetype)flex_subArrayUpto:(NSUInteger)maxLength {
+    if (maxLength > self.count) {
+        if (FLEXArrayClassIsMutable(self)) {
+            return self.mutableCopy;
+        }
+        
+        return self;
+    }
+    
+    return [self subarrayWithRange:NSMakeRange(0, maxLength)];
+}
+
 + (__kindof NSArray *)flex_forEachUpTo:(NSUInteger)bound map:(id(^)(NSUInteger))block {
 + (__kindof NSArray *)flex_forEachUpTo:(NSUInteger)bound map:(id(^)(NSUInteger))block {
     NSMutableArray *array = [NSMutableArray new];
     NSMutableArray *array = [NSMutableArray new];
     for (NSUInteger i = 0; i < bound; i++) {
     for (NSUInteger i = 0; i < bound; i++) {

+ 8 - 0
FLEX.xcodeproj/project.pbxproj

@@ -141,6 +141,8 @@
 		94AAF0381BAF2E1F00DE8760 /* FLEXKeyboardHelpViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 94AAF0361BAF2E1F00DE8760 /* FLEXKeyboardHelpViewController.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		94AAF0381BAF2E1F00DE8760 /* FLEXKeyboardHelpViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 94AAF0361BAF2E1F00DE8760 /* FLEXKeyboardHelpViewController.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		94AAF0391BAF2E1F00DE8760 /* FLEXKeyboardHelpViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 94AAF0371BAF2E1F00DE8760 /* FLEXKeyboardHelpViewController.m */; };
 		94AAF0391BAF2E1F00DE8760 /* FLEXKeyboardHelpViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 94AAF0371BAF2E1F00DE8760 /* FLEXKeyboardHelpViewController.m */; };
 		94AAF03A1BAF2F0300DE8760 /* FLEXKeyboardShortcutManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 942DCD821BAE0AD300DB5DC2 /* FLEXKeyboardShortcutManager.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		94AAF03A1BAF2F0300DE8760 /* FLEXKeyboardShortcutManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 942DCD821BAE0AD300DB5DC2 /* FLEXKeyboardShortcutManager.h */; settings = {ATTRIBUTES = (Private, ); }; };
+		C301994A2409B38A00759E8E /* CALayer+FLEX.h in Headers */ = {isa = PBXBuildFile; fileRef = C30199482409B38A00759E8E /* CALayer+FLEX.h */; };
+		C301994B2409B38A00759E8E /* CALayer+FLEX.m in Sources */ = {isa = PBXBuildFile; fileRef = C30199492409B38A00759E8E /* CALayer+FLEX.m */; };
 		C309B82F223ED64400B228EC /* FLEXLogController.h in Headers */ = {isa = PBXBuildFile; fileRef = C309B82D223ED64400B228EC /* FLEXLogController.h */; };
 		C309B82F223ED64400B228EC /* FLEXLogController.h in Headers */ = {isa = PBXBuildFile; fileRef = C309B82D223ED64400B228EC /* FLEXLogController.h */; };
 		C312A13023ECB5D300E38049 /* FLEXBookmarkManager.h in Headers */ = {isa = PBXBuildFile; fileRef = C312A12E23ECB5D300E38049 /* FLEXBookmarkManager.h */; };
 		C312A13023ECB5D300E38049 /* FLEXBookmarkManager.h in Headers */ = {isa = PBXBuildFile; fileRef = C312A12E23ECB5D300E38049 /* FLEXBookmarkManager.h */; };
 		C312A13123ECB5D300E38049 /* FLEXBookmarkManager.m in Sources */ = {isa = PBXBuildFile; fileRef = C312A12F23ECB5D300E38049 /* FLEXBookmarkManager.m */; };
 		C312A13123ECB5D300E38049 /* FLEXBookmarkManager.m in Sources */ = {isa = PBXBuildFile; fileRef = C312A12F23ECB5D300E38049 /* FLEXBookmarkManager.m */; };
@@ -480,6 +482,8 @@
 		94A515241C4CA2080063292F /* FLEXToolbarItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FLEXToolbarItem.m; path = Classes/Toolbar/FLEXToolbarItem.m; sourceTree = SOURCE_ROOT; };
 		94A515241C4CA2080063292F /* FLEXToolbarItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FLEXToolbarItem.m; path = Classes/Toolbar/FLEXToolbarItem.m; sourceTree = SOURCE_ROOT; };
 		94AAF0361BAF2E1F00DE8760 /* FLEXKeyboardHelpViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXKeyboardHelpViewController.h; sourceTree = "<group>"; };
 		94AAF0361BAF2E1F00DE8760 /* FLEXKeyboardHelpViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXKeyboardHelpViewController.h; sourceTree = "<group>"; };
 		94AAF0371BAF2E1F00DE8760 /* FLEXKeyboardHelpViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXKeyboardHelpViewController.m; sourceTree = "<group>"; };
 		94AAF0371BAF2E1F00DE8760 /* FLEXKeyboardHelpViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXKeyboardHelpViewController.m; sourceTree = "<group>"; };
+		C30199482409B38A00759E8E /* CALayer+FLEX.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CALayer+FLEX.h"; sourceTree = "<group>"; };
+		C30199492409B38A00759E8E /* CALayer+FLEX.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "CALayer+FLEX.m"; sourceTree = "<group>"; };
 		C309B82D223ED64400B228EC /* FLEXLogController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXLogController.h; sourceTree = "<group>"; };
 		C309B82D223ED64400B228EC /* FLEXLogController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXLogController.h; sourceTree = "<group>"; };
 		C312A12E23ECB5D300E38049 /* FLEXBookmarkManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXBookmarkManager.h; sourceTree = "<group>"; };
 		C312A12E23ECB5D300E38049 /* FLEXBookmarkManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXBookmarkManager.h; sourceTree = "<group>"; };
 		C312A12F23ECB5D300E38049 /* FLEXBookmarkManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXBookmarkManager.m; sourceTree = "<group>"; };
 		C312A12F23ECB5D300E38049 /* FLEXBookmarkManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXBookmarkManager.m; sourceTree = "<group>"; };
@@ -1186,6 +1190,8 @@
 				C36B096423E0D4A1008F5D47 /* UIMenu+FLEX.m */,
 				C36B096423E0D4A1008F5D47 /* UIMenu+FLEX.m */,
 				C3694DC023EA147F006625D7 /* UIBarButtonItem+FLEX.h */,
 				C3694DC023EA147F006625D7 /* UIBarButtonItem+FLEX.h */,
 				C3694DC123EA147F006625D7 /* UIBarButtonItem+FLEX.m */,
 				C3694DC123EA147F006625D7 /* UIBarButtonItem+FLEX.m */,
+				C30199482409B38A00759E8E /* CALayer+FLEX.h */,
+				C30199492409B38A00759E8E /* CALayer+FLEX.m */,
 			);
 			);
 			path = Categories;
 			path = Categories;
 			sourceTree = "<group>";
 			sourceTree = "<group>";
@@ -1441,6 +1447,7 @@
 				C36FBFCC230F3B98008D95D5 /* FLEXProtocolBuilder.h in Headers */,
 				C36FBFCC230F3B98008D95D5 /* FLEXProtocolBuilder.h in Headers */,
 				C36FBFDB230F3B98008D95D5 /* FLEXClassBuilder.h in Headers */,
 				C36FBFDB230F3B98008D95D5 /* FLEXClassBuilder.h in Headers */,
 				779B1ED41C0C4D7C001F5E49 /* FLEXTableContentCell.h in Headers */,
 				779B1ED41C0C4D7C001F5E49 /* FLEXTableContentCell.h in Headers */,
+				C301994A2409B38A00759E8E /* CALayer+FLEX.h in Headers */,
 				3A4C952C1B5B21410088C3F2 /* FLEXLiveObjectsTableViewController.h in Headers */,
 				3A4C952C1B5B21410088C3F2 /* FLEXLiveObjectsTableViewController.h in Headers */,
 				3A4C94EF1B5B21410088C3F2 /* FLEXArgumentInputDateView.h in Headers */,
 				3A4C94EF1B5B21410088C3F2 /* FLEXArgumentInputDateView.h in Headers */,
 				C36FBFCE230F3B98008D95D5 /* FLEXProperty.h in Headers */,
 				C36FBFCE230F3B98008D95D5 /* FLEXProperty.h in Headers */,
@@ -1645,6 +1652,7 @@
 				C36FBFD4230F3B98008D95D5 /* FLEXProtocol.m in Sources */,
 				C36FBFD4230F3B98008D95D5 /* FLEXProtocol.m in Sources */,
 				C34D4EB123A2ABD900C1F903 /* FLEXLayerShortcuts.m in Sources */,
 				C34D4EB123A2ABD900C1F903 /* FLEXLayerShortcuts.m in Sources */,
 				C38F3F32230C958F004E3731 /* FLEXAlert.m in Sources */,
 				C38F3F32230C958F004E3731 /* FLEXAlert.m in Sources */,
+				C301994B2409B38A00759E8E /* CALayer+FLEX.m in Sources */,
 				3A4C95081B5B21410088C3F2 /* FLEXDefaultEditorViewController.m in Sources */,
 				3A4C95081B5B21410088C3F2 /* FLEXDefaultEditorViewController.m in Sources */,
 				779B1ED11C0C4D7C001F5E49 /* FLEXMultiColumnTableView.m in Sources */,
 				779B1ED11C0C4D7C001F5E49 /* FLEXMultiColumnTableView.m in Sources */,
 				C36FBFD0230F3B98008D95D5 /* FLEXProperty.m in Sources */,
 				C36FBFD0230F3B98008D95D5 /* FLEXProperty.m in Sources */,