feat: "folder" onscreen button

This commit is contained in:
True Zhuanjia
2026-01-31 20:59:24 +08:00
parent 8f8e86ab4f
commit ac6c688054
12 changed files with 607 additions and 74 deletions
+2 -2
View File
@@ -2888,8 +2888,8 @@
<constraint firstAttribute="width" constant="180" id="TbL-OE-jvg"/>
</constraints>
<segments>
<segment title="display"/>
<segment title="hide"/>
<segment title="unfold"/>
<segment title="fold"/>
</segments>
<color key="tintColor" systemColor="systemGray3Color"/>
<color key="selectedSegmentTintColor" red="0.23087820410000001" green="0.4576539993" blue="0.51765710119999997" alpha="1" colorSpace="custom" customColorSpace="displayP3"/>
+58 -6
View File
@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="24412" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES" initialViewController="DL0-L5-LOv">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="24506" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES" initialViewController="DL0-L5-LOv">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="24405"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="24504"/>
<capability name="Image references" minToolsVersion="12.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
@@ -156,7 +156,7 @@
<viewControllerLayoutGuide type="bottom" id="TWA-bF-hnk"/>
</layoutGuides>
<view key="view" opaque="NO" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="Mwx-9q-yJJ">
<rect key="frame" x="0.0" y="0.0" width="1300" height="896"/>
<rect key="frame" x="0.0" y="0.0" width="1300" height="1800"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Jnu-63-bcf" customClass="ToolBarContainerView">
@@ -315,7 +315,7 @@
</constraints>
</view>
<stackView opaque="NO" contentMode="scaleToFill" ambiguous="YES" axis="vertical" spacing="7" translatesAutoresizingMaskIntoConstraints="NO" id="BVp-3O-vZ7" userLabel="widgetPanelStack" customClass="WidgetPanelStackView">
<rect key="frame" x="375" y="132" width="550" height="786"/>
<rect key="frame" x="375" y="132" width="550" height="813"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Profile:" textAlignment="center" lineBreakMode="tailTruncation" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="f46-Pt-0Pv">
<rect key="frame" x="0.0" y="0.0" width="550" height="28"/>
@@ -799,6 +799,55 @@
<constraint firstAttribute="height" constant="20" id="Qap-bn-YnJ"/>
</constraints>
</stackView>
<stackView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="weV-Li-Itz">
<rect key="frame" x="0.0" y="793" width="550" height="20"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Collected Widgets " textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.10000000000000001" translatesAutoresizingMaskIntoConstraints="NO" id="kek-77-Kmg" userLabel="uilabel">
<rect key="frame" x="0.0" y="0.0" width="120" height="20"/>
<constraints>
<constraint firstAttribute="width" constant="120" id="TPJ-nn-WHY"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<nil key="highlightedColor"/>
</label>
<segmentedControl opaque="NO" alpha="0.69999998807907104" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="0" translatesAutoresizingMaskIntoConstraints="NO" id="Oo7-lI-LXT">
<rect key="frame" x="120" y="0.0" width="161" height="21"/>
<color key="backgroundColor" white="0.33333333329999998" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<segments>
<segment title="unfold"/>
<segment title="fold"/>
</segments>
<color key="tintColor" systemColor="systemGray2Color"/>
<color key="selectedSegmentTintColor" red="0.23136288839999999" green="0.45776400810000001" blue="0.5149240678" alpha="1" colorSpace="custom" customColorSpace="displayP3"/>
</segmentedControl>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text=" Reveal mode " textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.10000000149011612" translatesAutoresizingMaskIntoConstraints="NO" id="YLN-X6-oj9" userLabel="uilabel">
<rect key="frame" x="281" y="0.0" width="120" height="20"/>
<constraints>
<constraint firstAttribute="width" constant="120" id="rIX-6N-uU4"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<nil key="highlightedColor"/>
</label>
<segmentedControl opaque="NO" alpha="0.69999998807907104" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="0" translatesAutoresizingMaskIntoConstraints="NO" id="myu-Th-CTZ">
<rect key="frame" x="401" y="0.0" width="149" height="21"/>
<color key="backgroundColor" white="0.33333333329999998" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="width" constant="149" id="UjR-CH-nwX"/>
</constraints>
<segments>
<segment title="coexist"/>
<segment title="exclusive"/>
</segments>
<color key="tintColor" systemColor="systemGray2Color"/>
<color key="selectedSegmentTintColor" red="0.23136288839999999" green="0.45776400810000001" blue="0.5149240678" alpha="1" colorSpace="custom" customColorSpace="displayP3"/>
</segmentedControl>
</subviews>
<constraints>
<constraint firstAttribute="height" constant="20" id="dxB-cV-EvQ"/>
</constraints>
</stackView>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.5" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
@@ -813,7 +862,7 @@
<constraint firstItem="Jnu-63-bcf" firstAttribute="top" secondItem="Mwx-9q-yJJ" secondAttribute="top" id="wNr-7F-S0i" userLabel="Toolbar Top Constraint iPhone"/>
</constraints>
</view>
<size key="freeformSize" width="1300" height="896"/>
<size key="freeformSize" width="1300" height="1800"/>
<connections>
<outlet property="autoTapField" destination="DOu-sG-eRq" id="Ubj-og-ThW"/>
<outlet property="autoTapLabel" destination="IuG-44-1Je" id="YWb-0d-zbB"/>
@@ -823,6 +872,8 @@
<outlet property="buttonModeStack" destination="jE7-IB-GTS" id="hKb-gp-eem"/>
<outlet property="chevronImageView" destination="76O-3K-aa8" id="yGv-fM-Ohf"/>
<outlet property="chevronView" destination="Zll-IP-PFt" id="MGR-C0-h57"/>
<outlet property="collectedWidgetsSelector" destination="Oo7-lI-LXT" id="ufg-er-4aZ"/>
<outlet property="collectedWidgetsStack" destination="weV-Li-Itz" id="SII-3v-ylG"/>
<outlet property="componentSizeLabel" destination="kcX-kR-3gl" id="FTP-ar-6Uh"/>
<outlet property="componentSizeSlider" destination="hTI-E5-8cp" id="TSO-4T-sF3"/>
<outlet property="componentSizeStack" destination="xNe-VP-2ix" id="zBD-X1-BNH"/>
@@ -838,6 +889,7 @@
<outlet property="pitchFactorLabel" destination="dEy-ER-Mb3" id="0or-ME-7P1"/>
<outlet property="pitchFactorSlider" destination="ol0-mA-VxW" id="bmd-0a-O4H"/>
<outlet property="pitchFactorStack" destination="gkc-td-lg4" id="gI0-q7-tO2"/>
<outlet property="revealModeSelector" destination="myu-Th-CTZ" id="9lE-Vs-cxe"/>
<outlet property="rollFactorLabel" destination="BNN-vq-Pve" id="4mm-f5-d93"/>
<outlet property="rollFactorSlider" destination="Ple-mD-zK4" id="sHy-HC-68W"/>
<outlet property="rollFactorStack" destination="7G4-Ms-mkR" id="EzV-6y-77h"/>
@@ -883,7 +935,7 @@
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="ORo-HP-JDj" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="157.97101449275362" y="1245.5357142857142"/>
<point key="canvasLocation" x="-96" y="1822"/>
</scene>
<!--Profiles Table View Controller-->
<scene sceneID="C7c-yg-gQK">
+1
View File
@@ -30,6 +30,7 @@ typedef NS_ENUM(NSInteger, MapGyroTo) {
@property NSString *name;
@property NSMutableArray <NSData *> *buttonStatesEncoded;
@property (nonatomic, assign) int16_t unfoldedExclusiveFolderSequence;
@property BOOL isSelected;
@property (nonatomic, assign) MapGyroTo mapGyroTo;
@property (nonatomic, assign) bool yawPitchToRightStick;
+3
View File
@@ -31,6 +31,7 @@
- (void) encodeWithCoder:(NSCoder*)encoder {
[encoder encodeObject:self.name forKey:@"name"];
[encoder encodeObject:self.buttonStatesEncoded forKey:@"buttonStates"];
[encoder encodeInt32:self.unfoldedExclusiveFolderSequence forKey:@"unfoldedExclusiveFolderSequence"];
[encoder encodeBool:self.isSelected forKey:@"isSelected"];
[encoder encodeInt64:self.mapGyroTo forKey:@"mapGyroTo"];
[encoder encodeBool:self.yawPitchToRightStick forKey:@"yawPitchToRightStick"];
@@ -73,6 +74,7 @@
[NSData class],
nil]
forKey:@"buttonStates"];
self.unfoldedExclusiveFolderSequence = [decoder containsValueForKey:@"unfoldedExclusiveFolderSequence"] ? [decoder decodeInt32ForKey:@"unfoldedExclusiveFolderSequence"] : -1;
self.isSelected = [decoder decodeBoolForKey:@"isSelected"];
self.mapGyroTo = [decoder containsValueForKey:@"mapGyroTo"] ? [decoder decodeInt64ForKey:@"mapGyroTo"] : mapGyroToMouse;
self.yawPitchToRightStick = [decoder containsValueForKey:@"yawPitchToRightStick"] ? [decoder decodeBoolForKey:@"yawPitchToRightStick"] : true;
@@ -131,6 +133,7 @@
OSCProfile *copy = [[[self class] allocWithZone:zone] init];
copy.name = [self.name mutableCopy]; // NSString → NSMutableString
copy.buttonStatesEncoded = [[NSMutableArray alloc] initWithArray:self.buttonStatesEncoded copyItems:YES];
copy.unfoldedExclusiveFolderSequence = self.unfoldedExclusiveFolderSequence;
copy.isSelected = self.isSelected;
copy.mapGyroTo = self.mapGyroTo;
copy.yawPitchToRightStick = self.yawPitchToRightStick;
@@ -291,6 +291,7 @@ static CGRect layoutViewBounds;
*/
OSCProfile *selectedProfile = [self getSelectedProfile];
selectedProfile.buttonStatesEncoded = buttonStatesEncoded;
selectedProfile.unfoldedExclusiveFolderSequence = OnScreenWidgetView.unfoldedExclusiveFolderSequence;
[self replaceSelectedProfileWith:selectedProfile overwriteDefault:NO];
return true;
}
@@ -389,10 +390,14 @@ static CGRect layoutViewBounds;
// save on-screen widget views (keyboard & mouse command) as buttonstate:
_widgetSizeTransition = keepWidgetSize;
for(OnScreenWidgetView* widgetView in OnScreenWidgetViews){
CGPoint normalizedPosition = [self normalizeWidgetPosition:widgetView.center];
CGPoint normalizedPosition = [self normalizeWidgetPosition:widgetView.storedCenter];
OnScreenButtonState *buttonState = [[OnScreenButtonState alloc] initWithButtonName:widgetView.cmdString buttonType:CustomOnScreenWidget andPosition:normalizedPosition];
buttonState.alias = widgetView.widgetLabel;
buttonState.sequence = widgetView.sequence;
buttonState.sequenceSet = widgetView.sequenceSet;
buttonState.parentSequence = widgetView.parentSequence;
buttonState.folded = widgetView.folded;
buttonState.revealMode = widgetView.revealMode;
buttonState.widthFactor = [self normalizeSizeWidthFactorWith:widgetView];
buttonState.heightFactor = [self normalizeSizeHeightFactorWith:widgetView];
buttonState.componentSizeFactor = [self normalizeComponentSizeFactorWith:widgetView];
@@ -421,7 +426,6 @@ static CGRect layoutViewBounds;
NSData *buttonStateEncoded = [NSKeyedArchiver archivedDataWithRootObject:buttonState requiringSecureCoding:YES error:nil];
[buttonStatesEncoded addObject: buttonStateEncoded];
}
return buttonStatesEncoded;
}
@@ -477,7 +481,7 @@ static CGRect layoutViewBounds;
- (OnScreenButtonState *)unarchiveButtonStateEncoded:(NSData *)data {
OnScreenButtonState* buttonState;
buttonState = [NSKeyedUnarchiver unarchivedObjectOfClasses:[NSSet setWithObjects:[NSString class], [OnScreenButtonState class], nil]
buttonState = [NSKeyedUnarchiver unarchivedObjectOfClasses:[NSSet setWithObjects:[NSString class], [OnScreenButtonState class], [NSSet class], nil]
fromData:data
error:nil];
return buttonState;
@@ -27,7 +27,8 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, assign) BOOL folded;
@property (nonatomic, assign) uint8_t revealMode;
@property (nonatomic, assign) int16_t sequence;
@property NSMutableSet *sequenceSet;
@property (nonatomic, assign) int16_t parentSequence;
@property NSSet *sequenceSet;
@property CGPoint position;
@property (nonatomic, assign) BOOL isHidden;
@property (nonatomic, assign) uint8_t buttonMode;
@@ -35,6 +35,7 @@
[encoder encodeBool:self.folded forKey:@"folded"];
[encoder encodeInt32:self.revealMode forKey:@"revealMode"];
[encoder encodeInt32:self.sequence forKey:@"sequence"];
[encoder encodeInt32:self.parentSequence forKey:@"parentSequence"];
[encoder encodeObject:self.sequenceSet forKey:@"sequenceSet"];
[encoder encodeObject:self.alias forKey:@"alias"];
[encoder encodeInt:self.widgetType forKey:@"buttonType"]; // keep original key
@@ -73,8 +74,9 @@
if (self = [super init]) {
self.name = [decoder decodeObjectForKey:@"name"];
self.folded = [decoder containsValueForKey:@"folded"] ? [decoder decodeBoolForKey:@"folded"] : false;
self.revealMode = [decoder containsValueForKey:@"revealMode"] ? [decoder decodeInt32ForKey:@"revealMode"] : exclusive;
self.revealMode = [decoder containsValueForKey:@"revealMode"] ? [decoder decodeInt32ForKey:@"revealMode"] : coexist;
self.sequence = [decoder containsValueForKey:@"sequence"] ? [decoder decodeInt32ForKey:@"sequence"] : -1;
self.parentSequence = [decoder containsValueForKey:@"parentSequence"] ? [decoder decodeInt32ForKey:@"parentSequence"] : -1;
self.sequenceSet = [decoder containsValueForKey:@"sequenceSet"] ? [decoder decodeObjectForKey:@"sequenceSet"] : [NSSet set];
self.alias = [decoder decodeObjectForKey:@"alias"];
self.widgetType = [decoder decodeIntForKey:@"buttonType"];
+184 -43
View File
@@ -10,7 +10,18 @@ import UIKit
import SVGKit
@objc class OnScreenWidgetView: UIView {
@objc public static var mapping: [Int16:OnScreenWidgetView] = [:]
@objc public static func set(widget:OnScreenWidgetView, for key:Int16) {
mapping[key] = widget
}
@objc public static func removeWidgetFromMappings(key:Int16) {
mapping.removeValue(forKey: key)
}
@objc public static func clearMappings() {
mapping.removeAll()
}
@objc public static var unfoldedExclusiveFolderSequence:Int16 = -1
@objc public weak var guidelineDelegate: OnScreenWidgetGuidelineUpdateDelegate?
@objc protocol OnScreenWidgetGuidelineUpdateDelegate: AnyObject {
func updateGuidelinesForOnScreenWidget(_ sender: Any)
@@ -263,8 +274,9 @@ import SVGKit
private var tickFlag: UInt8 = 0
@objc public var folded: Bool = false
@objc public var revealMode: RevealMode = .exclusive
@objc public var revealMode: RevealMode = .coexist
@objc public var sequenceSet: Set<Int16> = Set()
@objc public var parentSequence: Int16 = -1
private weak var capturer: OnScreenWidgetView?
@objc init(cmdString: String, buttonLabel: String, shape:String, profile:OSCProfile) {
@@ -1415,17 +1427,21 @@ import SVGKit
}
}
private func handleFingerUpOrSlideout() {
private func handleFingerUpOrSlideout(leaveFunctionalButtonAlone: Bool = false, event: UIEvent? = nil) {
print("handleFingerUpOrSlideout triggered, \(self.widgetLabel) \(CACurrentMediaTime())")
if autoTapInterval < OnScreenWidgetView.MinAutotapInterval {
handleButtonUp()
handleButtonUp(leaveFunctionalButtonAlone:leaveFunctionalButtonAlone, event:event)
}
else{
self.autoTapTimer?.pause()
self.handleButtonUp()
self.handleButtonUp(leaveFunctionalButtonAlone: leaveFunctionalButtonAlone)
}
}
private func handleButtonDown() {
if !self.isUserInteractionEnabled {return}
if !OnScreenWidgetView.editMode, !self.functionalButtonString.isEmpty {self.handleFunctionalButtonDown()}
if !OnScreenWidgetView.editMode {self.sendComboButtonsDownEvent(comboStrings: self.comboButtonStrings)}
@@ -1460,7 +1476,8 @@ import SVGKit
}
}
private func handleButtonUp() {
private func handleButtonUp(leaveFunctionalButtonAlone:Bool = false, event: UIEvent? = nil) {
if !self.isUserInteractionEnabled {return}
if !OnScreenWidgetView.editMode {self.sendComboButtonsUpEvent(comboStrings: self.comboButtonStrings)}
if !OnScreenWidgetView.editMode && !self.motionControlButtonString.isEmpty{
@@ -1470,7 +1487,7 @@ import SVGKit
self.buttonUpVisualEffect()
if !OnScreenWidgetView.editMode && !self.functionalButtonString.isEmpty{
self.handleFunctionalButtonUp()
if !leaveFunctionalButtonAlone {self.handleFunctionalButtonUp(event:event)}
}
// legacy keyboard button combo connected by "+"
@@ -1817,9 +1834,9 @@ import SVGKit
if(self.tapToToggleFlag) {self.handleTapDownOrSlidein()}
else {self.handleFingerUpOrSlideout()}
self.tapToToggleFlag = !self.tapToToggleFlag
case .slideToToggle:
case .slideToToggle where !self.isFolder:
self.handleButtonSliding(touches: touches)
case .slideAndHold:
case .slideAndHold where !self.isFolder:
self.handleButtonSliding(touches: touches)
default:
self.handleTapDownOrSlidein()
@@ -1843,11 +1860,11 @@ import SVGKit
capturer = nil
currentLocation = touch.location(in: superview)
forEachWidget { (widget) in
guard widget != self else {return}
if widget.isFolder {
if self.frame.intersects(widget.frame), !self.sequenceSet.contains(widget.sequence) {
if isLocation(currentLocation, in: widget), !self.sequenceSet.contains(widget.sequence) {
// self.layer.borderColor = UIColor.systemYellow.cgColor
widget.highlightBorder(highlighted: true)
if widget == self {return}
capturer = widget
return
}
@@ -1856,7 +1873,13 @@ import SVGKit
}
}
else {
if self.buttonMode == .movable {currentLocation = touch.location(in: superview)}
if self.buttonMode == .movable {
currentLocation = touch.location(in: superview)
for sequence in self.sequenceSet {
let widget = OnScreenWidgetView.mapping[sequence]
if widget?.isHidden == true {widget?.center = currentLocation}
}
}
else {return}
}
@@ -1872,7 +1895,10 @@ import SVGKit
let outOfBoundsX = center.x+offsetX >= (self.superview?.bounds.width)! || center.x+offsetX < 0
let outOfBoundsY = center.y+offsetY >= (self.superview?.bounds.height)! || center.y+offsetY < 0
if firstTouchMoved {center = CGPoint(x: outOfBoundsX ? center.x : center.x+offsetX, y: outOfBoundsY ? center.y : center.y+offsetY)}
if firstTouchMoved {
center = CGPoint(x: outOfBoundsX ? center.x : center.x+offsetX, y: outOfBoundsY ? center.y : center.y+offsetY)
storedCenter = center
}
latestTouchLocation = currentLocation
@@ -1919,14 +1945,17 @@ import SVGKit
}
}
private func handleFingerUpAfterSliding(touches: Set<UITouch>) {
private func handleFingerUpAfterSliding(touches: Set<UITouch>, event: UIEvent? = nil) {
// a special touchUp handling for .slideToToggle
for touch in touches {
self.forEachWidget(){ widget in
setLock.lock()
let captured = widget.capturedTouches.contains(touch)
setLock.unlock()
if !captured || widget.buttonMode == .regular {return}
widget.handleFingerUpOrSlideout()
let needReleaseButton = (isLocation(touch.location(in: superview), in: widget)
|| widget.buttonMode == .slideAndHold)
if needReleaseButton {widget.handleFingerUpOrSlideout(event: event)}
setLock.lock()
widget.capturedTouches.remove(touch)
setLock.unlock()
@@ -1953,7 +1982,10 @@ import SVGKit
let locationInSuperView = touch.location(in: self.superview)
self.forEachWidget{ widget in
if widget.widgetType != WidgetTypeEnum.button {return}
let isSlidableButton = widget.buttonMode == .slideToToggle || widget.buttonMode == .slideAndHold
let isSlidableButton = (widget.buttonMode == .slideToToggle
|| widget.buttonMode == .slideAndHold
|| (widget.buttonMode == .movable && widget != self)
)
if isLocation(locationInSuperView, in: widget){
setLock.lock()
let captured = widget.capturedTouches.contains(touch)
@@ -1972,8 +2004,8 @@ import SVGKit
setLock.unlock()
if !captured || !isSlidableButton {return}
// print("UIButton: \(widget.buttonLabel) out test, \(widget.touchPadString), \(CACurrentMediaTime())")
if(widget.buttonMode == .slideToToggle){
widget.handleFingerUpOrSlideout()
if(widget.buttonMode == .slideToToggle || widget.buttonMode == .movable){
widget.handleFingerUpOrSlideout(leaveFunctionalButtonAlone: widget.isFuncationalButton)
setLock.lock()
widget.capturedTouches.remove(touch)
setLock.unlock()
@@ -2271,6 +2303,10 @@ import SVGKit
private func handleFunctionalButtonDown(){
switch self.functionalButtonString {
case "FOLDER":
if self.buttonMode != .slideAndHold {break}
self.folded = false
OnScreenWidgetView.set(folded: false, for: self)
case "ABSTCHDRAG":
let mouseButton = CommandManager.mouseButtonMappings[Set(self.comboButtonStrings).intersection(CommandManager.mouseButtonMappings.keys).first ?? "MLEFT"] ?? BUTTON_LEFT
print("mouseButton \(mouseButton)");
@@ -2292,10 +2328,16 @@ import SVGKit
private var singleTouchEnabled:Bool = true
private func handleFunctionalButtonUp(){
private func handleFunctionalButtonUp(event: UIEvent? = nil){
print("handleFunctionalButtonUp \(CACurrentMediaTime())")
if buttonMode == .movable {
if moveableButtonLongPressed() {return}
if moveableButtonLongPressed() && !UITouchUtil.touches(in: self, from: event).isEmpty {return}
print("handleFunctionalButtonUp movable, \(moveableButtonLongPressed()) \(CACurrentMediaTime())")
switch self.functionalButtonString {
// case "FOLDER":
// self.folded = !self.folded
// OnScreenWidgetView.set(folded: self.folded, for: self)
case "NOSINGLETOUCH":
if !self.isPencilProEnabled() {break}
singleTouchEnabled = !singleTouchEnabled
@@ -2306,6 +2348,9 @@ import SVGKit
}
}
switch self.functionalButtonString {
case "FOLDER":
self.folded = !self.folded
OnScreenWidgetView.set(folded: self.folded, for: self)
case "SETTINGS":
self.functionalButtonDelegate?.expandSettingsView()
case "TOOLBOX":
@@ -2512,8 +2557,9 @@ import SVGKit
}
if !OnScreenWidgetView.editMode && !self.cmdString.contains("+") && !self.comboButtonStrings.isEmpty { // if the command(keystring contains "+", it's a legacy multi-key command
// print("self.comboButtonStrings \(self.comboButtonStrings),\(CACurrentMediaTime())")
if self.buttonMode == .slideToToggle || self.buttonMode == .slideAndHold {
self.handleFingerUpAfterSliding(touches: touches)
self.handleFingerUpAfterSliding(touches: touches, event: event) // .slideAndHold has to be released at touchesEnded, don't deal with it here.
setLock.lock()
self.capturedTouches.minus(touches)
setLock.unlock()
@@ -2532,11 +2578,10 @@ import SVGKit
if firstTouchMoved {undoRelocation()}
if let capturer = capturer {
capturer.highlightBorder(highlighted: false)
guard !self.sequenceSet.contains(capturer.sequence) else { return }
OnScreenWidgetView.put(widget: self, into: capturer)
self.isHidden = capturer.folded
OnScreenWidgetView.putWidget(self, into: capturer)
}
}
capturer = nil
guard let superview = superview else { return }
@@ -2570,7 +2615,10 @@ import SVGKit
}
}
if self.buttonMode != .tapToToggle {self.handleFingerUpOrSlideout()}
if (self.buttonMode != .tapToToggle
&& self.buttonMode != .slideToToggle
&& self.buttonMode != .slideAndHold
&& self == touches.first?.view) {self.handleFingerUpOrSlideout(event:event)}
}
@objc public func setAutoTapIntervalByText(str: String){
@@ -2685,44 +2733,137 @@ import SVGKit
return sequence+1
}
@objc static func setCollection(hidden:Bool, for folder:OnScreenWidgetView) {
private static func setCollection(hidden:Bool, for folder:OnScreenWidgetView, exception:OnScreenWidgetView? = nil, recursive:Bool = false) {
guard folder.isFolder else {return}
// guard folder.folded != hidden else { return }
folder.folded = hidden
folder.setupAtrributedText()
if hidden {
folder.forEachWidget{ widget in
if widget == folder {return}
if folder.sequenceSet.contains(widget.sequence){
DispatchQueue.main.async {widget.isHidden = hidden}
if widget.isFolder {
// OnScreenWidgetView.setCollection(hidden: hidden, for: widget)
guard widget != folder else {return}
if folder.sequenceSet.contains(widget.sequence), widget != exception{
DispatchQueue.main.async {
widget.isUserInteractionEnabled = false
UIView.animate(withDuration: folder.buttonMode == .slideAndHold ? 0.05 : 0.15, animations: {
widget.center = folder.center
},completion: { finished in
widget.isUserInteractionEnabled = !folder.folded
widget.center = folder.folded ? folder.storedCenter : widget.storedCenter
widget.isHidden = folder.folded
})
}
}
}
}
else{
folder.forEachWidget{ widget in
if widget == folder {return}
if folder.sequenceSet.contains(widget.sequence){
DispatchQueue.main.async {widget.isHidden = hidden}
guard widget != folder else {return}
if folder.sequenceSet.contains(widget.sequence), widget != exception{
DispatchQueue.main.async {
widget.center = folder.storedCenter
widget.isUserInteractionEnabled = false
widget.isHidden = false
UIView.animate(withDuration: folder.buttonMode == .slideAndHold ? 0.05 : 0.15, animations: {
widget.center = widget.storedCenter
},completion: { finished in
widget.isUserInteractionEnabled = !folder.folded
widget.center = folder.folded ? folder.storedCenter : widget.storedCenter
widget.isHidden = folder.folded
})
}
}
}
if folder.revealMode == .exclusive {
folder.forEachWidget{ widget in
if widget == folder {return}
if widget.isFolder, !widget.sequenceSet.contains(folder.sequence) {
OnScreenWidgetView.setCollection(hidden: true, for: widget)
}
}
if recursive {
folder.forEachWidget{ widget in
guard widget != folder else {return}
guard widget.isFolder, widget != exception else {return}
if folder.sequenceSet.contains(widget.sequence){
OnScreenWidgetView.setCollection(hidden: hidden, for: widget, exception: exception, recursive: true)
}
}
}
}
private static func put(widget:OnScreenWidgetView, into folder:OnScreenWidgetView){
widget.forEachWidget{ otherWidget in
otherWidget.sequenceSet.remove(widget.sequence)
@objc static func set(folded:Bool, for folder:OnScreenWidgetView) { // folder综合逻辑
guard folder.isFolder else {return}
setCollection(hidden: folded, for: folder)
if !folded, folder.revealMode == .exclusive {
OnScreenWidgetView.unfoldedExclusiveFolderSequence = folder.sequence
let currentRootFolder = OnScreenWidgetView.getRootFolder(of: folder) ?? folder
var offshootRootFolders:Set<OnScreenWidgetView> = Set()
folder.forEachWidget{ widget in
guard folder != widget else {return}
let rootFolder = getRootFolder(of: widget)
guard let rootFolder = rootFolder else {return}
offshootRootFolders.insert(rootFolder)
}
offshootRootFolders.remove(currentRootFolder)
guard !folder.sequenceSet.isEmpty else {return}
for folder in offshootRootFolders {
setCollection(hidden: true, for: folder, recursive: true)
}
guard currentRootFolder != folder else {return}
setCollection(hidden: true, for:currentRootFolder, exception: folder, recursive: true)
}
}
private static func getRootFolder(of widget:OnScreenWidgetView) -> OnScreenWidgetView?{
var widgetRef:OnScreenWidgetView? = widget
var parentFolder:OnScreenWidgetView? = OnScreenWidgetView.mapping[widgetRef?.parentSequence ?? -1]
repeat {
widgetRef = parentFolder
parentFolder = OnScreenWidgetView.mapping[widgetRef?.parentSequence ?? -1]
} while parentFolder != nil
return widgetRef
}
private static func getParentFolders(of widget: OnScreenWidgetView) -> Set<OnScreenWidgetView> {
var parents = Set<OnScreenWidgetView>()
var current = widget
while let parent = OnScreenWidgetView.mapping[current.parentSequence] {
parents.insert(parent)
current = parent
}
return parents
}
private static func putWidget(_ widget:OnScreenWidgetView, into folder:OnScreenWidgetView){
guard !OnScreenWidgetView.getParentFolders(of: folder).contains(widget) else { return }
let parentFolder = OnScreenWidgetView.mapping[widget.parentSequence]
if parentFolder != nil {
parentFolder?.sequenceSet.remove(widget.sequence)
}
widget.parentSequence = folder.sequence
folder.sequenceSet.insert(widget.sequence)
widget.isHidden = folder.folded
if folder.buttonMode == .slideAndHold, widget.isFuncationalButton {
widget.buttonMode = .slideToToggle
}
}
@objc static func setFree(widget:OnScreenWidgetView){
let parentFolder = OnScreenWidgetView.mapping[widget.parentSequence]
if parentFolder != nil {
parentFolder?.sequenceSet.remove(widget.sequence)
}
widget.parentSequence = -1
}
@objc static func restoreFoldedStates(){
var unfoldedExclusiveFolder: OnScreenWidgetView?
if unfoldedExclusiveFolderSequence != -1 {
unfoldedExclusiveFolder = OnScreenWidgetView.mapping[unfoldedExclusiveFolderSequence]
if(unfoldedExclusiveFolder != nil){
OnScreenWidgetView.set(folded: false, for: unfoldedExclusiveFolder!)
}
}
for widget in OnScreenWidgetView.mapping.values {
OnScreenWidgetView.setCollection(hidden: widget.folded, for: widget, exception: unfoldedExclusiveFolder)
}
}
override func didMoveToSuperview() {
+16 -7
View File
@@ -467,6 +467,7 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
- (void) clearOnScreenWidgets{
OnScreenWidgetView.isTweakingHighlight = false;
[OnScreenWidgetView clearMappings];
for (UIView *subview in self->_streamFrameTopLayerView.subviews) {
// 检查子视图是否是特定类型的实例
if ([subview isKindOfClass:[OnScreenWidgetView class]]) {
@@ -503,7 +504,7 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
}
}
NSLog(@"relocatedWidgetDict.allKeys.count %d, %f", relocatedWidgetDict.allKeys.count, CACurrentMediaTime());
// NSLog(@"relocatedWidgetDict.allKeys.count %d, %f", relocatedWidgetDict.allKeys.count, CACurrentMediaTime());
if(relocatedWidgetDict.allKeys.count == 0) return;
@@ -515,7 +516,7 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
OnScreenButtonState *newButtonState = [oscProfileMan unarchiveButtonStateEncoded:buttonStateEncoded];
if([relocatedWidgetDict.allKeys containsObject:@(newButtonState.sequence)]){
OnScreenWidgetView* widget = [relocatedWidgetDict objectForKey:@(newButtonState.sequence)];
newButtonState.position = [oscProfileMan normalizeWidgetPosition:widget.center];
newButtonState.position = [oscProfileMan normalizeWidgetPosition:widget.storedCenter];
NSData *newButtonStateEncoded = [NSKeyedArchiver archivedDataWithRootObject:newButtonState requiringSecureCoding:YES error:nil];
newProfile.buttonStatesEncoded[i] = newButtonStateEncoded;
}
@@ -560,11 +561,11 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
widgetView.sequence = buttonState.sequence == -1 ? i : buttonState.sequence;
if(!sequenceGenerated) sequenceGenerated = buttonState.sequence == -1;
if(!hasMovableWidget) hasMovableWidget = buttonState.buttonMode == movable;
if(sequenceGenerated){
NSLog(@"widgetView.sequence %d %f", widgetView.sequence, CACurrentMediaTime());
buttonState.sequence = widgetView.sequence;
oscProfile.buttonStatesEncoded[i] = [NSKeyedArchiver archivedDataWithRootObject:buttonState requiringSecureCoding:YES error:nil];
}
[OnScreenWidgetView setWithWidget:widgetView for:widgetView.sequence];
widgetView.sequenceSet = buttonState.sequenceSet;
widgetView.parentSequence = buttonState.parentSequence;
widgetView.folded = buttonState.folded;
widgetView.revealMode = buttonState.revealMode;
widgetView.translatesAutoresizingMaskIntoConstraints = NO; // weird but this is mandatory, or you will find no key views added to the right place
widgetView.widthFactor = buttonState.widthFactor;
@@ -602,6 +603,12 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
[widgetView tweakHighlightAlphaWithAlpha:buttonState.highlightAlpha];
[widgetView setupAutoTapTimer];
[widgetView setupInertialScrollerWithFps:self->settings.framerate.intValue];
if(sequenceGenerated){
// NSLog(@"widgetView.sequence %d %f", widgetView.sequence, CACurrentMediaTime());
buttonState.sequence = widgetView.sequence;
oscProfile.buttonStatesEncoded[i] = [NSKeyedArchiver archivedDataWithRootObject:buttonState requiringSecureCoding:YES error:nil];
}
}
else if(buttonState.widgetType == LegacyOnScreenControls) hasLegacyWidget = true;
}
@@ -636,6 +643,8 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
}
}
[OnScreenWidgetView restoreFoldedStates];
NSLog(@"hasLegacyWidget %d %f", hasLegacyWidget, CACurrentMediaTime());
// legacy widgets
if(!OnScreenWidgetView.editMode && (hasLegacyWidget || !reload)){
@@ -128,6 +128,7 @@ typedef NS_ENUM(NSUInteger, DecelerationRateSliderMode) {
}
- (void)clearOnScreenWidgets{
[OnScreenWidgetView clearMappings];
for (UIView *subview in self.view.subviews) {
if ([subview isKindOfClass:[OnScreenWidgetView class]]) {
[subview removeFromSuperview];
@@ -156,7 +157,14 @@ typedef NS_ENUM(NSUInteger, DecelerationRateSliderMode) {
NSLog(@"reloadOnScreenWidgets name %@", buttonState.name);
if(buttonState.widgetType == CustomOnScreenWidget){
OnScreenWidgetView* widgetView = [[OnScreenWidgetView alloc] initWithCmdString:buttonState.name buttonLabel:buttonState.alias shape:buttonState.widgetShape profile:oscProfile]; //reconstruct widgetView
widgetView.sequence = buttonState.sequence == -1 ? ++sequence : buttonState.sequence;
[OnScreenWidgetView setWithWidget:widgetView for:widgetView.sequence];
widgetView.sequenceSet = buttonState.sequenceSet;
widgetView.parentSequence = buttonState.parentSequence;
widgetView.folded = buttonState.folded;
widgetView.revealMode = buttonState.revealMode;
widgetView.guidelineDelegate = (id<OnScreenWidgetGuidelineUpdateDelegate>)self;
widgetView.translatesAutoresizingMaskIntoConstraints = NO; // weird but this is mandatory, or you will find no key views added to the right place
widgetView.widthFactor = buttonState.widthFactor;
@@ -194,6 +202,13 @@ typedef NS_ENUM(NSUInteger, DecelerationRateSliderMode) {
}
else if(buttonState.widgetType == LegacyOnScreenControls) hasLegacyWidget = true;
}
for(OnScreenWidgetView* widget in self.onScreenWidgetViews){
[widget accessWidgetAttributes];
// if(widget.isFolder) [OnScreenWidgetView setWithFolded:widget.folded for:widget];
}
[OnScreenWidgetView restoreFoldedStates];
NSLog(@"reloadOnScreenWidgets hasLegacyWidget %d %f", hasLegacyWidget, CACurrentMediaTime());
if(hasLegacyWidget) [self reloadLegacyOnScreenControls:oscProfile];
else{
@@ -789,6 +804,7 @@ typedef NS_ENUM(NSUInteger, DecelerationRateSliderMode) {
OSCProfile* profile = [[OSCProfilesManager sharedManager:CGRectZero] getSelectedProfile];
OnScreenWidgetView* newWidget = [[OnScreenWidgetView alloc] initWithCmdString:widgetInitParams[@"cmdString"] buttonLabel:widgetInitParams[@"buttonLabel"] shape:widgetInitParams[@"shape"] profile:profile]; //reconstruct widgetView
newWidget.sequence = widget.sequence;
newWidget.revealMode = widget.revealMode;
newWidget.guidelineDelegate = (id<OnScreenWidgetGuidelineUpdateDelegate>)self;
newWidget.translatesAutoresizingMaskIntoConstraints = NO; // weird but this is mandatory, or you will find no key views added to the right place
newWidget.widthFactor = widget.widthFactor;
@@ -824,7 +840,12 @@ typedef NS_ENUM(NSUInteger, DecelerationRateSliderMode) {
[newWidget setLocationWithPosition:CGPointMake(90, 130)];
newWidget.sequence = [newWidget getAvailableSequence];
}
else [newWidget setLocationWithPosition:widget.center];
else{
[newWidget setLocationWithPosition:widget.center];
newWidget.sequenceSet = [widget.sequenceSet mutableCopy];
newWidget.parentSequence = widget.parentSequence;
}
[OnScreenWidgetView setWithWidget:newWidget for:newWidget.sequence];
newWidget.sizeReference = widget.sizeReference;
[newWidget resizeWidgetView]; // resize must be called after relocation
[newWidget adjustTransparencyWithAlpha:widget.backgroundAlpha tweakBorderAlpha:NO];
@@ -849,6 +870,7 @@ typedef NS_ENUM(NSUInteger, DecelerationRateSliderMode) {
[self.view insertSubview:widgetView belowSubview:self.widgetPanelStack];
widgetView.hidden = true;
widgetView.sequence = [widgetView getAvailableSequence];
[OnScreenWidgetView setWithWidget:widgetView for:widgetView.sequence];
widgetView.guidelineDelegate = (id<OnScreenWidgetGuidelineUpdateDelegate>)self;
widgetView.translatesAutoresizingMaskIntoConstraints = NO; // weird but this is mandatory, or you will find no key views added to the right place
[self.onScreenWidgetViews addObject:widgetView];
@@ -1088,17 +1110,21 @@ typedef NS_ENUM(NSUInteger, DecelerationRateSliderMode) {
self.mouseButtonDownSelector.selectedSegmentIndex = selectedWidgetView.mouseButtonAction;
self.buttonModeStack.hidden = selectedWidgetView.widgetType != WidgetTypeEnumButton;
[self.buttonModeSelector setEnabled:!selectedWidgetView.isFuncationalButton forSegmentAtIndex:0];
[self.buttonModeSelector setEnabled:!selectedWidgetView.isFuncationalButton forSegmentAtIndex:1];
// bool slideToToggleEnabled = !selectedWidgetView.isFuncationalButton || selectedWidgetView.isFolder;
// [self.buttonModeSelector setEnabled:!selectedWidgetView.isFuncationalButton forSegmentAtIndex:0];
[self.buttonModeSelector setEnabled:true forSegmentAtIndex:slideToToggle];
bool slideAndHoldEnabled = !selectedWidgetView.isFuncationalButton || selectedWidgetView.isFolder;
[self.buttonModeSelector setEnabled:slideAndHoldEnabled forSegmentAtIndex:slideAndHold];
[self.buttonModeSelector setEnabled:!selectedWidgetView.isFolder forSegmentAtIndex:regular];
[self.buttonModeSelector setEnabled:!selectedWidgetView.isFuncationalButton
|| [selectedWidgetView.functionalButtonString containsString:@"ABSTCHDRAG"]
forSegmentAtIndex:3];
forSegmentAtIndex:tapToToggle];
[self.buttonModeSelector setSelectedSegmentIndex:selectedWidgetView.buttonMode];
self.collectedWidgetsStack.hidden = !selectedWidgetView.isFolder;
[self.collectedWidgetsSelector setSelectedSegmentIndex:selectedWidgetView.folded ? 1 :0];
[self.revealModeSelector setSelectedSegmentIndex:selectedWidgetView.revealMode];
[OnScreenWidgetView setCollectionWithHidden:selectedWidgetView.folded for:selectedWidgetView];
[OnScreenWidgetView setWithFolded:selectedWidgetView.folded for:selectedWidgetView];
[self autoFitStack:self.widgetPanelStack];
@@ -1264,14 +1290,14 @@ typedef NS_ENUM(NSUInteger, DecelerationRateSliderMode) {
- (void)collectionHiddenChanged:(UISegmentedControl* )sender{
if(self->selectedWidgetView != nil && self->widgetViewSelected){
[OnScreenWidgetView setCollectionWithHidden:sender.selectedSegmentIndex==1 for:selectedWidgetView];
[OnScreenWidgetView setWithFolded:sender.selectedSegmentIndex==1 for:selectedWidgetView];
}
}
- (void)revealModeChanged:(UISegmentedControl* )sender{
if(self->selectedWidgetView != nil && self->widgetViewSelected){
selectedWidgetView.revealMode = sender.selectedSegmentIndex;
[OnScreenWidgetView setCollectionWithHidden:selectedWidgetView.folded for:selectedWidgetView];
[OnScreenWidgetView setWithFolded:selectedWidgetView.folded for:selectedWidgetView];
}
}
@@ -1772,6 +1798,10 @@ typedef NS_ENUM(NSUInteger, DecelerationRateSliderMode) {
[self.layoutOSC updateGuidelinesForOnScreenWidget:widget];
[self.view bringSubviewToFront:widget];
trashCanButton.tintColor = trashCanButton.titleLabel.textColor = [self layerIsOverlappingWithTrashcanButton:widget.layer] ? [UIColor redColor] : trashCanStoryBoardColor;
if(trashCanButton.tintColor == [UIColor redColor] && selectedWidgetView != nil){
[OnScreenWidgetView setFreeWithWidget:selectedWidgetView];
}
self.undoButton.alpha = 1.0;
}
@@ -1861,6 +1891,8 @@ typedef NS_ENUM(NSUInteger, DecelerationRateSliderMode) {
if(!isToolbarHidden && self->selectedWidgetView != nil && [self layerIsOverlappingWithTrashcanButton:selectedWidgetView.layer]){
[OnScreenWidgetView setFreeWithWidget:selectedWidgetView];
[OnScreenWidgetView removeWidgetFromMappingsWithKey:selectedWidgetView.sequence];
[self->selectedWidgetView removeFromSuperview];
[self.onScreenWidgetViews removeObject:self->selectedWidgetView];
}
+112 -4
View File
@@ -4048,6 +4048,24 @@
"state" : "new",
"value" : " Reveal mode "
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : " 展开模式"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : " 展開模式"
}
},
"zh-HK" : {
"stringUnit" : {
"state" : "translated",
"value" : " 展開模式"
}
}
}
},
@@ -4756,6 +4774,24 @@
"state" : "new",
"value" : "coexist"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "共存"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "共存"
}
},
"zh-HK" : {
"stringUnit" : {
"state" : "translated",
"value" : "共存"
}
}
}
},
@@ -4768,6 +4804,24 @@
"state" : "new",
"value" : "exclusive"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "互斥"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "互斥"
}
},
"zh-HK" : {
"stringUnit" : {
"state" : "translated",
"value" : "互斥"
}
}
}
},
@@ -5048,25 +5102,61 @@
}
},
"sch-0e-nfa.segmentTitles[0]" : {
"comment" : "Class = \"UISegmentedControl\"; sch-0e-nfa.segmentTitles[0] = \"display\"; ObjectID = \"sch-0e-nfa\";",
"comment" : "Class = \"UISegmentedControl\"; sch-0e-nfa.segmentTitles[0] = \"unfold\"; ObjectID = \"sch-0e-nfa\";",
"extractionState" : "extracted_with_value",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "new",
"value" : "display"
"value" : "unfold"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "展开"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "展開"
}
},
"zh-HK" : {
"stringUnit" : {
"state" : "translated",
"value" : "展開"
}
}
}
},
"sch-0e-nfa.segmentTitles[1]" : {
"comment" : "Class = \"UISegmentedControl\"; sch-0e-nfa.segmentTitles[1] = \"hide\"; ObjectID = \"sch-0e-nfa\";",
"comment" : "Class = \"UISegmentedControl\"; sch-0e-nfa.segmentTitles[1] = \"fold\"; ObjectID = \"sch-0e-nfa\";",
"extractionState" : "extracted_with_value",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "new",
"value" : "hide"
"value" : "fold"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "收起"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "收起"
}
},
"zh-HK" : {
"stringUnit" : {
"state" : "translated",
"value" : "收起"
}
}
}
@@ -5518,6 +5608,24 @@
"state" : "new",
"value" : "Collected widgets "
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "已收纳控件 "
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "已收納小工具 "
}
},
"zh-HK" : {
"stringUnit" : {
"state" : "translated",
"value" : "已收納小工具 "
}
}
}
},
+180
View File
@@ -2815,6 +2815,36 @@
}
}
},
"kek-77-Kmg.text" : {
"comment" : "Class = \"UILabel\"; text = \"Collected Widgets \"; ObjectID = \"kek-77-Kmg\";",
"extractionState" : "extracted_with_value",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "new",
"value" : "Collected Widgets "
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "已收纳控件 "
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "已收納小工具 "
}
},
"zh-HK" : {
"stringUnit" : {
"state" : "translated",
"value" : "已收納小工具 "
}
}
}
},
"kva-gQ-CZh.text" : {
"comment" : "Class = \"UILabel\"; text = \"Size\"; ObjectID = \"kva-gQ-CZh\";",
"extractionState" : "extracted_with_value",
@@ -3205,6 +3235,66 @@
}
}
},
"myu-Th-CTZ.segmentTitles[0]" : {
"comment" : "Class = \"UISegmentedControl\"; myu-Th-CTZ.segmentTitles[0] = \"coexist\"; ObjectID = \"myu-Th-CTZ\";",
"extractionState" : "extracted_with_value",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "new",
"value" : "coexist"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "共存"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "共存"
}
},
"zh-HK" : {
"stringUnit" : {
"state" : "translated",
"value" : "共存"
}
}
}
},
"myu-Th-CTZ.segmentTitles[1]" : {
"comment" : "Class = \"UISegmentedControl\"; myu-Th-CTZ.segmentTitles[1] = \"exclusive\"; ObjectID = \"myu-Th-CTZ\";",
"extractionState" : "extracted_with_value",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "new",
"value" : "exclusive"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "互斥"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "互斥"
}
},
"zh-HK" : {
"stringUnit" : {
"state" : "translated",
"value" : "互斥"
}
}
}
},
"n6Z-cT-3a1.text" : {
"comment" : "Class = \"UILabel\"; text = \"Mic Volume\"; ObjectID = \"n6Z-cT-3a1\";",
"extractionState" : "extracted_with_value",
@@ -3775,6 +3865,66 @@
}
}
},
"Oo7-lI-LXT.segmentTitles[0]" : {
"comment" : "Class = \"UISegmentedControl\"; Oo7-lI-LXT.segmentTitles[0] = \"unfold\"; ObjectID = \"Oo7-lI-LXT\";",
"extractionState" : "extracted_with_value",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "new",
"value" : "unfold"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "展开"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "展開"
}
},
"zh-HK" : {
"stringUnit" : {
"state" : "translated",
"value" : "展開"
}
}
}
},
"Oo7-lI-LXT.segmentTitles[1]" : {
"comment" : "Class = \"UISegmentedControl\"; Oo7-lI-LXT.segmentTitles[1] = \"fold\"; ObjectID = \"Oo7-lI-LXT\";",
"extractionState" : "extracted_with_value",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "new",
"value" : "fold"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "收起"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "收起"
}
},
"zh-HK" : {
"stringUnit" : {
"state" : "translated",
"value" : "收起"
}
}
}
},
"ozU-7C-qKE.text" : {
"comment" : "Class = \"UILabel\"; text = \"Button down\"; ObjectID = \"ozU-7C-qKE\";",
"extractionState" : "extracted_with_value",
@@ -5527,6 +5677,36 @@
}
}
},
"YLN-X6-oj9.text" : {
"comment" : "Class = \"UILabel\"; text = \" Reveal mode \"; ObjectID = \"YLN-X6-oj9\";",
"extractionState" : "extracted_with_value",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "new",
"value" : " Reveal mode "
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : " 展开模式"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : " 展開模式"
}
},
"zh-HK" : {
"stringUnit" : {
"state" : "translated",
"value" : " 展開模式"
}
}
}
},
"Z9M-yq-an2.title" : {
"comment" : "Class = \"UIBarButtonItem\"; title = \"Import\"; ObjectID = \"Z9M-yq-an2\";",
"extractionState" : "extracted_with_value",