// // AudioMixerViewController.swift // XIONControlPanel // import UIKit import WebKit final class AudioMixerViewController: UIViewController, WKNavigationDelegate { static let controlsWidth: CGFloat = 54.0 var closeHandler: (() -> Void)? private static let mixerURL = URL(string: "http://avmotu.localdomain/#touch-console")! private static let accentColor = UIColor(red: 0.0, green: 0.95, blue: 0.95, alpha: 1.0) private let _panelView = UIView(frame: .zero) private let _webView = WKWebView(frame: .zero, configuration: WKWebViewConfiguration()) private let _closeButton = UIButton(type: .custom) private let _refreshButton = UIButton(type: .custom) private let _loadingProgressView = UIProgressView(progressViewStyle: .bar) private var _progressObservation: NSKeyValueObservation? override func loadView() { view = UIView(frame: .zero) view.backgroundColor = .clear view.clipsToBounds = false } override func viewDidLoad() { super.viewDidLoad() _panelView.backgroundColor = .black _panelView.layer.borderColor = Self.accentColor.withAlphaComponent(0.65).cgColor _panelView.layer.borderWidth = 1.0 _panelView.layer.shadowColor = UIColor.black.cgColor _panelView.layer.shadowOpacity = 0.95 _panelView.layer.shadowRadius = 116.0 _panelView.layer.shadowOffset = CGSize(width: -10.0, height: 0.0) view.addSubview(_panelView) _webView.navigationDelegate = self _webView.isOpaque = false _webView.backgroundColor = .black _webView.scrollView.backgroundColor = .black _webView.allowsBackForwardNavigationGestures = false _panelView.addSubview(_webView) _configureControlTab(_closeButton, accessibilityLabel: "Close Mixer") _closeButton.setTitle("X", for: .normal) _closeButton.titleLabel?.font = UIFont(name: "Orbitron-Medium", size: 18.0) _closeButton.accessibilityIdentifier = "audio.close" _closeButton.addTarget(self, action: #selector(_close), for: .touchUpInside) view.addSubview(_closeButton) _configureControlTab(_refreshButton, accessibilityLabel: "Refresh Mixer") let refreshImage = UIImage( systemName: "arrow.clockwise", withConfiguration: UIImage.SymbolConfiguration(pointSize: 20.0, weight: .medium) ) _refreshButton.setImage(refreshImage, for: .normal) _refreshButton.tintColor = Self.accentColor _refreshButton.accessibilityIdentifier = "audio.refresh" _refreshButton.addTarget(self, action: #selector(_refresh), for: .touchUpInside) view.addSubview(_refreshButton) _loadingProgressView.progressTintColor = Self.accentColor _loadingProgressView.trackTintColor = UIColor(white: 0.15, alpha: 1.0) _panelView.addSubview(_loadingProgressView) _progressObservation = _webView.observe( \.estimatedProgress, options: [.new] ) { [weak self] webView, _ in guard let self else { return } let progress = Float(webView.estimatedProgress) self._loadingProgressView.setProgress(progress, animated: true) self._loadingProgressView.isHidden = progress >= 1.0 } _loadMixer() } override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() let contentFrame = CGRect( x: Self.controlsWidth, y: 0.0, width: max(0.0, view.bounds.width - Self.controlsWidth), height: view.bounds.height ) _panelView.frame = contentFrame _panelView.layer.shadowPath = UIBezierPath(rect: _panelView.bounds).cgPath _webView.frame = _panelView.bounds _loadingProgressView.frame = CGRect( x: 1.0, y: 1.0, width: max(0.0, _panelView.bounds.width - 2.0), height: 2.0 ) let tabHeight: CGFloat = 54.0 let tabSpacing: CGFloat = 5.0 let tabTop: CGFloat = 16.0 _closeButton.frame = CGRect( x: 0.0, y: tabTop, width: Self.controlsWidth + 1.0, height: tabHeight ) _refreshButton.frame = CGRect( x: 0.0, y: _closeButton.frame.maxY + tabSpacing, width: Self.controlsWidth + 1.0, height: tabHeight ) view.bringSubviewToFront(_closeButton) view.bringSubviewToFront(_refreshButton) } private func _configureControlTab(_ button: UIButton, accessibilityLabel: String) { button.backgroundColor = UIColor(white: 0.2, alpha: 1.0) button.setTitleColor(Self.accentColor, for: .normal) button.accessibilityTraits = .button button.accessibilityLabel = accessibilityLabel button.layer.borderColor = Self.accentColor.withAlphaComponent(0.65).cgColor button.layer.borderWidth = 1.0 } private func _loadMixer() { _loadingProgressView.isHidden = false _loadingProgressView.progress = 0.0 let request = URLRequest( url: Self.mixerURL, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: 30.0 ) _webView.load(request) } @objc private func _close() { closeHandler?() } @objc private func _refresh() { if _webView.url == nil { _loadMixer() } else { _webView.reload() } } }