Files
virtual-controller/remote-client-ios/remote-client-ios/Core/Widgets-Reusable-UI/FaderWidget.swift
T
Paul Lipscomb d6201c59b4 Multi-iPad dest_id routing, activity ring, context menu hide, live label/visibility sync
Desktop:
- Replace Remote checkbox with T: dest_spin (0=Off, 1-9=tablet) on all widgets
- dest_id persisted in get_state/set_state, broadcast in tablet snapshot
- dest_spin.valueChanged triggers save + broadcast to iPad
- _on_widget_visibility_from_tablet updates dest_spin from iPad hide event
- _broadcast_preset_update shared helper; ws_server.has_clients guard
- label editingFinished triggers live label sync to iPad

iOS:
- VC-Arrange/VC-Logging/Widgets-Reusable-UI file reorganisation
- ArrangeObjects/ArrangeView rename, 7-bucket Arrange-Functions split
- myTabletId (UserDefaults) + tabletIdField next to port in toolbar
- Preset filter: destId == myTabletId instead of visible bool
- BaseWidget activity ring: long press gesture (touch) + final update() flash
- Context menu long press (arrange mode): hide widget, save layout, send dest_id:0
- Same-UUID preset check skips Preset Switched alert on visibility/label updates
- autoSwitchEnabled, toolbarStatusLabel, presetLabel, statusToken

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-02 21:56:35 -04:00

89 lines
2.9 KiB
Swift

import UIKit
class FaderWidget: BaseWidget {
static let size = CGSize(width: 140, height: 360)
static func defaultFrame(idx: Int) -> CGRect {
let col = idx % 3
let row = idx / 3
return CGRect(x: 16 + CGFloat(col) * 170, y: 48 + CGFloat(row) * 380, width: size.width, height: size.height)
}
// MARK: - Properties
let color: UIColor
private var sliderWidthConstraint: NSLayoutConstraint?
// MARK: - Subviews
lazy var nameLabel: UILabel = {
let lbl = UILabel()
lbl.translatesAutoresizingMaskIntoConstraints = false
lbl.textColor = UIColor(white: 0.53, alpha: 1)
lbl.font = .systemFont(ofSize: 14)
lbl.textAlignment = .center
return lbl
}()
lazy var slider: UISlider = {
let s = UISlider()
s.translatesAutoresizingMaskIntoConstraints = false
s.minimumValue = 0
s.maximumValue = 127
s.transform = CGAffineTransform(rotationAngle: -.pi / 2)
return s
}()
// MARK: - Init
init(uid: String, label: String, value: Int, color: UIColor, frame: CGRect) {
self.color = color
super.init(uid: uid, frame: frame)
self.displayName = label
self.restingBorderColor = color.cgColor
self.layer.borderColor = color.cgColor
self.nameLabel.text = label
self.slider.value = Float(value)
self.slider.minimumTrackTintColor = color
setupUI()
}
required init?(coder: NSCoder) { fatalError() }
// MARK: - Layout
func setupUI() {
self.addSubview(self.nameLabel)
self.nameLabel.topAnchor.constraint(equalTo: self.topAnchor, constant: 8).isActive = true
self.nameLabel.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 4).isActive = true
self.nameLabel.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -4).isActive = true
self.addSubview(self.slider)
self.slider.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
self.slider.centerYAnchor.constraint(equalTo: self.centerYAnchor, constant: 10).isActive = true
let wc = self.slider.widthAnchor.constraint(equalToConstant: 216)
wc.isActive = true
self.sliderWidthConstraint = wc
self.slider.addTarget(self, action: #selector(self.sliderChanged), for: .valueChanged)
}
override func layoutSubviews() {
super.layoutSubviews()
self.sliderWidthConstraint?.constant = bounds.height * 0.6
}
// MARK: - Update
override func applyValue(_ value: Int) {
self.slider.value = Float(value)
}
// MARK: - Arrange Mode
override func setArrangeMode(_ enabled: Bool) {
self.slider.isUserInteractionEnabled = (enabled == false)
}
// MARK: - Action
@objc private func sliderChanged() {
self.onSend?(self.uid, Int(self.slider.value))
}
}