chromium/ios/chrome/browser/ui/omnibox/popup/row/actions/omnibox_popup_actions_row_content_configuration.mm

// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#import "ios/chrome/browser/ui/omnibox/popup/row/actions/omnibox_popup_actions_row_content_configuration.h"

#import "ios/chrome/browser/ui/omnibox/popup/autocomplete_suggestion.h"
#import "ios/chrome/browser/ui/omnibox/popup/row/actions/omnibox_popup_actions_row_content_view.h"
#import "ios/chrome/browser/ui/omnibox/popup/row/actions/omnibox_popup_actions_row_delegate.h"
#import "ios/chrome/browser/ui/omnibox/popup/row/actions/suggest_action.h"
#import "ios/chrome/common/ui/colors/semantic_color_names.h"
#import "net/base/apple/url_conversions.h"

// Redefined as readwrite.
@interface OmniboxPopupActionsRowContentConfiguration ()

@property(nonatomic, strong, readwrite) NSArray<SuggestAction*>* actions;
@property(nonatomic, readwrite) NSUInteger highlightedActionIndex;
@property(nonatomic, assign, readwrite) BOOL isBackgroundHighlighted;
@property(nonatomic, assign, readwrite) BOOL leadingIconHighlighted;
@property(nonatomic, strong, readwrite) NSAttributedString* primaryText;
@property(nonatomic, strong, readwrite) NSAttributedString* secondaryText;
@property(nonatomic, strong, readwrite) UIColor* trailingIconTintColor;

@end

@implementation OmniboxPopupActionsRowContentConfiguration

@synthesize isBackgroundHighlighted;
@synthesize leadingIconHighlighted;
@synthesize primaryText;
@synthesize secondaryText;
@synthesize trailingIconTintColor;

/// Layout this cell with the given data before displaying.
+ (instancetype)cellConfiguration {
  return [[OmniboxPopupActionsRowContentConfiguration alloc] init];
}

- (void)setSuggestion:(id<AutocompleteSuggestion>)suggestion {
  [super setSuggestion:suggestion];

  _actions = suggestion.actionsInSuggest;
  _highlightedActionIndex = NSNotFound;
}

- (void)highlightNextActionButton {
  if (_highlightedActionIndex == _actions.count - 1) {
    return;
  }
  _highlightedActionIndex = _highlightedActionIndex + 1;
}

- (void)highlightPreviousActionButton {
  if (_highlightedActionIndex == 0) {
    return;
  }
  _highlightedActionIndex = _highlightedActionIndex - 1;
}

#pragma mark OmniboxKeyboardDelegate

- (BOOL)canPerformKeyboardAction:(OmniboxKeyboardAction)keyboardAction {
  switch (keyboardAction) {
    case OmniboxKeyboardActionUpArrow: {
      if (self.isBackgroundHighlighted) {
        return NO;
      }
      return YES;
    }
    case OmniboxKeyboardActionDownArrow: {
      if (self.highlightedActionIndex != NSNotFound) {
        return NO;
      }
      return YES;
    }
    case OmniboxKeyboardActionLeftArrow:
    case OmniboxKeyboardActionRightArrow:
      return YES;
  }
}

- (void)performKeyboardAction:(OmniboxKeyboardAction)keyboardAction {
  switch (keyboardAction) {
    case OmniboxKeyboardActionUpArrow: {
      self.highlightedActionIndex = NSNotFound;
      break;
    }
    case OmniboxKeyboardActionDownArrow: {
      self.highlightedActionIndex = 0;
      break;
    }
    case OmniboxKeyboardActionLeftArrow:
    case OmniboxKeyboardActionRightArrow: {
      if (self.isBackgroundHighlighted) {
        self.highlightedActionIndex = 0;
      } else {
        BOOL isRTL =
            [UIView userInterfaceLayoutDirectionForSemanticContentAttribute:
                        self.semanticContentAttribute] ==
            UIUserInterfaceLayoutDirectionRightToLeft;

        OmniboxKeyboardAction nextActionButton =
            isRTL ? OmniboxKeyboardActionLeftArrow
                  : OmniboxKeyboardActionRightArrow;
        OmniboxKeyboardAction previousActionButton =
            isRTL ? OmniboxKeyboardActionRightArrow
                  : OmniboxKeyboardActionLeftArrow;

        if (keyboardAction == nextActionButton) {
          if (self.highlightedActionIndex == self.actions.count - 1) {
            break;
          }
          self.highlightedActionIndex = self.highlightedActionIndex + 1;
        } else if (keyboardAction == previousActionButton) {
          if (self.highlightedActionIndex == 0) {
            break;
          }
          self.highlightedActionIndex = self.highlightedActionIndex - 1;
        }
      }
      break;
    }
  }
}

#pragma mark - OmniboxReturnDelegate

- (void)omniboxReturnPressed:(id)sender {
  CHECK(self.highlightedActionIndex != NSNotFound);
  CHECK(self.highlightedActionIndex < self.actions.count);

  SuggestAction* action = self.actions[self.highlightedActionIndex];
  [self.delegate omniboxPopupRowActionSelectedWithConfiguration:self
                                                         action:action];
}

#pragma mark - UIContentConfiguration

- (id)copyWithZone:(NSZone*)zone {
  __typeof__(self) configuration = [super copyWithZone:zone];
  configuration.actions = self.actions;
  configuration.highlightedActionIndex = self.highlightedActionIndex;
  return configuration;
}

- (UIView<UIContentView>*)makeContentView {
  return [[OmniboxPopupActionsRowContentView alloc] initWithConfiguration:self];
}

/// Updates the configuration for different state of the view. This contains
/// everything that can change in the configuration's lifetime.
- (instancetype)updatedConfigurationForState:
    (UIViewConfigurationState*)viewState {
  OmniboxPopupActionsRowContentConfiguration* configuration =
      [super updatedConfigurationForState:viewState];

  const BOOL isHighlighted = (viewState.highlighted || viewState.selected) &&
                             (self.highlightedActionIndex == NSNotFound);

  configuration.isBackgroundHighlighted = isHighlighted;
  configuration.leadingIconHighlighted = isHighlighted;
  configuration.primaryText =
      isHighlighted
          ? [self.class highlightedAttributedStringWithString:self.suggestion
                                                                  .text]
          : self.suggestion.text;
  configuration.secondaryText =
      isHighlighted
          ? [self.class highlightedAttributedStringWithString:self.suggestion
                                                                  .detailText]
          : self.suggestion.detailText;
  configuration.trailingIconTintColor =
      isHighlighted ? UIColor.whiteColor : [UIColor colorNamed:kBlueColor];
  configuration.actions = self.actions;

  if (!viewState.highlighted && !viewState.selected) {
    configuration.highlightedActionIndex = NSNotFound;
  } else {
    configuration.highlightedActionIndex = self.highlightedActionIndex;
  }

  return configuration;
}

@end