<commit_before>//
//  TableViewIndex.swift
//  TableViewIndex
//
//  Created by <NAME> on 28/04/16.
//  Copyright © 2016 <NAME>. All rights reserved.
//

import UIKit

@objc(MYTableViewIndex)
@objcMembers
open class TableViewIndex : UIControl {
    
    // MARK: - Properties
    
    /// Data source for the table index object. See TableViewIndexDataSource protocol for details.
    @IBOutlet public weak var dataSource: TableViewIndexDataSource? {
        didSet { reloadData() }
    }
    
    /// Delegate for the table index object. See TableViewIndexDelegate protocol for details.
    @IBOutlet public weak var delegate: TableViewIndexDelegate?
    
    /// Background view is displayed below the index items and can be customized with any UIView.
    /// If not set or set to nil, creates a default view which mimics the system index appearance.
    public var backgroundView: UIView! {
        didSet {
            if let view = backgroundView {
                insertSubview(view, at: 0)
            } else {
                backgroundView = BackgroundView()
            }
        }
    }
    
    /// Font for the index view items. If not set, uses a default font which is chosen to
    /// match system appearance.
    /// Use resetFont to fall back to default font.
    public var font: UIFont {
        get { return style.font }
        set { style = style.copy(applying: newValue) }
    }
    
    /// Vertical spacing between the items. Equals to 1 point by default to match system appearance.
    /// Use resetItemSpacing to fall back to default spacing.
    public var itemSpacing: CGFloat {
        get { return style.itemSpacing }
        set { style = style.copy(applying: newValue) }
    }
    
    /// The distance that index items are inset from the enclosing background view. The property
    /// doesn't change the position of index items. Instead, it changes the size of the background view
    /// to match the inset. In other words, the background view "wraps" the content. Affects intrinsic
    /// content size.
    /// Set inset value to CGFloat.max to make the background view fill all the available space.
    /// Default value matches the system index appearance.
    /// Use resetIndexInset to fall back to default inset.
    /// Left and right values are flipped when using right-to-left user interface direction.
    public var indexInset: UIEdgeInsets {
        get { return style.indexInset }
        set { style = style.copy(applying: newValue) }
    }

    /// The distance from the left (or right in case of right-to-left languages) border of the background view
    /// for which index items are shifted inside it.
    /// The property only affects the position of the index items and doesn't change the size of the background view.
    /// Default value matches the system index appearance.
    /// Use resetIndexOffset to fall back to default offset.
    public var indexOffset: UIOffset {
        get { return style.indexOffset }
        set { style = style.copy(applying: newValue) }
    }
    
    /// The list of all items provided by the data source.
    public private(set) var items: [UIView] = []
    
    /// Returns a set of items suitable for displaying within the current bounds. If there is not enough space
    /// to display all the items provided by the data source, some of them are replaced with a special truncation item.
    /// To customize the class of truncation item, use the corresponding TableViewIndexDataSource method.
    public var displayedItems: [UIView] {
        return indexView.items ?? []
    }
    
    private var truncation: Truncation<UIView>?
    
    private var style: Style! {
        didSet {
            applyItemAttributes()
            setNeedsLayout()
        }
    }
    
    private lazy var indexView: IndexView = { [unowned self] in
        let view = IndexView()
        self.addSubview(view)
        return view
    }()
    
    // MARK: - Initialization
    
