1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-09-30 07:55:01 +00:00

Starts towards using an in-window options panel.

With the same fade in/out behaviour as the volume control.
This commit is contained in:
Thomas Harte 2021-07-12 22:38:08 -04:00
parent 6e62e4e296
commit 324edcb391
2 changed files with 138 additions and 107 deletions

View File

@ -8,97 +8,69 @@
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="MachineDocument" customModule="Clock_Signal" customModuleProvider="target">
<connections>
<outlet property="optionsPanel" destination="gsl-7V-TTU" id="BEE-05-h0B"/>
<outlet property="optionsView" destination="fX1-EX-wGf" id="dPS-F9-xmL"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<window title="Options" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" hidesOnDeactivate="YES" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" id="gsl-7V-TTU" customClass="Atari2600OptionsPanel" customModule="Clock_Signal" customModuleProvider="target">
<windowStyleMask key="styleMask" titled="YES" closable="YES" utility="YES" nonactivatingPanel="YES" HUD="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="80" y="150" width="200" height="121"/>
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1440"/>
<view key="contentView" id="aQh-Pm-DEo">
<rect key="frame" x="0.0" y="0.0" width="200" height="121"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="rQO-uD-fwn">
<rect key="frame" x="13" y="74" width="88" height="32"/>
<buttonCell key="cell" type="push" title="Reset" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="l3H-0m-aK0">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="optionWasPressed:" target="gsl-7V-TTU" id="wbx-SP-OZc"/>
</connections>
</button>
<button translatesAutoresizingMaskIntoConstraints="NO" id="3qw-C1-NYW">
<rect key="frame" x="18" y="58" width="162" height="18"/>
<buttonCell key="cell" type="check" title="Black and White" bezelStyle="regularSquare" imagePosition="left" inset="2" id="UP7-mf-IKo">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="optionDidChange:" target="gsl-7V-TTU" id="s5e-66-aY1"/>
</connections>
</button>
<button translatesAutoresizingMaskIntoConstraints="NO" id="Xbc-cw-Sc2">
<rect key="frame" x="18" y="36" width="162" height="18"/>
<buttonCell key="cell" type="check" title="Left Player Difficulty" bezelStyle="regularSquare" imagePosition="left" inset="2" id="wlJ-8s-PEh">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="optionDidChange:" target="gsl-7V-TTU" id="PbG-zF-y0W"/>
</connections>
</button>
<button translatesAutoresizingMaskIntoConstraints="NO" id="kPV-Tm-TTc">
<rect key="frame" x="18" y="14" width="162" height="18"/>
<buttonCell key="cell" type="check" title="Right Player Difficulty" bezelStyle="regularSquare" imagePosition="left" inset="2" id="F05-cA-66S">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="optionDidChange:" target="gsl-7V-TTU" id="XMR-tK-HN5"/>
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="nt7-8K-xY9">
<rect key="frame" x="99" y="74" width="88" height="32"/>
<buttonCell key="cell" type="push" title="Select" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="8Na-Z1-EXS">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="optionWasPressed:" target="gsl-7V-TTU" id="db2-Bu-6h9"/>
</connections>
</button>
</subviews>
<constraints>
<constraint firstItem="3qw-C1-NYW" firstAttribute="leading" secondItem="aQh-Pm-DEo" secondAttribute="leading" constant="20" id="1Ri-ZO-GJy"/>
<constraint firstItem="nt7-8K-xY9" firstAttribute="leading" secondItem="rQO-uD-fwn" secondAttribute="trailing" constant="12" id="46p-Z3-kgW"/>
<constraint firstItem="nt7-8K-xY9" firstAttribute="top" secondItem="aQh-Pm-DEo" secondAttribute="top" constant="20" id="6Uc-12-11y"/>
<constraint firstItem="Xbc-cw-Sc2" firstAttribute="leading" secondItem="aQh-Pm-DEo" secondAttribute="leading" constant="20" id="7es-iv-JOh"/>
<constraint firstItem="kPV-Tm-TTc" firstAttribute="top" secondItem="Xbc-cw-Sc2" secondAttribute="bottom" constant="6" id="Env-nl-M2e"/>
<constraint firstAttribute="trailing" secondItem="kPV-Tm-TTc" secondAttribute="trailing" constant="20" id="Fim-Ej-8Ux"/>
<constraint firstAttribute="trailing" secondItem="Xbc-cw-Sc2" secondAttribute="trailing" constant="20" id="HkS-6c-WZm"/>
<constraint firstItem="3qw-C1-NYW" firstAttribute="top" secondItem="nt7-8K-xY9" secondAttribute="bottom" constant="6" id="Hxq-Pm-o4G"/>
<constraint firstAttribute="trailing" secondItem="nt7-8K-xY9" secondAttribute="trailing" constant="20" id="JRO-de-WQp"/>
<constraint firstItem="rQO-uD-fwn" firstAttribute="top" secondItem="aQh-Pm-DEo" secondAttribute="top" constant="20" id="N3p-aY-2Nx"/>
<constraint firstItem="nt7-8K-xY9" firstAttribute="width" secondItem="rQO-uD-fwn" secondAttribute="width" id="NOc-hJ-8Mm"/>
<constraint firstItem="Xbc-cw-Sc2" firstAttribute="top" secondItem="3qw-C1-NYW" secondAttribute="bottom" constant="6" id="ORX-bF-2WS"/>
<constraint firstItem="kPV-Tm-TTc" firstAttribute="leading" secondItem="aQh-Pm-DEo" secondAttribute="leading" constant="20" id="x8p-Hm-xeu"/>
<constraint firstItem="rQO-uD-fwn" firstAttribute="leading" secondItem="aQh-Pm-DEo" secondAttribute="leading" constant="20" id="xhD-iY-vt2"/>
<constraint firstAttribute="trailing" secondItem="3qw-C1-NYW" secondAttribute="trailing" constant="20" id="yff-e9-OBY"/>
</constraints>
</view>
<connections>
<outlet property="colourButton" destination="3qw-C1-NYW" id="5HZ-fq-XtP"/>
<outlet property="leftPlayerDifficultyButton" destination="Xbc-cw-Sc2" id="OfB-Hr-fDC"/>
<outlet property="resetButton" destination="rQO-uD-fwn" id="XsE-tH-9oS"/>
<outlet property="rightPlayerDifficultyButton" destination="kPV-Tm-TTc" id="BGU-Hi-AZl"/>
<outlet property="selectButton" destination="nt7-8K-xY9" id="AF6-Qk-HZN"/>
</connections>
<point key="canvasLocation" x="157" y="12.5"/>
</window>
<visualEffectView hidden="YES" appearanceType="vibrantDark" blendingMode="withinWindow" material="HUDWindow" state="followsWindowActiveState" translatesAutoresizingMaskIntoConstraints="NO" id="fX1-EX-wGf">
<rect key="frame" x="0.0" y="0.0" width="200" height="140"/>
<subviews>
<button wantsLayer="YES" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="pzn-mL-BPh">
<rect key="frame" x="13" y="93" width="88" height="32"/>
<buttonCell key="cell" type="push" title="Reset" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="W9s-t2-TJ5">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
</button>
<button translatesAutoresizingMaskIntoConstraints="NO" id="YZK-m7-ihU">
<rect key="frame" x="18" y="63" width="162" height="18"/>
<buttonCell key="cell" type="check" title="Black and White" bezelStyle="regularSquare" imagePosition="left" inset="2" id="IxG-1J-fdG">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
</button>
<button translatesAutoresizingMaskIntoConstraints="NO" id="nEt-YK-aWs">
<rect key="frame" x="18" y="41" width="162" height="18"/>
<buttonCell key="cell" type="check" title="Left Player Difficulty" bezelStyle="regularSquare" imagePosition="left" inset="2" id="YOY-n5-F4C">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
</button>
<button translatesAutoresizingMaskIntoConstraints="NO" id="pDu-eg-6n6">
<rect key="frame" x="18" y="19" width="162" height="18"/>
<buttonCell key="cell" type="check" title="Right Player Difficulty" bezelStyle="regularSquare" imagePosition="left" inset="2" id="Q2e-Fa-VyK">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
</button>
<button wantsLayer="YES" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="YqD-w8-fXK">
<rect key="frame" x="99" y="93" width="88" height="32"/>
<buttonCell key="cell" type="push" title="Select" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="0DC-Xk-VFt">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
</button>
</subviews>
<constraints>
<constraint firstAttribute="trailing" secondItem="pDu-eg-6n6" secondAttribute="trailing" constant="20" symbolic="YES" id="5ye-bu-m6o"/>
<constraint firstItem="pzn-mL-BPh" firstAttribute="leading" secondItem="fX1-EX-wGf" secondAttribute="leading" constant="20" symbolic="YES" id="6QS-0r-f3d"/>
<constraint firstAttribute="trailing" secondItem="YqD-w8-fXK" secondAttribute="trailing" constant="20" symbolic="YES" id="7cJ-oo-obl"/>
<constraint firstItem="YqD-w8-fXK" firstAttribute="leading" secondItem="pzn-mL-BPh" secondAttribute="trailing" constant="12" symbolic="YES" id="BcE-ir-sSp"/>
<constraint firstItem="pzn-mL-BPh" firstAttribute="top" secondItem="fX1-EX-wGf" secondAttribute="top" constant="20" symbolic="YES" id="Dbu-nw-TCe"/>
<constraint firstAttribute="bottom" secondItem="pDu-eg-6n6" secondAttribute="bottom" constant="20" symbolic="YES" id="Dn7-jh-quS"/>
<constraint firstItem="pDu-eg-6n6" firstAttribute="top" secondItem="nEt-YK-aWs" secondAttribute="bottom" constant="6" symbolic="YES" id="EpU-Dx-JTJ"/>
<constraint firstItem="nEt-YK-aWs" firstAttribute="leading" secondItem="fX1-EX-wGf" secondAttribute="leading" constant="20" symbolic="YES" id="Hju-rR-j7R"/>
<constraint firstItem="pDu-eg-6n6" firstAttribute="leading" secondItem="fX1-EX-wGf" secondAttribute="leading" constant="20" symbolic="YES" id="PGK-pi-Ffh"/>
<constraint firstItem="nEt-YK-aWs" firstAttribute="top" secondItem="YZK-m7-ihU" secondAttribute="bottom" constant="6" symbolic="YES" id="R3E-mQ-u1P"/>
<constraint firstItem="YZK-m7-ihU" firstAttribute="leading" secondItem="fX1-EX-wGf" secondAttribute="leading" constant="20" symbolic="YES" id="ZZ6-MO-XuN"/>
<constraint firstItem="YZK-m7-ihU" firstAttribute="top" secondItem="YqD-w8-fXK" secondAttribute="bottom" constant="20" symbolic="YES" id="cMM-Rh-C4P"/>
<constraint firstAttribute="trailing" secondItem="nEt-YK-aWs" secondAttribute="trailing" constant="20" symbolic="YES" id="eqF-G9-V65"/>
<constraint firstAttribute="trailing" secondItem="YZK-m7-ihU" secondAttribute="trailing" constant="20" symbolic="YES" id="hR2-qd-sfM"/>
<constraint firstItem="YqD-w8-fXK" firstAttribute="width" secondItem="pzn-mL-BPh" secondAttribute="width" id="uTY-TW-OMR"/>
<constraint firstItem="YqD-w8-fXK" firstAttribute="top" secondItem="fX1-EX-wGf" secondAttribute="top" constant="20" symbolic="YES" id="xPH-7Q-jTz"/>
</constraints>
<point key="canvasLocation" x="139" y="214"/>
</visualEffectView>
</objects>
</document>

