Skip to content
This repository has been archived by the owner on May 10, 2024. It is now read-only.

Fix #3044: Search Suggestion Text Color blinks when text is entered #3091

Merged
merged 4 commits into from
Dec 2, 2020
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Client.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,7 @@
2FCAE2781ABB531100877008 /* Visit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FCAE25C1ABB531100877008 /* Visit.swift */; };
2FCAE2841ABB533A00877008 /* MockFiles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FCAE2791ABB533A00877008 /* MockFiles.swift */; };
2FCAE33E1ABB5F1800877008 /* Storage-Bridging-Header.h in Headers */ = {isa = PBXBuildFile; fileRef = 2FCAE33D1ABB5F1800877008 /* Storage-Bridging-Header.h */; settings = {ATTRIBUTES = (Public, ); }; };
2FD0E3D62577F327000C773B /* SearchSuggestionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FD0E3D52577F327000C773B /* SearchSuggestionCell.swift */; };
2FDB10931A9FBEC5006CF312 /* PrefsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FDB10921A9FBEC5006CF312 /* PrefsTests.swift */; };
31ADB5DA1E58CEC300E87909 /* ClipboardBarDisplayHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31ADB5D91E58CEC300E87909 /* ClipboardBarDisplayHandler.swift */; };
39236E721FCC600200A38F1B /* TabEventHandlerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39236E711FCC600200A38F1B /* TabEventHandlerTests.swift */; };
Expand Down Expand Up @@ -1653,6 +1654,7 @@
2FCAE25C1ABB531100877008 /* Visit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Visit.swift; sourceTree = "<group>"; };
2FCAE2791ABB533A00877008 /* MockFiles.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockFiles.swift; sourceTree = "<group>"; };
2FCAE33D1ABB5F1800877008 /* Storage-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "Storage-Bridging-Header.h"; sourceTree = "<group>"; };
2FD0E3D52577F327000C773B /* SearchSuggestionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchSuggestionCell.swift; sourceTree = "<group>"; };
2FDB10921A9FBEC5006CF312 /* PrefsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PrefsTests.swift; sourceTree = "<group>"; };
2FEBABAE1AB3659000DB5728 /* ResultTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ResultTests.swift; sourceTree = "<group>"; };
31ADB5D91E58CEC300E87909 /* ClipboardBarDisplayHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClipboardBarDisplayHandler.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2807,6 +2809,7 @@
59A68CCB63E2A565CB03F832 /* SearchViewController.swift */,
27F94A3921909A5900F4FADF /* SearchSuggestionsPromptView.swift */,
0A179E272519F97E0014D9E1 /* InitialSearchEngines.swift */,
2FD0E3D52577F327000C773B /* SearchSuggestionCell.swift */,
);
path = Search;
sourceTree = "<group>";
Expand Down Expand Up @@ -6338,6 +6341,7 @@
F9C6B7D623886EF8003F7D16 /* SceneObserver.swift in Sources */,
4422D50421BFFB7600BF1855 /* memtable.cc in Sources */,
D3A9949D1A3686BD008AD1AC /* Tab.swift in Sources */,
2FD0E3D62577F327000C773B /* SearchSuggestionCell.swift in Sources */,
0A4BEFD8221EE78E0005551A /* CachedNetworkResource.swift in Sources */,
0A1E84412190A57F0042F782 /* SyncViewController.swift in Sources */,
27A1ABF62485568700344503 /* FlexibleSpaceSectionProvider.swift in Sources */,
Expand Down
221 changes: 221 additions & 0 deletions Client/Frontend/Browser/Search/SearchSuggestionCell.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
// Copyright 2020 The Brave Authors. All rights reserved.
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

import UIKit
import Shared

// MARK: - SuggestionCellUX

private struct SuggestionCellUX {
static let suggestionMargin: CGFloat = 8
static let suggestionInsets = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8)
static let suggestionBorderWidth: CGFloat = 1
static let suggestionCornerRadius: CGFloat = 4
static let suggestionCellVerticalPadding: CGFloat = 10
static let suggestionCellMaxRows = 2

static let faviconSize: CGFloat = 29
static let leftItemEdgeInset = UIEdgeInsets(top: 0, left: 8, bottom: 0, right: 0)
static let rightItemEdgeInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 8)

// The left bounds of the suggestions, aligned with where text would be displayed.
static let leftTextMargin: CGFloat = 61
}

// MARK: - SuggestionCelDelegate

protocol SuggestionCellDelegate: class {
func suggestionCell(_ suggestionCell: SuggestionCell, didSelectSuggestion suggestion: String)
func suggestionCell(_ suggestionCell: SuggestionCell, didLongPressSuggestion suggestion: String)
}

// MARK: - SuggestionCell