    override public init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    
    required public init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        commonInit()
    }
    
    private func commonInit() {
        backgroundColor = UIColor.clear
        
        style = Style(userInterfaceDirection: UIView.my_userInterfaceLayoutDirection(for: self))
        backgroundView = BackgroundView()
        
        isExclusiveTouch = true
        isMultipleTouchEnabled = false
        isAccessibilityElement = true
        accessibilityTraits = UIAccessibilityTraitAdjustable
        accessibilityLabel = NSLocalizedString("Table index", comment: "Accessibility title for the section index control")
        
        updateAccessibilityValue()
    }
    
    // MARK: - Updates
    
    /// Forces table index to reload its items. This causes table index to discard its current items
    /// and refill itself from the data source.
    public func reloadData() {
        items = queryItems()
        truncation = queryTruncation()
        applyItemAttributes()
        setNeedsLayout()
    }
    
    private func queryItems() -> [UIView] {
        return dataSource != nil ? dataSource!.indexItems(for: self) : []
    }
    
    private func queryTruncation() -> Truncation<UIView>? {
        guard let dataSource = dataSource else {
            return nil
        }
        var truncationItemClass = TruncationItem.self
        
        // Check if the data source provides a custom class for truncation item
        if dataSource.responds(to: #selector(TableViewIndexDataSource.truncationItemClass(for:))) {
            truncationItemClass = dataSource.truncationItemClass!(for: self) as! TruncationItem.Type
        }
        // Now we now the item class and can create truncation items on demand
        return Truncation(items: items, truncationItemFactory: {
            return truncationItemClass.init()
        })
    }
    
    private func applyItemAttributes() {
        for item in items {
            item.applyAttributes(style)
        }
    }
    
    private func selectIndex(_ index: Int) {
        guard index != currentIndex else { return }
        
        currentIndex = index
        
        updateAccessibilityValue()
        
        if let delegate = self.delegate
            , delegate.responds(to: #selector(TableViewIndexDelegate.tableViewIndex(_:didSelect:at:))) {
            
            let shouldProduceFeedback = delegate.tableViewIndex!(self, didSelect: items[index], at: index)
            if shouldProduceFeedback {
                notifyFeedbackGenerator()
            }
        }
    }
    
    private func updateAccessibilityValue() {
        guard currentIndex >= 0 && currentIndex < items.count else { return }
        
        let currentItem = items[currentIndex]
        
        let titleText: String
        if let labelText = currentItem.accessibilityLabel {
            titleText = labelText
        } else if let labelText = (currentItem as? UILabel)?.text {
            titleText = labelText
        } else {
            titleText = String.localizedStringWithFormat(NSLocalizedString("Section %d", comment: "Accessibility title for a numbered section"), currentIndex + 1)
        }
        
        let selectedText = NSLocalizedString("Selected", comment: "Accessibility title for the selected state")
        accessibilityValue = "\(titleText), \(selectedText)"
    }
        
    // MARK: - Layout
    
    /// Returns a drawing area for the index items.
    public func indexRect() -> CGRect {
        return indexView.frame
    }
    
    /// Returns a drawing area for the background view.
    public func backgroundRect() -> CGRect {
        return backgroundView.frame
    }
    
    open override func layoutSubviews() {
        super.layoutSubviews()
        
        var visibleItems = items
        
        if let truncation = truncation {
            visibleItems = truncation.truncate(forHeight: bounds.height, style: style)
        }
        let layout = Layout(items: visibleItems, style: style, bounds: bounds)
        
        indexView.frame = layout.contentFrame<commit_msg>Post accessibility notification instead of setting accessibility value when index changes<commit_after>//
//  TableViewIndex.swift
//  TableViewIndex
//
//  Created by <NAME> on 28/04/16.
//  Copyright © 2016 <NAME>. All rights reserved.
//

import UIKit

@objc(MYTableViewIndex)
@objcMembers
open class TableViewIndex : UIControl {
    
    // MARK: - Properties
    
    /// Data source for the table index object. See TableViewIndexDataSource protocol for details.
    @IBOutlet public weak var dataSource: TableViewIndexDataSource? {
        didSet { reloadData() }
    }
    
    /// Delegate for the table index object. See TableViewIndexDelegate protocol for details.
    @IBOutlet public weak var delegate: TableViewIndexDelegate?
    
    /// Background view is displayed below the index items and can be customized with any UIView.
    /// If not set or set to nil, creates a default view which mimics the system index appearance.
    public var backgroundView: UIView! {
        didSet {
            if let view = backgroundView {
                insertSubview(view, at: 0)
            } else {
                backgroundView = BackgroundView()
            }
        }
    }
    
    /// Font for the index view items. If not set, uses a default font which is chosen to
    /// match system appearance.
    /// Use resetFont to fall back to default font.
    public var font: UIFont {
        get { return style.font }
        set { style = style.copy(applying: newValue) }
    }
    
    /// Vertical spacing between the items. Equals to 1 point by default to match system appearance.
    /// Use resetItemSpacing to fall back to default spacing.
    public var itemSpacing: CGFloat {
        get { return style.itemSpacing }
        set { style = style.copy(applying: newValue) }
    }
    
    /// The distance that index items are inset from the enclosing background view. The property
    /// doesn't change the position of index items. Instead, it changes the size of the background view
    /// to match the inset. In other words, the background view "wraps" the content. Affects intrinsic
    /// content size.
    /// Set inset value to CGFloat.max to make the background view fill all the available space.
    /// Default value matches the system index appearance.
    /// Use resetIndexInset to fall back to default inset.
    /// Left and right values are flipped when using right-to-left user interface direction.
    public var indexInset: UIEdgeInsets {
        get { return style.indexInset }
        set { style = style.copy(applying: newValue) }
    }

    /// The distance from the left (or right in case of right-to-left languages) border of the background view
    /// for which index items are shifted inside it.
    /// The property only affects the position of the index items and doesn't change the size of the background view.
    /// Default value matches the system index appearance.
    /// Use resetIndexOffset to fall back to default offset.
    public var indexOffset: UIOffset {
        get { return style.indexOffset }
        set { style = style.copy(applying: newValue) }
    }
    
    /// The list of all items provided by the data source.
    public private(set) var items: [UIView] = []
    
    /// Returns a set of items suitable for displaying within the current bounds. If there is not enough space
    /// to display all the items provided by the data source, some of them are replaced with a special truncation item.
    /// To customize the class of truncation item, use the corresponding TableViewIndexDataSource method.
    public var displayedItems: [UIView] {
        return indexView.items ?? []
    }
    
    private var truncation: Truncation<UIView>?
    
    private var style: Style! {
        didSet {
            applyItemAttributes()
            setNeedsLayout()
        }
    }
    
    private lazy var indexView: IndexView = { [unowned self] in
        let view = IndexView()
        self.addSubview(view)
        return view
    }()
    
    // MARK: - Initialization
    
    override public init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    
    required public init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        commonInit()
    }
    
    private func commonInit() {
        backgroundColor = UIColor.clear
        
        style = Style(userInterfaceDirection: UIView.my_userInterfaceLayoutDirection(for: self))
        backgroundView = BackgroundView()
        
        isExclusiveTouch = true
        isMultipleTouchEnabled = false
        isAccessibilityElement = true
        accessibilityTraits = UIAccessibilityTraitAdjustable
        accessibilityLabel = NSLocalizedString("Table index", comment: "Accessibility title for the section index control")
    }
    
    // MARK: - Updates
    
    /// Forces table index to reload its items. This causes table index to discard its current items
    /// and refill itself from the data source.
    public func reloadData() {
        items = queryItems()
        truncation = queryTruncation()
        applyItemAttributes()
        setNeedsLayout()
    }
    
    private func queryItems() -> [UIView] {
        return dataSource != nil ? dataSource!.indexItems(for: self) : []
    }
    
    private func queryTruncation() -> Truncation<UIView>? {
        guard let dataSource = dataSource else {
            return nil
        }
        var truncationItemClass = TruncationItem.self
        
        // Check if the data source provides a custom class for truncation item
        if dataSource.responds(to: #selector(TableViewIndexDataSource.truncationItemClass(for:))) {
            truncationItemClass = dataSource.truncationItemClass!(for: self) as! TruncationItem.Type
        }
        // Now we now the item class and can create truncation items on demand
        return Truncation(items: items, truncationItemFactory: {
            return truncationItemClass.init()
        })
    }
    
    private func applyItemAttributes() {
        for item in items {
            item.applyAttributes(style)
        }
    }
    
    private func selectIndex(_ index: Int) {
        guard index != currentIndex else { return }
        
        currentIndex = index
        
        if let delegate = self.delegate
            , delegate.responds(to: #selector(TableViewIndexDelegate.tableViewIndex(_:didSelect:at:))) {
            
            let shouldProduceFeedback = delegate.tableViewIndex!(self, didSelect: items[index], at: index)
            if shouldProduceFeedback {
                notifyFeedbackGenerator()
            }
        }
        
        let currentItem = items[index]
        
        let titleText: String
        if let labelText = currentItem.accessibilityLabel {
            titleText = labelText
        } else if let labelText = (currentItem as? UILabel)?.text {
            titleText = labelText
        } else {
            titleText = String.localizedStringWithFormat(NSLocalizedString("Section %d", comment: "Accessibility title for a numbered section"), currentIndex + 1)
        }
        
        let selectedText = NSLocalizedString("Selected", comment: "Accessibility title for the selected state")
        UIAccessibilityPostNotification(UIAccessibilityAnnouncementNotification, "\(titleText), \(selectedText)")
    }
        
    // MARK: - Layout
    
    /// Returns a drawing area for the index items.
    public func indexRect() -> CGRect {
        return indexView.frame
    }
    
    /// Returns a drawing area for the background view.
    public func backgroundRect() -> CGRect {
        return backgroundView.frame
    }
    
    open override func layoutSubviews() {
        super.layoutSubviews()
        
        var visibleItems = items
        
        if let truncation = truncation {
            visibleItems = truncation.truncate(forHeight: bounds.height, style: style)
        }
        let layout = Layout(items: visibleItems, style: style, bounds: bounds)
        
        indexView.frame = layout.contentFrame
{'fraction_non_alphanumeric': 0.07532515564322259, 'fraction_numerical': 0.001664303766257782, 'mean_word_length': 2.2519542994588093, 'pattern_counts': {'":': 0, '<': 12, '<?xml version=': 0, '>': 20, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2969214', 'n_tokens_mistral': 4157, 'n_tokens_neox': 4057, 'n_words': 1483}