View File

@ -49,6 +49,9 @@ class MachineDocument:
/// The options panel, if any.
@IBOutlet var optionsPanel: MachinePanel!
/// The options view, if any.
@IBOutlet var optionsView: NSView!
/// An action to display the options panel, if there is one.
@IBAction func showOptions(_ sender: AnyObject!) {
optionsPanel?.setIsVisible(true)
@ -217,9 +220,44 @@ class MachineDocument:
// Attach an options panel if one is available.
if let optionsPanelNibName = self.machineDescription?.optionsPanelNibName {
Bundle.main.loadNibNamed(optionsPanelNibName, owner: self, topLevelObjects: nil)
self.optionsPanel.machine = machine
self.optionsPanel?.establishStoredOptions()
showOptions(self)
if let optionsPanel = self.optionsPanel {
optionsPanel.machine = machine
optionsPanel.establishStoredOptions()
showOptions(self)
}
if let optionsView = self.optionsView, let superview = self.volumeView.superview {
// Apply rounded edges.
optionsView.wantsLayer = true
optionsView.layer?.cornerRadius = 5.0
// optionsView.translatesAutoresizingMaskIntoConstraints = false
// Add to the superview.
superview.addSubview(optionsView)
// Apply constraints to appear centred and above the volume view.
let centreConstraint = NSLayoutConstraint(
item: optionsView,
attribute: .centerX,
relatedBy: .equal,
toItem: self.volumeView,
attribute: .centerX,
multiplier: 1.0,
constant: 0.0
)
superview.addConstraint(centreConstraint)
let verticalConstraint = NSLayoutConstraint(
item: optionsView,
attribute: .bottom,
relatedBy: .equal,
toItem: self.volumeView,
attribute: .top,
multiplier: 1.0,
constant: -8.0 // TODO: find a way to use an OS-supplied standard value here.
)
superview.addConstraint(verticalConstraint)
}
}
// Create and populate an activity display if required.
@ -713,37 +751,58 @@ class MachineDocument:
// So, the workaround: make my CAAnimationDelegate something that doesn't
// appear in the bridging header.
fileprivate class ViewFader: NSObject, CAAnimationDelegate {
var volumeView: NSView
var views: [NSView]
init(view: NSView) {
volumeView = view
init(views: [NSView]) {
self.views = views
}
func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
volumeView.isHidden = true
for view in views {
view.isHidden = true
}
}
}
fileprivate var animationFader: ViewFader? = nil
var fadingViews: [NSView] {
get {
var views: [NSView] = []
if let optionsView = self.optionsView {
views.append(optionsView)
}
if let volumeView = self.volumeView {
views.append(volumeView)
}
return views
}
}
internal func scanTargetViewDidShowOSMouseCursor(_ view: CSScanTargetView) {
// The OS mouse cursor became visible, so show the volume controls.
animationFader = nil
volumeView.layer?.removeAllAnimations()
volumeView.isHidden = false
volumeView.layer?.opacity = 1.0
for view in self.fadingViews {
view.layer?.removeAllAnimations()
view.isHidden = false
view.layer?.opacity = 1.0
}
}
internal func scanTargetViewWillHideOSMouseCursor(_ view: CSScanTargetView) {
// The OS mouse cursor will be hidden, so hide the volume controls.
if !volumeView.isHidden && volumeView.layer?.animation(forKey: "opacity") == nil {
let fadeAnimation = CABasicAnimation(keyPath: "opacity")
fadeAnimation.fromValue = 1.0
fadeAnimation.toValue = 0.0
fadeAnimation.duration = 0.2
animationFader = ViewFader(view: volumeView)
fadeAnimation.delegate = animationFader
volumeView.layer?.add(fadeAnimation, forKey: "opacity")
volumeView.layer?.opacity = 0.0
let fadingViews = self.fadingViews
if !fadingViews[0].isHidden && fadingViews[0].layer?.animation(forKey: "opacity") == nil {
for view in self.fadingViews {
let fadeAnimation = CABasicAnimation(keyPath: "opacity")
fadeAnimation.fromValue = 1.0
fadeAnimation.toValue = 0.0
fadeAnimation.duration = 0.2
fadeAnimation.delegate = animationFader
view.layer?.add(fadeAnimation, forKey: "opacity")
view.layer?.opacity = 0.0
}
animationFader = ViewFader(views: fadingViews)
}
}