/**
* Cell that wraps a list of search suggestion buttons.
*/
class SuggestionCell: UITableViewCell {

// MARK: Properties

weak var delegate: SuggestionCellDelegate?

var suggestions: [String] = [] {
willSet {
for view in contentView.subviews {
view.removeFromSuperview()
}
}

didSet {
suggestions.forEach { suggestion in
let button = SuggestionButton()
button.setTitle(suggestion, for: [])

button.addTarget(self, action: #selector(didSelectSuggestion), for: .touchUpInside)
button.addGestureRecognizer(UILongPressGestureRecognizer(target: self, action: #selector(didLongPressSuggestion)))

// If this is the first image, add the search icon.
if contentView.subviews.isEmpty {
button.setImage(#imageLiteral(resourceName: "search"), for: [])

if UIApplication.shared.userInterfaceLayoutDirection == .leftToRight {
button.titleEdgeInsets = UIEdgeInsets(top: 0, left: 8, bottom: 0, right: 0)
} else {
button.titleEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 8)
}
}

contentView.addSubview(button)
}

setNeedsLayout()
}
}

// MARK: Lifecycle

override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)

isAccessibilityElement = false
accessibilityLabel = nil
layoutMargins = .zero
separatorInset = .zero
selectionStyle = .none

contentView.backgroundColor = .clear
backgroundColor = .clear
}

required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

// MARK: Internal

internal override func layoutSubviews() {
super.layoutSubviews()

// The maximum width of the container, after which suggestions will wrap to the next line.
let maxWidth = contentView.frame.width

let imageSize = CGFloat(SuggestionCellUX.faviconSize)

// The height of the suggestions container (minus margins), used to determine the frame.
// We set it to imageSize.height as a minimum since we don't want the cell to be shorter than the icon
var height: CGFloat = imageSize

var currentLeft = SuggestionCellUX.leftTextMargin
var currentTop = SuggestionCellUX.suggestionCellVerticalPadding
var currentRow = 0

let suggestionButtonList = contentView.subviews.compactMap({ $0 as? SuggestionButton })

for view in suggestionButtonList {
let button = view
var buttonSize = button.intrinsicContentSize

// Update our base frame height by the max size of either the image or the button so we never
// make the cell smaller than any of the two
if height == imageSize {
height = max(buttonSize.height, imageSize)
}

var width = currentLeft + buttonSize.width + SuggestionCellUX.suggestionMargin
if width > maxWidth {
// Only move to the next row if there's already a suggestion on this row.
// Otherwise, the suggestion is too big to fit and will be resized below.
if currentLeft > SuggestionCellUX.leftTextMargin {
currentRow += 1
if currentRow >= SuggestionCellUX.suggestionCellMaxRows {
// Don't draw this button if it doesn't fit on the row.
button.frame = .zero
continue
}

currentLeft = SuggestionCellUX.leftTextMargin
currentTop += buttonSize.height + SuggestionCellUX.suggestionMargin
height += buttonSize.height + SuggestionCellUX.suggestionMargin
width = currentLeft + buttonSize.width + SuggestionCellUX.suggestionMargin
}

// If the suggestion is too wide to fit on its own row, shrink it.
if width > maxWidth {
buttonSize.width = maxWidth - currentLeft - SuggestionCellUX.suggestionMargin
}
}

button.frame = CGRect(x: currentLeft, y: currentTop, width: buttonSize.width, height: buttonSize.height)
button.titleLabel?.alpha = 1.0

currentLeft += buttonSize.width + SuggestionCellUX.suggestionMargin
}

frame.size.height = height + 2 * SuggestionCellUX.suggestionCellVerticalPadding
contentView.frame = bounds

let imageX = (SuggestionCellUX.leftTextMargin - imageSize) / 2
let imageY = (frame.size.height - imageSize) / 2

if let cellImageView = imageView {
cellImageView.frame = CGRect(x: imageX, y: imageY, width: imageSize, height: imageSize)
}
}

// MARK: Actions

@objc
func didSelectSuggestion(_ sender: UIButton) {
if let titleText = sender.titleLabel?.text {
delegate?.suggestionCell(self, didSelectSuggestion: titleText)
}
}

@objc
func didLongPressSuggestion(_ recognizer: UILongPressGestureRecognizer) {
if recognizer.state == .began {
if let button = recognizer.view as? UIButton, let titleText = button.titleLabel?.text {
delegate?.suggestionCell(self, didLongPressSuggestion: titleText)
}
}
}
}

// MARK: - SuggestionButton

/**
* Rounded search suggestion button that highlights when selected.
*/
private class SuggestionButton: InsetButton {

// MARK: Properties

@objc
override var isHighlighted: Bool {
didSet {
alpha = isHighlighted ? 0.6 : 1.0
}
}

// MARK: Lifecycle

override init(frame: CGRect) {
super.init(frame: frame)

setTitleColor(UIConstants.highlightBlue, for: [])
titleLabel?.font = DynamicFontHelper.defaultHelper.DefaultMediumFont
layer.borderWidth = SuggestionCellUX.suggestionBorderWidth
layer.cornerRadius = SuggestionCellUX.suggestionCornerRadius
layer.borderColor = UIConstants.highlightBlue.cgColor
contentEdgeInsets = SuggestionCellUX.suggestionInsets

accessibilityHint = Strings.searchesForSuggestionButtonAccessibilityText
}

required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Loading