Browse Source

Refactor FLEXToolbar*, add a "recent" button

Recent button presents most recently active tab, if any
Tanner Bennett 4 years ago
parent
commit
e78947260d

+ 18 - 4
Classes/ExplorerInterface/FLEXExplorerViewController.m

@@ -372,6 +372,7 @@ typedef NS_ENUM(NSUInteger, FLEXExplorerMode) {
     NSDictionary<NSString *, FLEXToolbarItem *> *actionsToItems = @{
         NSStringFromSelector(@selector(selectButtonTapped:)):        toolbar.selectItem,
         NSStringFromSelector(@selector(hierarchyButtonTapped:)):     toolbar.hierarchyItem,
+        NSStringFromSelector(@selector(recentButtonTapped:)):        toolbar.recentItem,
         NSStringFromSelector(@selector(moveButtonTapped:)):          toolbar.moveItem,
         NSStringFromSelector(@selector(globalsButtonTapped:)):       toolbar.globalsItem,
         NSStringFromSelector(@selector(closeButtonTapped:)):         toolbar.closeItem,
@@ -395,6 +396,11 @@ typedef NS_ENUM(NSUInteger, FLEXExplorerMode) {
     return [UIApplication.sharedApplication valueForKey:statusBarString];
 }
 
+- (void)recentButtonTapped:(FLEXToolbarItem *)sender {
+    NSAssert(FLEXTabList.sharedList.activeTab, @"Must have active tab");
+    [self presentViewController:FLEXTabList.sharedList.activeTab animated:YES completion:nil];
+}
+
 - (void)moveButtonTapped:(FLEXToolbarItem *)sender {
     [self toggleMoveTool];
 }
@@ -409,11 +415,17 @@ typedef NS_ENUM(NSUInteger, FLEXExplorerMode) {
 }
 
 - (void)updateButtonStates {
-    // Move and details only active when an object is selected.
+    FLEXExplorerToolbar *toolbar = self.explorerToolbar;
+    
+    toolbar.selectItem.selected = self.currentMode == FLEXExplorerModeSelect;
+    
+    // Move only enabled when an object is selected.
     BOOL hasSelectedObject = self.selectedView != nil;
-    self.explorerToolbar.moveItem.enabled = hasSelectedObject;
-    self.explorerToolbar.selectItem.selected = self.currentMode == FLEXExplorerModeSelect;
-    self.explorerToolbar.moveItem.selected = self.currentMode == FLEXExplorerModeMove;
+    toolbar.moveItem.enabled = hasSelectedObject;
+    toolbar.moveItem.selected = self.currentMode == FLEXExplorerModeMove;
+    
+    // Recent only enabled when we have a last active tab
+    toolbar.recentItem.enabled = FLEXTabList.sharedList.activeTab != nil;
 }
 
 
@@ -832,6 +844,8 @@ typedef NS_ENUM(NSUInteger, FLEXExplorerMode) {
     // scroll to top, but below FLEX otherwise for exploration.
     [self statusWindow].windowLevel = UIWindowLevelStatusBar;
     
+    [self updateButtonStates];
+    
     [super dismissViewControllerAnimated:animated completion:completion];
 }
 

+ 7 - 6
Classes/Toolbar/FLEXExplorerToolbar.h

@@ -10,6 +10,8 @@
 
 @class FLEXToolbarItem;
 
+/// Users of the toolbar can configure the enabled state
+/// and event target/actions for each item.
 @interface FLEXExplorerToolbar : UIView
 
 /// The items to be displayed in the toolbar. Defaults to:
@@ -17,23 +19,22 @@
 @property (nonatomic, copy) NSArray<FLEXToolbarItem *> *toolbarItems;
 
 /// Toolbar item for selecting views.
-/// Users of the toolbar can configure the enabled/selected state and event targets/actions.
 @property (nonatomic, readonly) FLEXToolbarItem *selectItem;
 
 /// Toolbar item for presenting a list with the view hierarchy.
-/// Users of the toolbar can configure the enabled state and event targets/actions.
 @property (nonatomic, readonly) FLEXToolbarItem *hierarchyItem;
 
 /// Toolbar item for moving views.
-/// Users of the toolbar can configure the enabled/selected state and event targets/actions.
+/// Its \c sibling is the \c lastTabItem
 @property (nonatomic, readonly) FLEXToolbarItem *moveItem;
 
-/// Toolbar item for inspecting details of the selected view.
-/// Users of the toolbar can configure the enabled state and event targets/actions.
+/// Toolbar item for presenting the currently active tab.
+@property (nonatomic, readonly) FLEXToolbarItem *recentItem;
+
+/// Toolbar item for presenting a screen with various tools for inspecting the app.
 @property (nonatomic, readonly) FLEXToolbarItem *globalsItem;
 
 /// Toolbar item for hiding the explorer.
-/// Users of the toolbar can configure the event targets/actions.
 @property (nonatomic, readonly) FLEXToolbarItem *closeItem;
 
 /// A view for moving the entire toolbar.

+ 24 - 27
Classes/Toolbar/FLEXExplorerToolbar.m

@@ -14,11 +14,12 @@
 
 @interface FLEXExplorerToolbar ()
 
+@property (nonatomic, readwrite) FLEXToolbarItem *globalsItem;
+@property (nonatomic, readwrite) FLEXToolbarItem *hierarchyItem;
 @property (nonatomic, readwrite) FLEXToolbarItem *selectItem;
+@property (nonatomic, readwrite) FLEXToolbarItem *recentItem;
 @property (nonatomic, readwrite) FLEXToolbarItem *moveItem;
-@property (nonatomic, readwrite) FLEXToolbarItem *globalsItem;
 @property (nonatomic, readwrite) FLEXToolbarItem *closeItem;
-@property (nonatomic, readwrite) FLEXToolbarItem *hierarchyItem;
 @property (nonatomic, readwrite) UIView *dragHandle;
 
 @property (nonatomic) UIImageView *dragHandleImageView;
@@ -37,34 +38,29 @@
 - (id)initWithFrame:(CGRect)frame {
     self = [super initWithFrame:frame];
     if (self) {
+        // Background
         self.backgroundView = [UIView new];
         self.backgroundView.backgroundColor = [FLEXColor secondaryBackgroundColorWithAlpha:0.95];
         [self addSubview:self.backgroundView];
 
+        // Drag handle
         self.dragHandle = [UIView new];
         self.dragHandle.backgroundColor = UIColor.clearColor;
-        [self addSubview:self.dragHandle];
-        
-        UIImage *dragHandle = [FLEXResources dragHandle];
-        self.dragHandleImageView = [[UIImageView alloc] initWithImage:dragHandle];
-        self.dragHandleImageView.tintColor = [[FLEXColor iconColor] colorWithAlphaComponent:0.666];
+        self.dragHandleImageView = [[UIImageView alloc] initWithImage:FLEXResources.dragHandle];
+        self.dragHandleImageView.tintColor = [FLEXColor.iconColor colorWithAlphaComponent:0.666];
         [self.dragHandle addSubview:self.dragHandleImageView];
+        [self addSubview:self.dragHandle];
         
-        UIImage *globalsIcon = [FLEXResources globeIcon];
-        self.globalsItem = [FLEXToolbarItem toolbarItemWithTitle:@"menu" image:globalsIcon];
-        
-        UIImage *listIcon = [FLEXResources listIcon];
-        self.hierarchyItem = [FLEXToolbarItem toolbarItemWithTitle:@"views" image:listIcon];
-        
-        UIImage *selectIcon = [FLEXResources selectIcon];
-        self.selectItem = [FLEXToolbarItem toolbarItemWithTitle:@"select" image:selectIcon];
-        
-        UIImage *moveIcon = [FLEXResources moveIcon];
-        self.moveItem = [FLEXToolbarItem toolbarItemWithTitle:@"move" image:moveIcon];
+        // Buttons
+        self.globalsItem   = [FLEXToolbarItem itemWithTitle:@"menu" image:FLEXResources.globalsIcon];
+        self.hierarchyItem = [FLEXToolbarItem itemWithTitle:@"views" image:FLEXResources.hierarchyIcon];
+        self.selectItem    = [FLEXToolbarItem itemWithTitle:@"select" image:FLEXResources.selectIcon];
+        self.recentItem    = [FLEXToolbarItem itemWithTitle:@"recent" image:FLEXResources.recentIcon];
+        self.moveItem      = [FLEXToolbarItem itemWithTitle:@"move" image:FLEXResources.moveIcon sibling:self.recentItem];
+        self.closeItem     = [FLEXToolbarItem itemWithTitle:@"close" image:FLEXResources.closeIcon];
+
+        // Selected view box //
         
-        UIImage *closeIcon = [FLEXResources closeIcon];
-        self.closeItem = [FLEXToolbarItem toolbarItemWithTitle:@"close" image:closeIcon];
-
         self.selectedViewDescriptionContainer = [UIView new];
         self.selectedViewDescriptionContainer.backgroundColor = [FLEXColor tertiaryBackgroundColorWithAlpha:0.95];
         self.selectedViewDescriptionContainer.hidden = YES;
@@ -83,6 +79,7 @@
         self.selectedViewDescriptionLabel.font = [[self class] descriptionLabelFont];
         [self.selectedViewDescriptionSafeAreaContainer addSubview:self.selectedViewDescriptionLabel];
         
+        // toolbarItems
         self.toolbarItems = @[_globalsItem, _hierarchyItem, _selectItem, _moveItem, _closeItem];
     }
 
@@ -108,13 +105,13 @@
     CGFloat originY = CGRectGetMinY(safeArea);
     CGFloat height = kToolbarItemHeight;
     CGFloat width = FLEXFloor((CGRectGetWidth(safeArea) - CGRectGetWidth(self.dragHandle.frame)) / self.toolbarItems.count);
-    for (UIView *toolbarItem in self.toolbarItems) {
-        toolbarItem.frame = CGRectMake(originX, originY, width, height);
-        originX = CGRectGetMaxX(toolbarItem.frame);
+    for (FLEXToolbarItem *toolbarItem in self.toolbarItems) {
+        toolbarItem.currentItem.frame = CGRectMake(originX, originY, width, height);
+        originX = CGRectGetMaxX(toolbarItem.currentItem.frame);
     }
     
     // Make sure the last toolbar item goes to the edge to account for any accumulated rounding effects.
-    UIView *lastToolbarItem = self.toolbarItems.lastObject;
+    UIView *lastToolbarItem = self.toolbarItems.lastObject.currentItem;
     CGRect lastToolbarItemFrame = lastToolbarItem.frame;
     lastToolbarItemFrame.size.width = CGRectGetMaxX(safeArea) - lastToolbarItemFrame.origin.x;
     lastToolbarItem.frame = lastToolbarItemFrame;
@@ -170,7 +167,7 @@
     
     // Remove old toolbar items, if any
     for (FLEXToolbarItem *item in _toolbarItems) {
-        [item removeFromSuperview];
+        [item.currentItem removeFromSuperview];
     }
     
     // Trim to 5 items if necessary
@@ -179,7 +176,7 @@
     }
 
     for (FLEXToolbarItem *item in toolbarItems) {
-        [self addSubview:item];
+        [self addSubview:item.currentItem];
     }
 
     _toolbarItems = toolbarItems.copy;

+ 30 - 1
Classes/Toolbar/FLEXToolbarItem.h

@@ -8,8 +8,37 @@
 
 #import <UIKit/UIKit.h>
 
+NS_ASSUME_NONNULL_BEGIN
+
 @interface FLEXToolbarItem : UIButton
 
-+ (instancetype)toolbarItemWithTitle:(NSString *)title image:(UIImage *)image;
++ (instancetype)itemWithTitle:(NSString *)title image:(UIImage *)image;
+
+/// @param backupItem a toolbar item to use in place of this item when it becomes disabled.
+/// Items without a sibling item exhibit expected behavior when they become disabled, and are greyed out.
++ (instancetype)itemWithTitle:(NSString *)title image:(UIImage *)image sibling:(nullable FLEXToolbarItem *)backupItem;
+
+/// If a toolbar item has a sibling, the item will replace itself with its
+/// sibling when it becomes disabled, and vice versa when it becomes enabled again.
+@property (nonatomic, readonly) FLEXToolbarItem *sibling;
+
+/// When a toolbar item has a sibling and it becomes disabled, the sibling is the view
+/// that should be added to or removed from a new or existing toolbar. This property
+/// alleviates the programmer from determining whether to use \c item or \c item.sibling
+/// or \c item.sibling.sibling and so on. Yes, sibling items can also have siblings so
+/// that each item which becomes disabled may present another item in its place, creating
+/// a "stack" of toolbar items. This behavior is useful for making buttons which occupy
+/// the same space under different states.
+///
+/// With this in mind, you should never access a stored toolbar item's view properties
+/// such as \c frame or \c superview directly; you should access them on \c currentItem.
+/// If you are trying to modify the frame of an item, and the item itself is not currently
+/// displayed but instead its sibling is being displayed, then your changes could be ignored.
+///
+/// @return the result of the item's sibling's \c currentItem,
+/// if this item has a sibling and this item is disabled, otherwise this item.
+@property (nonatomic, readonly) FLEXToolbarItem *currentItem;
 
 @end
+
+NS_ASSUME_NONNULL_END

+ 53 - 11
Classes/Toolbar/FLEXToolbarItem.m

@@ -12,32 +12,54 @@
 
 @interface FLEXToolbarItem ()
 
+@property (nonatomic) FLEXToolbarItem *sibling;
 @property (nonatomic, copy) NSString *title;
 @property (nonatomic) UIImage *image;
 
+@property (nonatomic, readonly, class) UIColor *defaultBackgroundColor;
+@property (nonatomic, readonly, class) UIColor *highlightedBackgroundColor;
+@property (nonatomic, readonly, class) UIColor *selectedBackgroundColor;
+
 @end
 
 @implementation FLEXToolbarItem
 
-+ (instancetype)toolbarItemWithTitle:(NSString *)title image:(UIImage *)image {
+#pragma mark - Public
+
++ (instancetype)itemWithTitle:(NSString *)title image:(UIImage *)image {
+    return [self itemWithTitle:title image:image sibling:nil];
+}
+
++ (instancetype)itemWithTitle:(NSString *)title image:(UIImage *)image sibling:(FLEXToolbarItem *)backupItem {
+    NSParameterAssert(title); NSParameterAssert(image);
+    
     FLEXToolbarItem *toolbarItem = [self buttonWithType:UIButtonTypeSystem];
+    toolbarItem.sibling = backupItem;
     toolbarItem.title = title;
-    toolbarItem.backgroundColor = [self defaultBackgroundColor];
     toolbarItem.image = image;
+    toolbarItem.tintColor = FLEXColor.iconColor;
+    toolbarItem.backgroundColor = self.defaultBackgroundColor;
     toolbarItem.titleLabel.font = [UIFont systemFontOfSize:12.0];
     [toolbarItem setTitle:title forState:UIControlStateNormal];
     [toolbarItem setImage:image forState:UIControlStateNormal];
-    [toolbarItem setTitleColor:[FLEXColor primaryTextColor] forState:UIControlStateNormal];
-    [toolbarItem setTitleColor:[FLEXColor deemphasizedTextColor] forState:UIControlStateDisabled];
-    [toolbarItem setTintColor:[FLEXColor iconColor]];
+    [toolbarItem setTitleColor:FLEXColor.primaryTextColor forState:UIControlStateNormal];
+    [toolbarItem setTitleColor:FLEXColor.deemphasizedTextColor forState:UIControlStateDisabled];
     return toolbarItem;
 }
 
+- (FLEXToolbarItem *)currentItem {
+    if (!self.enabled && self.sibling) {
+        return self.sibling.currentItem;
+    }
+    
+    return self;
+}
+
 
 #pragma mark - Display Defaults
 
 + (NSDictionary<NSString *, id> *)titleAttributes {
-    return @{NSFontAttributeName : [UIFont systemFontOfSize:12.0]};
+    return @{ NSFontAttributeName : [UIFont systemFontOfSize:12.0] };
 }
 
 + (UIColor *)highlightedBackgroundColor {
@@ -60,25 +82,45 @@
 #pragma mark - State Changes
 
 - (void)setHighlighted:(BOOL)highlighted {
-    [super setHighlighted:highlighted];
+    super.highlighted = highlighted;
     [self updateColors];
 }
 
 - (void)setSelected:(BOOL)selected {
-    [super setSelected:selected];
+    super.selected = selected;
     [self updateColors];
 }
 
+- (void)setEnabled:(BOOL)enabled {
+    if (self.enabled != enabled) {
+        if (self.sibling) {
+            if (enabled) { // Replace sibling with myself
+                UIView *superview = self.sibling.superview;
+                [self.sibling removeFromSuperview];
+                self.frame = self.sibling.frame;
+                [superview addSubview:self];
+            } else { // Replace myself with sibling
+                UIView *superview = self.superview;
+                [self removeFromSuperview];
+                self.sibling.frame = self.frame;
+                [superview addSubview:self.sibling];
+            }
+        }
+        
+        super.enabled = enabled;
+    }
+}
+
 + (id)_selectedIndicatorImage { return nil; }
 
 - (void)updateColors {
     // Background color
     if (self.highlighted) {
-        self.backgroundColor = [[self class] highlightedBackgroundColor];
+        self.backgroundColor = self.class.highlightedBackgroundColor;
     } else if (self.selected) {
-        self.backgroundColor = [[self class] selectedBackgroundColor];
+        self.backgroundColor = self.class.selectedBackgroundColor;
     } else {
-        self.backgroundColor = [[self class] defaultBackgroundColor];
+        self.backgroundColor = self.class.defaultBackgroundColor;
     }
 }
 

+ 3 - 3
Classes/Utility/FLEXResources.h

@@ -6,16 +6,16 @@
 //  Copyright (c) 2014 Flipboard. All rights reserved.
 //
 
-#import <Foundation/Foundation.h>
 #import <UIKit/UIKit.h>
 
 @interface FLEXResources : NSObject
 
 @property (readonly, class) UIImage *closeIcon;
 @property (readonly, class) UIImage *dragHandle;
-@property (readonly, class) UIImage *globeIcon;
+@property (readonly, class) UIImage *globalsIcon;
 @property (readonly, class) UIImage *hierarchyIndentPattern;
-@property (readonly, class) UIImage *listIcon;
+@property (readonly, class) UIImage *hierarchyIcon;
+@property (readonly, class) UIImage *recentIcon;
 @property (readonly, class) UIImage *moveIcon;
 @property (readonly, class) UIImage *selectIcon;
 @property (readonly, class) UIImage *checkerPattern;

+ 16 - 2
Graphics/images_to_hex.sh

@@ -7,9 +7,23 @@
 # the original script, I left it to be generic as it was originally intended
 # in case someone else finds it useful. The original python script outputs
 # header and implementation files, which this script removes after it is run.
+#
+# Run this command to remove the hex files: find . -type f -name "*.txt" | xargs rm
+#
+# Usage: ./images_to_hex.sh [one or more folders in a quoted string]
+# Examples:
+#     bash images_to_hex.sh
+#     bash images_to_hex.sh toolbar
+#     bash images_to_hex.sh "toolbar filetypes"
+#
+
+if [[ $1 ]]; then
+    imageFolders="$1"
+else
+    imageFolders="filetypes range-slider toolbar misc"
+fi
 
-allImageFolders="filetypes range-slider toolbar misc"
-for dir in $allImageFolders; do
+for dir in $imageFolders; do
     rm $dir/*.txt
     for image in `ls $dir`; do
         name=`basename $image .png`