OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "ios/chrome/browser/ui/contextual_search/panel_configuration.h" |
| 6 |
| 7 #include "base/logging.h" |
| 8 #include "base/mac/scoped_nsobject.h" |
| 9 |
| 10 namespace { |
| 11 // Amount of tab that a previewing pane leaves visible, expressed as a fraction. |
| 12 const CGFloat kPhonePreviewingDisplayRatio = 1.0 / 3.0; |
| 13 // Phone peeking height equals phone toolbar height. |
| 14 const CGFloat kPhonePeekingHeight = 64.0; |
| 15 // Phone covering offset equals the status bar height; |
| 16 const CGFloat kPhoneCoveringHeightOffset = 20.0; |
| 17 |
| 18 const CGFloat kPadPreviewingDisplayRatio = 1.0 / 3.0; |
| 19 // iPad peeking height equals iPad toolbar height. |
| 20 const CGFloat kPadPeekingHeight = 56.0; |
| 21 // Pad covering offset = tab strip height (39) + status bar height (20). |
| 22 const CGFloat kPadCoveringHeightOffset = 59.0; |
| 23 // Offset to move the pad covering height down by so it looks better. |
| 24 const CGFloat kPadCoveringHeightDecrease = 30.0; |
| 25 // Width ratio for regular panel sizes. |
| 26 const CGFloat kRegularPanelWidthRatio = 0.6; |
| 27 |
| 28 // Struct wrapper for passing around a C array. |
| 29 typedef struct { CGFloat byState[ContextualSearch::COVERING + 1]; } Positions; |
| 30 } |
| 31 |
| 32 @interface PanelConfiguration () |
| 33 @property(nonatomic, assign) Positions positions; |
| 34 - (void)updatePositions; |
| 35 @end |
| 36 |
| 37 @implementation PanelConfiguration |
| 38 |
| 39 @synthesize containerSize = _containerSize; |
| 40 @synthesize positions = _positions; |
| 41 @synthesize horizontalSizeClass = _horizontalSizeClass; |
| 42 |
| 43 + (instancetype)configurationForContainerSize:(CGSize)containerSize |
| 44 horizontalSizeClass: |
| 45 (UIUserInterfaceSizeClass)horizontalSizeClass { |
| 46 PanelConfiguration* config = [[[self alloc] init] autorelease]; |
| 47 config.containerSize = containerSize; |
| 48 config.horizontalSizeClass = horizontalSizeClass; |
| 49 return config; |
| 50 } |
| 51 |
| 52 - (void)setContainerSize:(CGSize)containerSize { |
| 53 DCHECK(containerSize.height > self.peekingHeight); |
| 54 _containerSize = containerSize; |
| 55 [self updatePositions]; |
| 56 } |
| 57 |
| 58 - (CGFloat)peekingHeight { |
| 59 NOTIMPLEMENTED() |
| 60 << "PanelConfiguation is an abstract superclass; use a concrete subclass"; |
| 61 return 0.0; |
| 62 } |
| 63 |
| 64 - (void)updatePositions { |
| 65 NOTIMPLEMENTED() |
| 66 << "PanelConfiguation is an abstract superclass; use a concrete subclass"; |
| 67 } |
| 68 |
| 69 - (CGFloat)positionForPanelState:(ContextualSearch::PanelState)state { |
| 70 return self.positions.byState[state]; |
| 71 } |
| 72 |
| 73 - (ContextualSearch::PanelState)panelStateForPosition:(CGFloat)position { |
| 74 for (NSInteger i = ContextualSearch::DISMISSED; |
| 75 i < ContextualSearch::COVERING; i++) { |
| 76 if (position > self.positions.byState[i + 1]) |
| 77 return static_cast<ContextualSearch::PanelState>(i); |
| 78 } |
| 79 return ContextualSearch::COVERING; |
| 80 } |
| 81 |
| 82 - (CGFloat)gradationToState:(ContextualSearch::PanelState)toState |
| 83 fromState:(ContextualSearch::PanelState)fromState |
| 84 atPosition:(CGFloat)position { |
| 85 if (toState == fromState) |
| 86 return 0; |
| 87 CGFloat distance = |
| 88 fabs(self.positions.byState[toState] - self.positions.byState[fromState]); |
| 89 CGFloat progress = fabs(self.positions.byState[fromState] - position); |
| 90 if (fromState > toState) |
| 91 progress = distance - progress; |
| 92 return progress / distance; |
| 93 } |
| 94 |
| 95 - (NSArray*)constraintsForSizingPanel:(UIView*)panel { |
| 96 // Panels in both width classes should be exactly visible on cover mode and |
| 97 // horizontally centered in the container view. |
| 98 DCHECK(panel.superview != nil); |
| 99 NSMutableArray* constraints = [NSMutableArray arrayWithArray:@[ |
| 100 [panel.heightAnchor |
| 101 constraintEqualToAnchor:panel.superview.heightAnchor |
| 102 constant:-self.positions |
| 103 .byState[ContextualSearch::COVERING]], |
| 104 // This technically positions the panel, it doesn't size it. |
| 105 [panel.centerXAnchor constraintEqualToAnchor:panel.superview.centerXAnchor], |
| 106 ]]; |
| 107 if (self.horizontalSizeClass == UIUserInterfaceSizeClassCompact) { |
| 108 // In a compact layout, the panel is the width of its superview. |
| 109 [constraints |
| 110 addObject:[panel.widthAnchor |
| 111 constraintEqualToAnchor:panel.superview.widthAnchor]]; |
| 112 } else if (self.horizontalSizeClass == UIUserInterfaceSizeClassRegular) { |
| 113 // In a regular layout, the panel width is |kRegularPanelWidthRatio| of its |
| 114 // superview. |
| 115 [constraints |
| 116 addObject:[panel.widthAnchor |
| 117 constraintEqualToAnchor:panel.superview.widthAnchor |
| 118 multiplier:kRegularPanelWidthRatio]]; |
| 119 } |
| 120 return constraints; |
| 121 } |
| 122 |
| 123 - (NSLayoutConstraint*) |
| 124 constraintForPositioningGuide:(UILayoutGuide*)guide |
| 125 atState:(ContextualSearch::PanelState)state { |
| 126 DCHECK(guide.owningView != nil); |
| 127 |
| 128 NSLayoutConstraint* positioningConstraint = nil; |
| 129 switch (state) { |
| 130 case ContextualSearch::DISMISSED: |
| 131 positioningConstraint = [guide.heightAnchor |
| 132 constraintEqualToAnchor:guide.owningView.heightAnchor]; |
| 133 break; |
| 134 case ContextualSearch::PEEKING: |
| 135 positioningConstraint = [guide.heightAnchor |
| 136 constraintEqualToAnchor:guide.owningView.heightAnchor |
| 137 constant:-self.peekingHeight]; |
| 138 break; |
| 139 case ContextualSearch::PREVIEWING: |
| 140 // Previewing height should be provided by a subclass. |
| 141 NOTREACHED(); |
| 142 break; |
| 143 case ContextualSearch::COVERING: |
| 144 positioningConstraint = [guide.heightAnchor |
| 145 constraintEqualToConstant:self.positions |
| 146 .byState[ContextualSearch::COVERING]]; |
| 147 break; |
| 148 default: |
| 149 positioningConstraint = nil; |
| 150 NOTREACHED(); |
| 151 break; |
| 152 } |
| 153 return positioningConstraint; |
| 154 } |
| 155 @end |
| 156 |
| 157 @implementation PhonePanelConfiguration |
| 158 |
| 159 - (CGFloat)peekingHeight { |
| 160 return kPhonePeekingHeight; |
| 161 } |
| 162 |
| 163 - (void)updatePositions { |
| 164 Positions newPositions; |
| 165 |
| 166 newPositions.byState[ContextualSearch::DISMISSED] = self.containerSize.height; |
| 167 newPositions.byState[ContextualSearch::PEEKING] = |
| 168 self.containerSize.height - self.peekingHeight; |
| 169 newPositions.byState[ContextualSearch::PREVIEWING] = |
| 170 self.containerSize.height * kPhonePreviewingDisplayRatio; |
| 171 DCHECK(newPositions.byState[ContextualSearch::PREVIEWING] < |
| 172 newPositions.byState[ContextualSearch::PEEKING]); |
| 173 newPositions.byState[ContextualSearch::COVERING] = kPhoneCoveringHeightOffset; |
| 174 |
| 175 self.positions = newPositions; |
| 176 } |
| 177 |
| 178 - (NSLayoutConstraint*) |
| 179 constraintForPositioningGuide:(UILayoutGuide*)guide |
| 180 atState:(ContextualSearch::PanelState)state { |
| 181 NSLayoutConstraint* positioningConstraint; |
| 182 switch (state) { |
| 183 case ContextualSearch::PREVIEWING: |
| 184 positioningConstraint = [guide.heightAnchor |
| 185 constraintEqualToAnchor:guide.owningView.heightAnchor |
| 186 multiplier:kPhonePreviewingDisplayRatio]; |
| 187 break; |
| 188 default: |
| 189 positioningConstraint = |
| 190 [super constraintForPositioningGuide:guide atState:state]; |
| 191 break; |
| 192 } |
| 193 return positioningConstraint; |
| 194 } |
| 195 |
| 196 @end |
| 197 |
| 198 @implementation PadPanelConfiguration |
| 199 |
| 200 - (CGFloat)peekingHeight { |
| 201 return kPadPeekingHeight; |
| 202 } |
| 203 |
| 204 - (void)updatePositions { |
| 205 Positions newPositions; |
| 206 |
| 207 newPositions.byState[ContextualSearch::DISMISSED] = self.containerSize.height; |
| 208 newPositions.byState[ContextualSearch::PEEKING] = |
| 209 self.containerSize.height - self.peekingHeight; |
| 210 newPositions.byState[ContextualSearch::PREVIEWING] = |
| 211 self.containerSize.height * kPadPreviewingDisplayRatio; |
| 212 DCHECK(newPositions.byState[ContextualSearch::PREVIEWING] < |
| 213 newPositions.byState[ContextualSearch::PEEKING]); |
| 214 newPositions.byState[ContextualSearch::COVERING] = |
| 215 kPadCoveringHeightOffset + kPadCoveringHeightDecrease; |
| 216 |
| 217 self.positions = newPositions; |
| 218 } |
| 219 |
| 220 - (NSLayoutConstraint*) |
| 221 constraintForPositioningGuide:(UILayoutGuide*)guide |
| 222 atState:(ContextualSearch::PanelState)state { |
| 223 NSLayoutConstraint* positioningConstraint; |
| 224 switch (state) { |
| 225 case ContextualSearch::PREVIEWING: |
| 226 positioningConstraint = [guide.heightAnchor |
| 227 constraintEqualToAnchor:guide.owningView.heightAnchor |
| 228 multiplier:kPadPreviewingDisplayRatio]; |
| 229 break; |
| 230 default: |
| 231 positioningConstraint = |
| 232 [super constraintForPositioningGuide:guide atState:state]; |
| 233 break; |
| 234 } |
| 235 return positioningConstraint; |
| 236 } |
| 237 |
| 238 @end |
OLD | NEW |