diff --git a/Limelight/Base.lproj/iPad.storyboard b/Limelight/Base.lproj/iPad.storyboard
index 848a5fbe..42be2326 100644
--- a/Limelight/Base.lproj/iPad.storyboard
+++ b/Limelight/Base.lproj/iPad.storyboard
@@ -751,14 +751,14 @@ ICAgICAgICAgICAgICA
-
+
-
+
-
+
-
+
+
+
-
@@ -807,42 +820,43 @@ ICAgICAgICAgICAgICA
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
+
+
-
+
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
+
@@ -939,6 +953,7 @@ ICAgICAgICAgICAgICA
+
@@ -1038,7 +1053,7 @@ ICAgICAgICAgICAgICA
-
+
diff --git a/Limelight/Base.lproj/iPhone.storyboard b/Limelight/Base.lproj/iPhone.storyboard
index b1e4837b..cc952df2 100644
--- a/Limelight/Base.lproj/iPhone.storyboard
+++ b/Limelight/Base.lproj/iPhone.storyboard
@@ -722,7 +722,7 @@ ICAgICAgICAgICAgIA
-
+
@@ -732,6 +732,7 @@ ICAgICAgICAgICAgIA
+
@@ -749,14 +750,14 @@ ICAgICAgICAgICAgIA
-
+
-
+
-
+
@@ -788,27 +789,22 @@ ICAgICAgICAgICAgIA
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -824,35 +820,44 @@ ICAgICAgICAgICAgIA
-
+
+
-
-
-
-
-
-
-
-
-
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
+
@@ -942,7 +947,7 @@ ICAgICAgICAgICAgIA
-
+
diff --git a/Limelight/Input/CommandManager.swift b/Limelight/Input/CommandManager.swift
index 534202ff..1c77081c 100644
--- a/Limelight/Input/CommandManager.swift
+++ b/Limelight/Input/CommandManager.swift
@@ -19,29 +19,29 @@ import UIKit
// MARK: - Properties
- @objc var keyboardCmdString: String
+ @objc var cmdString: String
@objc var alias: String
// MARK: - Initialization
- init(keyboardCmdString: String, alias: String) {
- self.keyboardCmdString = keyboardCmdString
+ init(cmdString: String, alias: String) {
+ self.cmdString = cmdString
self.alias = alias
}
// MARK: - NSSecureCoding
required public init?(coder: NSCoder) {
- guard let keyboardCmdString = coder.decodeObject(of: NSString.self, forKey: "keyboardCmdString") as String?,
+ guard let cmdString = coder.decodeObject(of: NSString.self, forKey: "keyboardCmdString") as String?,
let alias = coder.decodeObject(of: NSString.self, forKey: "alias") as String? else {
return nil
}
- self.keyboardCmdString = keyboardCmdString
+ self.cmdString = cmdString
self.alias = alias
}
public func encode(with coder: NSCoder) {
- coder.encode(keyboardCmdString, forKey: "keyboardCmdString")
+ coder.encode(cmdString, forKey: "keyboardCmdString")
coder.encode(alias, forKey: "alias")
}
}
@@ -377,21 +377,21 @@ import UIKit
let defaults = UserDefaults.standard
//if true { // save default entries if the data is empty.
if defaults.data(forKey: "savedCommands") == nil { // save default entries if the data is empty.
- let defaultCommands: [RemoteCommand] = [
- RemoteCommand(keyboardCmdString: "WIN", alias: "WIN"),
- RemoteCommand(keyboardCmdString: "F11", alias: "F11"),
- RemoteCommand(keyboardCmdString: "ESC", alias: "ESC"),
- RemoteCommand(keyboardCmdString: "CTRL+SHIFT+ESC", alias: "任务管理器(Task Manager)"),
- RemoteCommand(keyboardCmdString: "ALT+F1", alias: "N卡截图(Nvidia Screenshot)"),
- RemoteCommand(keyboardCmdString: "ALT+F9", alias: "N卡录屏(Nvidia Screen Recording)"),
- RemoteCommand(keyboardCmdString: "ALT+F4", alias: "关闭窗口(ALT+F4)"),
- RemoteCommand(keyboardCmdString: "CTRL+A", alias: "全选(Select All)"),
- RemoteCommand(keyboardCmdString: "CTRL+C", alias: "复制(Copy)"),
- RemoteCommand(keyboardCmdString: "CTRL+V", alias: "粘贴(Paste)"),
- RemoteCommand(keyboardCmdString: "WIN+D", alias: "切换桌面(Switch to Desktop)"),
- RemoteCommand(keyboardCmdString: "WIN+P", alias: "多显模式(Project)"),
- RemoteCommand(keyboardCmdString: "WIN+G", alias: "Xbox Game Bar"),
- RemoteCommand(keyboardCmdString: "SHIFT+TAB", alias: "Steam Overlay"),
+ var defaultCommands: [RemoteCommand] = [
+ RemoteCommand(cmdString: "WIN", alias: "WIN"),
+ RemoteCommand(cmdString: "F11", alias: "F11"),
+ RemoteCommand(cmdString: "ESC", alias: "ESC"),
+ RemoteCommand(cmdString: "CTRL+SHIFT+ESC", alias: "任务管理器(Task Manager)"),
+ RemoteCommand(cmdString: "ALT+F1", alias: "N卡截图(Nvidia Screenshot)"),
+ RemoteCommand(cmdString: "ALT+F9", alias: "N卡录屏(Nvidia Screen Recording)"),
+ RemoteCommand(cmdString: "ALT+F4", alias: "关闭窗口(ALT+F4)"),
+ RemoteCommand(cmdString: "CTRL+A", alias: "全选(Select All)"),
+ RemoteCommand(cmdString: "CTRL+C", alias: "复制(Copy)"),
+ RemoteCommand(cmdString: "CTRL+V", alias: "粘贴(Paste)"),
+ RemoteCommand(cmdString: "WIN+D", alias: "切换桌面(Switch to Desktop)"),
+ RemoteCommand(cmdString: "WIN+P", alias: "多显模式(Project)"),
+ RemoteCommand(cmdString: "WIN+G", alias: "Xbox Game Bar"),
+ RemoteCommand(cmdString: "SHIFT+TAB", alias: "Steam Overlay"),
]
let data = try? NSKeyedArchiver.archivedData(withRootObject: defaultCommands, requiringSecureCoding: false)
@@ -505,9 +505,9 @@ import UIKit
}
@objc public func addCommand(_ command: RemoteCommand) -> Bool {
- command.keyboardCmdString = command.keyboardCmdString.uppercased() // convert all letters to upper case
- if(command.alias.trimmingCharacters(in: .whitespacesAndNewlines).count == 0) {command.alias = command.keyboardCmdString} // copy cmd string as alias when alias is empty
- let keyStrings = extractKeyStringsFromComboCommand(from: command.keyboardCmdString)
+ command.cmdString = command.cmdString.uppercased() // convert all letters to upper case
+ if(command.alias.trimmingCharacters(in: .whitespacesAndNewlines).count == 0) {command.alias = command.cmdString} // copy cmd string as alias when alias is empty
+ let keyStrings = extractKeyStringsFromComboCommand(from: command.cmdString)
if (keyStrings == nil) {return false} // in case of non-keyboard command strings, return false
commands.append(command)
saveCommands()
diff --git a/Limelight/Input/OnScreenWidgetView.swift b/Limelight/Input/OnScreenWidgetView.swift
index 04b5aac7..b1587a40 100644
--- a/Limelight/Input/OnScreenWidgetView.swift
+++ b/Limelight/Input/OnScreenWidgetView.swift
@@ -223,14 +223,14 @@ import UIKit
// ======================================================================================================
- @objc public func setLocation(xOffset:CGFloat, yOffset:CGFloat) {
+ @objc public func setLocation(position: CGPoint) {
/*
NSLayoutConstraint.activate([
self.centerXAnchor.constraint(equalTo: self.superview!.leadingAnchor, constant: xOffset),
self.centerYAnchor.constraint(equalTo: self.superview!.topAnchor, constant: yOffset),
])
*/
- storedCenter = CGPointMake(xOffset, yOffset)
+ storedCenter = position
center = storedCenter
}
diff --git a/Limelight/Input/StreamView.m b/Limelight/Input/StreamView.m
index daf83d2a..61740cd2 100644
--- a/Limelight/Input/StreamView.m
+++ b/Limelight/Input/StreamView.m
@@ -417,7 +417,7 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
// Add the widgetView to the view controller's view
[self->streamFrameTopLayerView addSubview:widgetView]; // add keyboard button to the stream frame view. must add it to the target view before setting location.
buttonState.position = [self denormalizeWidgetPosition:buttonState.position];
- [widgetView setLocationWithXOffset:buttonState.position.x yOffset:buttonState.position.y];
+ [widgetView setLocationWithPosition:buttonState.position];
[widgetView resizeWidgetView]; // resize must be called after relocation
[widgetView adjustTransparencyWithAlpha:buttonState.backgroundAlpha];
[widgetView adjustBorderWithWidth:buttonState.borderWidth];
diff --git a/Limelight/Limelight.xcdatamodeld/Moonlight v1.10.xcdatamodel/contents b/Limelight/Limelight.xcdatamodeld/Moonlight v1.10.xcdatamodel/contents
index b16013cc..476a2df4 100644
--- a/Limelight/Limelight.xcdatamodeld/Moonlight v1.10.xcdatamodel/contents
+++ b/Limelight/Limelight.xcdatamodeld/Moonlight v1.10.xcdatamodel/contents
@@ -45,7 +45,7 @@
-
+
diff --git a/Limelight/Localization/Localizable.xcstrings b/Limelight/Localization/Localizable.xcstrings
index 89026697..538181ee 100644
--- a/Limelight/Localization/Localizable.xcstrings
+++ b/Limelight/Localization/Localizable.xcstrings
@@ -53,6 +53,29 @@
}
}
},
+ "[ Open On-Screen Widget Tool ]" : {
+ "extractionState" : "manual",
+ "localizations" : {
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "[ 打开屏显控件工具 ]"
+ }
+ },
+ "zh-Hant" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "[ 打开屏显控件工具 ]"
+ }
+ },
+ "zh-HK" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "[ 打开屏显控件工具 ]"
+ }
+ }
+ }
+ },
"%@ is currently running" : {
"extractionState" : "manual",
"localizations" : {
@@ -398,6 +421,29 @@
}
}
},
+ "Command Tool" : {
+ "extractionState" : "manual",
+ "localizations" : {
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "命令工具"
+ }
+ },
+ "zh-Hant" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "命令工具"
+ }
+ },
+ "zh-HK" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "命令工具"
+ }
+ }
+ }
+ },
"Connecting" : {
"extractionState" : "manual",
"localizations" : {
@@ -565,6 +611,29 @@
}
}
},
+ "Create New" : {
+ "extractionState" : "manual",
+ "localizations" : {
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "新增"
+ }
+ },
+ "zh-Hant" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "新增"
+ }
+ },
+ "zh-HK" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "新增"
+ }
+ }
+ }
+ },
"Current profile updated successfully" : {
"extractionState" : "manual",
"localizations" : {
@@ -749,6 +818,29 @@
}
}
},
+ "Edit Selected Widget" : {
+ "extractionState" : "manual",
+ "localizations" : {
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "编辑所选控件"
+ }
+ },
+ "zh-Hant" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "编辑所选控件"
+ }
+ },
+ "zh-HK" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "编辑所选控件"
+ }
+ }
+ }
+ },
"Enter a new command and alias" : {
"extractionState" : "manual",
"localizations" : {
@@ -1238,6 +1330,29 @@
}
}
},
+ "left" : {
+ "extractionState" : "manual",
+ "localizations" : {
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "左"
+ }
+ },
+ "zh-Hant" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "左"
+ }
+ },
+ "zh-HK" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "左"
+ }
+ }
+ }
+ },
"Local Keyboard Toggle Disabled" : {
"extractionState" : "manual",
"localizations" : {
@@ -1342,6 +1457,29 @@
}
}
},
+ "Modify" : {
+ "extractionState" : "manual",
+ "localizations" : {
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "修改"
+ }
+ },
+ "zh-Hant" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "修改"
+ }
+ },
+ "zh-HK" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "修改"
+ }
+ }
+ }
+ },
"Moonlight-iOS/tvOS only supports adding PCs on your local network" : {
"extractionState" : "manual",
"localizations" : {
@@ -1768,6 +1906,29 @@
}
}
},
+ "Open widget tool in streaming by:\nSliding from %@ screen edge to open cmd tool.\nOr tap %d fingers on stream view, number of fingers required:" : {
+ "extractionState" : "manual",
+ "localizations" : {
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "在串流视图中修改布局:\n从屏幕%@边缘向内滑动.\n或轻按%d根手指, 手势需要的手指数量:"
+ }
+ },
+ "zh-Hant" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "在串流视图中修改布局:\n从屏幕%@边缘向内滑动.\n或轻按%d根手指, 手势需要的手指数量:"
+ }
+ },
+ "zh-HK" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "在串流视图中修改布局:\n从屏幕%@边缘向内滑动.\n或轻按%d根手指, 手势需要的手指数量:"
+ }
+ }
+ }
+ },
"Pairing" : {
"extractionState" : "manual",
"localizations" : {
@@ -2113,7 +2274,7 @@
}
}
},
- "Rebase in Stream View" : {
+ "Rebase in Streaming" : {
"extractionState" : "manual",
"localizations" : {
"zh-Hans" : {
@@ -2205,6 +2366,29 @@
}
}
},
+ "right" : {
+ "extractionState" : "manual",
+ "localizations" : {
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "右"
+ }
+ },
+ "zh-Hant" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "右"
+ }
+ },
+ "zh-HK" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "右"
+ }
+ }
+ }
+ },
"Save" : {
"extractionState" : "manual",
"localizations" : {
@@ -2355,29 +2539,6 @@
}
}
},
- "Send Commands" : {
- "extractionState" : "manual",
- "localizations" : {
- "zh-Hans" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "发送命令"
- }
- },
- "zh-Hant" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "发送命令"
- }
- },
- "zh-HK" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "发送命令"
- }
- }
- }
- },
"Sensitivity: %.2f" : {
"extractionState" : "manual",
"localizations" : {
@@ -2539,29 +2700,6 @@
}
}
},
- "Tap %d fingers to change layout in stream view, or change the number of fingers required to:" : {
- "extractionState" : "manual",
- "localizations" : {
- "zh-Hans" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "在串流视图中轻按%d根手指修改布局, 或将手势需要的手指数量改为:"
- }
- },
- "zh-Hant" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "在串流视图中轻按%d根手指修改布局, 或将手势需要的手指数量改为:"
- }
- },
- "zh-HK" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "在串流视图中轻按%d根手指修改布局, 或将手势需要的手指数量改为:"
- }
- }
- }
- },
"Tap %d Fingers to Change OSC Layout in Stream View" : {
"extractionState" : "manual",
"localizations" : {
diff --git a/Limelight/ViewControllers/CommandManagerViewController.swift b/Limelight/ViewControllers/CommandManagerViewController.swift
index 01942893..66707e54 100644
--- a/Limelight/ViewControllers/CommandManagerViewController.swift
+++ b/Limelight/ViewControllers/CommandManagerViewController.swift
@@ -19,7 +19,11 @@ import UIKit
private let viewBackgroundColor = UIColor(white: 0.2, alpha: 0.8);
private let highlightColor = UIColor(white: 0.39, alpha: 0.8);
private let titleLabel = UILabel()
-
+ @objc public var specialEntries : NSMutableArray = ["widgetTool"]
+ private let specialEntryAliasDic : [String:String] = [
+ "widgetTool":SwiftLocalizationHelper.localizedString(forKey: "[ Open On-Screen Widget Tool ]")
+ ]
+
private var viewPinned: Bool = false
private var isEditingMode: Bool = false {
didSet {
@@ -42,9 +46,11 @@ import UIKit
CommandManager.shared.viewController = self
}
+
override public func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
setupConstraints()
+ reloadTableView()
}
private func setupViews() {
@@ -54,7 +60,7 @@ import UIKit
view.layer.masksToBounds = true
// Set up the title label
- titleLabel.text = SwiftLocalizationHelper.localizedString(forKey: "Send Commands")
+ titleLabel.text = SwiftLocalizationHelper.localizedString(forKey: "Command Tool")
titleLabel.font = UIFont.boldSystemFont(ofSize: 20) // Adjust font size as needed
titleLabel.textColor = UIColor.white // Adjust color as needed
titleLabel.textAlignment = .center
@@ -96,7 +102,7 @@ import UIKit
private func setupConstraints() {
- guard let superview = view.superview else {
+ guard view.superview != nil else {
return
}
@@ -187,7 +193,7 @@ import UIKit
let submitAction = UIAlertAction(title: SwiftLocalizationHelper.localizedString(forKey: "Add"), style: .default) { [unowned alert] _ in
let keyboardCmdString = alert.textFields?[0].text ?? ""
let alias = alert.textFields?[1].text ?? keyboardCmdString
- let newCommand = RemoteCommand(keyboardCmdString: keyboardCmdString, alias: alias)
+ let newCommand = RemoteCommand(cmdString: keyboardCmdString, alias: alias)
let addCommandSuceeded = CommandManager.shared.addCommand(newCommand)
//self.reloadTableView() // don't know why but this reload has to be called from the CommandManager, it doesn't work here.
@@ -207,8 +213,9 @@ import UIKit
alert.addAction(cancelAction)
if let selectedIndexPath = self.tableView.indexPathForSelectedRow {
- let selectedCommand = CommandManager.shared.getAllCommands()[selectedIndexPath.row]
- alert.textFields?[0].text = selectedCommand.keyboardCmdString // load selected keyboard cmd string
+ if isSpecialEntrySelected() {return}
+ let selectedCommand = CommandManager.shared.getAllCommands()[selectedIndexPath.row-specialEntries.count]
+ alert.textFields?[0].text = selectedCommand.cmdString // load selected keyboard cmd string
//alert.textFields?[1].text = selectedCommand.alias // leave the alias input field empty
}
@@ -220,9 +227,12 @@ import UIKit
guard let selectedIndexPath = tableView.indexPathForSelectedRow else {
return
}
+
+ if isSpecialEntrySelected() {return}
+
let previouslySelectedIndexPath = selectedIndexPath // memorize selected row before reloading tableview
- CommandManager.shared.deleteCommand(at: selectedIndexPath.row)
+ CommandManager.shared.deleteCommand(at: selectedIndexPath.row-specialEntries.count)
reloadTableView()
// target new entry after deletion
var targetRowAfterDel = previouslySelectedIndexPath.row - 1
@@ -235,6 +245,8 @@ import UIKit
@objc private func editButtonTapped() {
isEditingMode.toggle()
+ deleteButton.isEnabled = !isSpecialEntrySelected()
+ addButton.isEnabled = !isSpecialEntrySelected()
}
@objc private func exitButtonTapped() {
@@ -257,7 +269,7 @@ import UIKit
// UITableViewDataSource
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
- return CommandManager.shared.getAllCommands().count
+ return specialEntries.count + CommandManager.shared.getAllCommands().count
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
@@ -271,9 +283,14 @@ import UIKit
let selectedBackgroundView = UIView()
selectedBackgroundView.backgroundColor = highlightColor // Color for the selected state
cell.selectedBackgroundView = selectedBackgroundView
-
- let command = CommandManager.shared.getAllCommands()[indexPath.row]
- cell.textLabel?.text = command.alias
+
+ if indexPath.row < specialEntries.count {
+ cell.textLabel?.text = specialEntryAliasDic[specialEntries[indexPath.row] as! String]
+ }
+ else {
+ let command = CommandManager.shared.getAllCommands()[indexPath.row-specialEntries.count]
+ cell.textLabel?.text = command.alias
+ }
return cell
}
@@ -292,18 +309,41 @@ import UIKit
}
}
}
-
+
if !isEditingMode {
// Sending keyboard command
- let command = CommandManager.shared.getAllCommands()[indexPath.row]
- sendKeyboardCommand(command)
+ if indexPath.row < specialEntries.count{
+ handleSpecialEntries(index: indexPath.row)
+ }
+ else {
+ let command = CommandManager.shared.getAllCommands()[indexPath.row-specialEntries.count]
+ sendKeyboardCommand(command)
+ }
if !viewPinned { dismiss(animated: false, completion: nil) } // dimiss the view in sending mode & the view is not pinned
}
+ else {
+ let specialEntrySelected = indexPath.row < specialEntries.count
+ addButton.isEnabled = !specialEntrySelected
+ deleteButton.isEnabled = !specialEntrySelected
+ }
+ }
+
+ private func isSpecialEntrySelected() -> Bool {
+ return self.tableView.indexPathForSelectedRow?.row ?? specialEntries.count+1 < specialEntries.count
+ }
+
+ private func handleSpecialEntries(index:Int){
+ switch specialEntries[index] as? String {
+ case "widgetTool":
+ dismiss(animated: true, completion: nil)
+ NotificationCenter.default.post(name: Notification.Name("OscLayoutToolOpenedFromCmdToolNotification"),object: nil) // inform streamFrameViewController to open osc tool
+ default: break
+ }
}
private func sendKeyboardCommand(_ cmd: RemoteCommand) {
print("Sending key-value")
- let keyboardCmdStrings = CommandManager.shared.extractKeyStringsFromComboCommand(from: cmd.keyboardCmdString)
+ let keyboardCmdStrings = CommandManager.shared.extractKeyStringsFromComboCommand(from: cmd.cmdString)
CommandManager.shared.sendKeyComboCommand(keyboardCmdStrings: keyboardCmdStrings!)
}
}
diff --git a/Limelight/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.h b/Limelight/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.h
index b51884bd..8f157cd8 100644
--- a/Limelight/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.h
+++ b/Limelight/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.h
@@ -3,7 +3,8 @@
// Moonlight
//
// Created by Long Le on 9/27/22.
-// Copyright © 2022 Moonlight Game Streaming Project. All rights reserved.
+// Modified by True砖家 since 2024.6.24
+// Copyright © True砖家 @ Bilibili & 2022 Moonlight Game Streaming Project. All rights reserved.
//
#import
diff --git a/Limelight/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m b/Limelight/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m
index 1b916842..b40b6257 100644
--- a/Limelight/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m
+++ b/Limelight/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m
@@ -3,7 +3,8 @@
// Moonlight
//
// Created by Long Le on 9/27/22.
-// Copyright © 2022 Moonlight Game Streaming Project. All rights reserved.
+// Modified by True砖家 since 2024.6.24
+// Copyright © True砖家 @ Bilibili & 2022 Moonlight Game Streaming Project. All rights reserved.
//
#import "LayoutOnScreenControlsViewController.h"
@@ -110,7 +111,7 @@
// Add the widgetView to the view controller's view
[self.view addSubview:widgetView];
buttonState.position = [self denormalizeWidgetPosition:buttonState.position];
- [widgetView setLocationWithXOffset:buttonState.position.x yOffset:buttonState.position.y];
+ [widgetView setLocationWithPosition:buttonState.position];
[widgetView resizeWidgetView]; // resize must be called after relocation
[widgetView adjustTransparencyWithAlpha:buttonState.backgroundAlpha];
[widgetView adjustBorderWithWidth:buttonState.borderWidth];
@@ -119,7 +120,6 @@
}
}
-
- (void) viewDidLoad {
[super viewDidLoad];
profilesManager = [OSCProfilesManager sharedManager:self.view.bounds];
@@ -230,6 +230,7 @@
- (void) viewDidAppear:(BOOL)animated {
OnScreenWidgetView.editMode = true;
+ selectedWidgetView = nil;
[super viewWillAppear:animated];
[self profileRefresh];
}
@@ -368,6 +369,8 @@
- (IBAction) addTapped:(id)sender{
+ NSMutableDictionary* widgetInitParams = [NSMutableDictionary dictionary];
+
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:[LocalizationHelper localizedStringForKey:@""]
message:[LocalizationHelper localizedStringForKey:@"New On-Screen Widget"]
preferredStyle:UIAlertControllerStyleAlert];
@@ -418,54 +421,11 @@
UIAlertAction *okAction = [UIAlertAction actionWithTitle:[LocalizationHelper localizedStringForKey:@"OK"]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) {
- /*
- alertController.textFields[0].keyboardType = UIKeyboardTypeASCIICapable;
- alertController.textFields[0].autocorrectionType = UITextAutocorrectionTypeNo;
- alertController.textFields[0].spellCheckingType = UITextSpellCheckingTypeNo;
- alertController.textFields[1].keyboardType = UIKeyboardTypeASCIICapable;
- alertController.textFields[1].autocorrectionType = UITextAutocorrectionTypeNo;
- alertController.textFields[1].spellCheckingType = UITextSpellCheckingTypeNo;*/
-
- NSString *cmdString = [alertController.textFields[0].text uppercaseString]; // convert to uppercase
- NSString *buttonLabel = alertController.textFields[1].text;
- NSString *minStickOffsetString = alertController.textFields[2].text;
- NSString *widgetShape = [alertController.textFields[3].text lowercaseString];
-
-
- if([buttonLabel isEqualToString:@""]) buttonLabel = [[cmdString lowercaseString] capitalizedString];
- bool noValidKeyboardString = [CommandManager.shared extractKeyStringsFromComboCommandFrom:cmdString] == nil; // this is a invalid string.
- bool noValidSuperComboButtonString = [CommandManager.shared extractSinglCmdStringsFromComboKeysFrom:cmdString] == nil; // this is a invalid string.
- bool noValidMouseButtonString = ![CommandManager.mouseButtonMappings.allKeys containsObject:cmdString];
- bool noValidTouchPadString = ![CommandManager.touchPadCmds containsObject:cmdString];
- bool noValidOscButtonString = ![CommandManager.oscButtonMappings.allKeys containsObject:cmdString];
- bool noValidSpecialButtonString = ![CommandManager.specialOverlayButtonCmds containsObject:cmdString];
-
- bool invalidInput = noValidKeyboardString && noValidMouseButtonString && noValidTouchPadString && noValidOscButtonString && noValidSpecialButtonString && noValidSuperComboButtonString;
-
- if([widgetShape isEqualToString:@"r"]) widgetShape = @"round";
- else if([widgetShape isEqualToString:@"s"]) widgetShape = @"square";
- else if([widgetShape isEqualToString:@""]) widgetShape = @"default";
- else invalidInput = true;
-
- NSCharacterSet *nonDigitCharacterSet = [[NSCharacterSet decimalDigitCharacterSet] invertedSet];
- NSString *trimmedString = [minStickOffsetString stringByTrimmingCharactersInSet:nonDigitCharacterSet];
- if(trimmedString.length != minStickOffsetString.length) invalidInput = true;
- minStickOffsetString = trimmedString;
-
- if(invalidInput) {
- [self presentInvalidWidgetCommandAlert];
- return;
- }
-
- //saving & present the keyboard button:
- OnScreenWidgetView* widgetView = [[OnScreenWidgetView alloc] initWithCmdString:cmdString buttonLabel:buttonLabel shape:widgetShape];
- widgetView.translatesAutoresizingMaskIntoConstraints = NO; // weird but this is mandatory, or you will find no key views added to the right place
- widgetView.minStickOffset = [minStickOffsetString floatValue];
- [self.OnScreenWidgetViews addObject:widgetView];
- // Add the widgetView to the view controller's view
- [self.view addSubview:widgetView];
- [widgetView setLocationWithXOffset:50 yOffset:50];
- [widgetView resizeWidgetView];
+ [widgetInitParams setObject: alertController.textFields[0].text forKey:@"cmdString"]; // convert to uppercase
+ [widgetInitParams setObject: alertController.textFields[1].text forKey:@"buttonLabel"]; // convert to uppercase
+ [widgetInitParams setObject: alertController.textFields[2].text forKey:@"minStickOffsetString"]; // convert to uppercase
+ [widgetInitParams setObject: alertController.textFields[3].text forKey:@"shape"]; // convert to uppercase
+ [self createWidgetFromParams:widgetInitParams];
}];
[alertController addAction:readInstruction];
[alertController addAction:cancelAction];
@@ -474,6 +434,146 @@
}
+- (IBAction) editTapped:(id)sender{
+
+ NSMutableDictionary* widgetInitParams = [NSMutableDictionary dictionary];
+
+ UIAlertController *alertController = [UIAlertController alertControllerWithTitle:[LocalizationHelper localizedStringForKey:@""]
+ message:[LocalizationHelper localizedStringForKey:@"Edit Selected Widget"]
+ preferredStyle:UIAlertControllerStyleAlert];
+
+ if(self->selectedWidgetView == nil) return;
+
+ [alertController addTextFieldWithConfigurationHandler:^(UITextField *textField) {
+ textField.placeholder = [LocalizationHelper localizedStringForKey:@"Command"];
+ textField.keyboardType = UIKeyboardTypeASCIICapable;
+ textField.autocorrectionType = UITextAutocorrectionTypeNo;
+ textField.spellCheckingType = UITextSpellCheckingTypeNo;
+ textField.text = [self->selectedWidgetView.cmdString lowercaseString];
+ }];
+
+ [alertController addTextFieldWithConfigurationHandler:^(UITextField *textField) {
+ textField.placeholder = [LocalizationHelper localizedStringForKey:@"Alias label (optional)"];
+ textField.keyboardType = UIKeyboardTypeASCIICapable;
+ textField.autocorrectionType = UITextAutocorrectionTypeNo;
+ textField.spellCheckingType = UITextSpellCheckingTypeNo;
+ textField.text = self->selectedWidgetView.buttonLabel;
+ }];
+
+ [alertController addTextFieldWithConfigurationHandler:^(UITextField *textField) {
+ textField.placeholder = [LocalizationHelper localizedStringForKey:@"Minimum stick offset (0~32766)"];
+ textField.keyboardType = UIKeyboardTypeASCIICapable;
+ textField.autocorrectionType = UITextAutocorrectionTypeNo;
+ textField.spellCheckingType = UITextSpellCheckingTypeNo;
+ if(self->selectedWidgetView.minStickOffset > 0) textField.text = [NSString stringWithFormat:@"%d", (int)self->selectedWidgetView.minStickOffset];
+ }];
+
+ [alertController addTextFieldWithConfigurationHandler:^(UITextField *textField) {
+ textField.placeholder = [LocalizationHelper localizedStringForKey:@"Shape (r - round, s - square)"];
+ textField.keyboardType = UIKeyboardTypeASCIICapable;
+ textField.autocorrectionType = UITextAutocorrectionTypeNo;
+ textField.spellCheckingType = UITextSpellCheckingTypeNo;
+ textField.text = self->selectedWidgetView.shape;
+ if([self->selectedWidgetView.shape isEqualToString: @"largeSquare"]) textField.enabled = false;
+ }];
+
+
+ UIAlertAction *createNewAction = [UIAlertAction actionWithTitle:[LocalizationHelper localizedStringForKey:@"Create New"]
+ style:UIAlertActionStyleDefault
+ handler:^(UIAlertAction *action) {
+ [widgetInitParams setObject: alertController.textFields[0].text forKey:@"cmdString"];
+ [widgetInitParams setObject: alertController.textFields[1].text forKey:@"buttonLabel"];
+ [widgetInitParams setObject: alertController.textFields[2].text forKey:@"minStickOffsetString"];
+ [widgetInitParams setObject: alertController.textFields[3].text forKey:@"shape"];
+ [self updateWidget:self->selectedWidgetView byParams:widgetInitParams createNew:true];
+ }];
+
+ UIAlertAction *modifyAction = [UIAlertAction actionWithTitle:[LocalizationHelper localizedStringForKey:@"Modify"]
+ style:UIAlertActionStyleDefault
+ handler:^(UIAlertAction *action) {
+ [widgetInitParams setObject: alertController.textFields[0].text forKey:@"cmdString"];
+ [widgetInitParams setObject: alertController.textFields[1].text forKey:@"buttonLabel"];
+ [widgetInitParams setObject: alertController.textFields[2].text forKey:@"minStickOffsetString"];
+ [widgetInitParams setObject: alertController.textFields[3].text forKey:@"shape"];
+ [self updateWidget:self->selectedWidgetView byParams:widgetInitParams createNew:false];
+ }];
+
+ [alertController addAction:createNewAction];
+ [alertController addAction:modifyAction];
+ [self presentViewController:alertController animated:YES completion:nil];
+}
+
+- (bool) isWidgetParamsValid:(NSMutableDictionary* )widgetInitParams{
+ NSString *cmdString = [widgetInitParams[@"cmdString"] uppercaseString]; // convert to uppercase
+ NSString *buttonLabel = widgetInitParams[@"buttonLabel"];
+ NSString *minStickOffsetString = widgetInitParams[@"minStickOffsetString"];
+ NSString *widgetShape = [widgetInitParams[@"shape"] lowercaseString];
+
+ widgetInitParams[@"cmdString"] = cmdString;
+ bool noValidKeyboardString = [CommandManager.shared extractKeyStringsFromComboCommandFrom:cmdString] == nil; // this is a invalid string.
+ bool noValidSuperComboButtonString = [CommandManager.shared extractSinglCmdStringsFromComboKeysFrom:cmdString] == nil; // this is a invalid string.
+ bool noValidMouseButtonString = ![CommandManager.mouseButtonMappings.allKeys containsObject:cmdString];
+ bool noValidTouchPadString = ![CommandManager.touchPadCmds containsObject:cmdString];
+ bool noValidOscButtonString = ![CommandManager.oscButtonMappings.allKeys containsObject:cmdString];
+ bool noValidSpecialButtonString = ![CommandManager.specialOverlayButtonCmds containsObject:cmdString];
+ bool paramInvalid = noValidKeyboardString && noValidMouseButtonString && noValidTouchPadString && noValidOscButtonString && noValidSpecialButtonString && noValidSuperComboButtonString;
+
+ if([buttonLabel isEqualToString:@""]) widgetInitParams[@"buttonLabel"] = [[cmdString lowercaseString] capitalizedString];
+
+ NSCharacterSet *nonDigitCharacterSet = [[NSCharacterSet decimalDigitCharacterSet] invertedSet];
+ NSString *trimmedString = [minStickOffsetString stringByTrimmingCharactersInSet:nonDigitCharacterSet];
+ if(trimmedString.length != minStickOffsetString.length) paramInvalid = true;
+ widgetInitParams[@"minStickOffsetString"] = trimmedString;
+
+ NSSet* validShapes = [NSSet setWithObjects:@"round", @"square", @"largesquare", nil];
+ if([widgetShape isEqualToString:@"r"]) widgetShape = @"round";
+ else if([widgetShape isEqualToString:@"s"]) widgetShape = @"square";
+ else if([widgetShape isEqualToString:@""]) widgetShape = @"default";
+ else if(![validShapes containsObject:widgetShape]){
+ paramInvalid = true;}
+ widgetInitParams[@"shape"] = widgetShape;
+
+ if(paramInvalid) [self presentInvalidWidgetCommandAlert];
+ return !paramInvalid;
+}
+
+- (void) updateWidget:(OnScreenWidgetView* )widget byParams:(NSMutableDictionary* )widgetInitParams createNew:(bool)createNew{
+ if(![self isWidgetParamsValid:widgetInitParams]) return;
+ OnScreenWidgetView* newWidget = [[OnScreenWidgetView alloc] initWithCmdString:widgetInitParams[@"cmdString"] buttonLabel:widgetInitParams[@"buttonLabel"] shape:widgetInitParams[@"shape"]]; //reconstruct widgetView
+ newWidget.translatesAutoresizingMaskIntoConstraints = NO; // weird but this is mandatory, or you will find no key views added to the right place
+ newWidget.widthFactor = widget.widthFactor;
+ newWidget.heightFactor = widget.heightFactor;
+ newWidget.borderWidth = widget.borderWidth;
+ newWidget.sensitivityFactor = widget.sensitivityFactor;
+ newWidget.stickIndicatorOffset = widget.stickIndicatorOffset;
+ newWidget.minStickOffset = [widgetInitParams[@"minStickOffsetString"] floatValue];
+ // Add the widgetView to the view controller's view
+ [self.view addSubview:newWidget];
+ if(createNew) [newWidget setLocationWithPosition:CGPointMake(50, 50)];
+ else [newWidget setLocationWithPosition:widget.center];
+ [newWidget resizeWidgetView]; // resize must be called after relocation
+ [newWidget adjustTransparencyWithAlpha:widget.backgroundAlpha];
+ [newWidget adjustBorderWithWidth:widget.borderWidth];
+ if(!createNew) [self.OnScreenWidgetViews removeObject:widget];
+ [self.OnScreenWidgetViews addObject:newWidget];
+ if(!createNew) [widget removeFromSuperview];
+ self->selectedWidgetView = newWidget;
+}
+
+
+- (void) createWidgetFromParams: (NSMutableDictionary*) widgetInitParams{
+ if(![self isWidgetParamsValid:widgetInitParams]) return;
+ //saving & present the keyboard button:
+ OnScreenWidgetView* widgetView = [[OnScreenWidgetView alloc] initWithCmdString:widgetInitParams[@"cmdString"] buttonLabel:widgetInitParams[@"buttonLabel"] shape:widgetInitParams[@"shape"]];
+ widgetView.translatesAutoresizingMaskIntoConstraints = NO; // weird but this is mandatory, or you will find no key views added to the right place
+ widgetView.minStickOffset = [widgetInitParams[@"minStickOffsetString"] floatValue];
+ [self.OnScreenWidgetViews addObject:widgetView];
+ // Add the widgetView to the view controller's view
+ [self.view addSubview:widgetView];
+ [widgetView setLocationWithPosition:CGPointMake(50, 50)];
+ [widgetView resizeWidgetView];
+}
+
/* show pop up notification that lets users choose to save the current OSC layout configuration as a profile they can load when they want. User can also choose to cancel out of this pop up */
- (IBAction) saveTapped:(id)sender {
diff --git a/Limelight/ViewControllers/SettingsViewController.m b/Limelight/ViewControllers/SettingsViewController.m
index 2bb5c64a..3ad06bab 100644
--- a/Limelight/ViewControllers/SettingsViewController.m
+++ b/Limelight/ViewControllers/SettingsViewController.m
@@ -602,8 +602,9 @@ BOOL isCustomResolution(CGSize res) {
}
- (void)showCustomOSCTip {
- UIAlertController *alertController = [UIAlertController alertControllerWithTitle:[LocalizationHelper localizedStringForKey:@"Rebase in Stream View"]
- message:[LocalizationHelper localizedStringForKey:@"Tap %d fingers to change layout in stream view, or change the number of fingers required to:", self->oscLayoutFingers]
+ NSString* edgeSide = self.slideToSettingsScreenEdgeSelector.selectedSegmentIndex == 1 ? [LocalizationHelper localizedStringForKey:@"left"] : [LocalizationHelper localizedStringForKey:@"right"];
+ UIAlertController *alertController = [UIAlertController alertControllerWithTitle:[LocalizationHelper localizedStringForKey:@"Rebase in Streaming"]
+ message:[LocalizationHelper localizedStringForKey:@"Open widget tool in streaming by:\nSliding from %@ screen edge to open cmd tool.\nOr tap %d fingers on stream view, number of fingers required:", edgeSide, self->oscLayoutFingers]
preferredStyle:UIAlertControllerStyleAlert];
[alertController addTextFieldWithConfigurationHandler:^(UITextField *textField) {
@@ -612,7 +613,7 @@ BOOL isCustomResolution(CGSize res) {
}];
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:[LocalizationHelper localizedStringForKey:@"Cancel"]
- style:UIAlertActionStyleCancel
+ style:UIAlertActionStyleDefault
handler:nil];
UIAlertAction *okAction = [UIAlertAction actionWithTitle:[LocalizationHelper localizedStringForKey:@"OK"]
diff --git a/Limelight/ViewControllers/StreamFrameViewController.m b/Limelight/ViewControllers/StreamFrameViewController.m
index f7277533..dc532ae3 100644
--- a/Limelight/ViewControllers/StreamFrameViewController.m
+++ b/Limelight/ViewControllers/StreamFrameViewController.m
@@ -70,6 +70,7 @@
CustomEdgeSlideGestureRecognizer *_slideToCmdToolRecognizer;
CustomTapGestureRecognizer *_oscLayoutTapRecoginizer;
LayoutOnScreenControlsViewController *_layoutOnScreenControlsVC;
+ CommandManagerViewController* cmdManViewController;
#endif
}
@@ -79,6 +80,7 @@
- (void)configOscLayoutTool{
if([self isOscLayoutToolEnabled]){
+ if(![cmdManViewController.specialEntries containsObject:@"widgetTool"]) [cmdManViewController.specialEntries insertObject:@"widgetTool" atIndex:0];
_oscLayoutTapRecoginizer = [[CustomTapGestureRecognizer alloc] initWithTarget:self action:@selector(layoutOSC)];
_oscLayoutTapRecoginizer.numberOfTouchesRequired = _settings.oscLayoutToolFingers.intValue; //tap a predefined number of fingers to open osc layout tool
_oscLayoutTapRecoginizer.tapDownTimeThreshold = 0.2;
@@ -102,10 +104,10 @@
_layoutOnScreenControlsVC.view.backgroundColor = UIColor.clearColor;
_layoutOnScreenControlsVC.modalPresentationStyle = UIModalPresentationOverCurrentContext;
}
+ else [cmdManViewController.specialEntries removeObject:@"widgetTool"];
}
- (void)presentCommandManagerViewController{
- CommandManagerViewController* cmdManViewController = [[CommandManagerViewController alloc] init];
[self presentViewController:cmdManViewController animated:YES completion:nil];
}
@@ -240,7 +242,11 @@
selector:@selector(expandSettingsView) // //force expand settings view to update resolution table, and all setting includes current fullscreen resolution will be updated.
name:@"SettingsOverlayButtonPressedNotification"
object:nil];
-
+
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(layoutOSC) // //force expand settings view to update resolution table, and all setting includes current fullscreen resolution will be updated.
+ name:@"OscLayoutToolOpenedFromCmdToolNotification"
+ object:nil];
#endif
}
@@ -291,6 +297,8 @@
_streamView = [[StreamView alloc] initWithFrame:self.view.frame];
+ cmdManViewController = [[CommandManagerViewController alloc] init];
+
/*
_settings.externalDisplayMode.intValue:
0 - stage manager
diff --git a/Limelight/mul.lproj/iPad.xcstrings b/Limelight/mul.lproj/iPad.xcstrings
index d960aef9..6b5f12ee 100644
--- a/Limelight/mul.lproj/iPad.xcstrings
+++ b/Limelight/mul.lproj/iPad.xcstrings
@@ -721,18 +721,6 @@
}
}
},
- "bMH-Yb-sb6.normalTitle" : {
- "comment" : "Class = \"UIButton\"; normalTitle = \"+\"; ObjectID = \"bMH-Yb-sb6\";",
- "extractionState" : "extracted_with_value",
- "localizations" : {
- "en" : {
- "stringUnit" : {
- "state" : "new",
- "value" : "+"
- }
- }
- }
- },
"bw9-4S-s2V.segmentTitles[0]" : {
"comment" : "Class = \"UISegmentedControl\"; bw9-4S-s2V.segmentTitles[0] = \"No\"; ObjectID = \"bw9-4S-s2V\";",
"extractionState" : "stale",
@@ -1417,6 +1405,18 @@
}
}
},
+ "fKN-ja-TRi.normalTitle" : {
+ "comment" : "Class = \"UIButton\"; normalTitle = \"Add\"; ObjectID = \"fKN-ja-TRi\";",
+ "extractionState" : "extracted_with_value",
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "new",
+ "value" : "Add"
+ }
+ }
+ }
+ },
"FwK-oG-vuz.text" : {
"comment" : "Class = \"UILabel\"; text = \"Reverse Wheel Direction for Physical Mouse \"; ObjectID = \"FwK-oG-vuz\";",
"extractionState" : "extracted_with_value",
@@ -3127,6 +3127,18 @@
}
}
},
+ "R45-lM-YPl.normalTitle" : {
+ "comment" : "Class = \"UIButton\"; normalTitle = \"Edit\"; ObjectID = \"R45-lM-YPl\";",
+ "extractionState" : "extracted_with_value",
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "new",
+ "value" : "Edit"
+ }
+ }
+ }
+ },
"RRk-03-hIr.segmentTitles[0]" : {
"comment" : "Class = \"UISegmentedControl\"; RRk-03-hIr.segmentTitles[0] = \"No\"; ObjectID = \"RRk-03-hIr\";",
"extractionState" : "extracted_with_value",
diff --git a/Limelight/mul.lproj/iPhone.xcstrings b/Limelight/mul.lproj/iPhone.xcstrings
index 0aeec072..c6268720 100644
--- a/Limelight/mul.lproj/iPhone.xcstrings
+++ b/Limelight/mul.lproj/iPhone.xcstrings
@@ -301,6 +301,18 @@
}
}
},
+ "4pE-po-Zn1.normalTitle" : {
+ "comment" : "Class = \"UIButton\"; normalTitle = \"Edit\"; ObjectID = \"4pE-po-Zn1\";",
+ "extractionState" : "extracted_with_value",
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "new",
+ "value" : "Edit"
+ }
+ }
+ }
+ },
"4Qy-hB-oa5.text" : {
"comment" : "Class = \"UILabel\"; text = \"External Display Mode\"; ObjectID = \"4Qy-hB-oa5\";",
"extractionState" : "extracted_with_value",
@@ -2329,6 +2341,18 @@
}
}
},
+ "Up4-28-sgF.normalTitle" : {
+ "comment" : "Class = \"UIButton\"; normalTitle = \"Add\"; ObjectID = \"Up4-28-sgF\";",
+ "extractionState" : "extracted_with_value",
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "new",
+ "value" : "Add"
+ }
+ }
+ }
+ },
"VGn-Oc-mKc.text" : {
"comment" : "Class = \"UILabel\"; text = \"Local Mouse Pointer\"; ObjectID = \"VGn-Oc-mKc\";",
"extractionState" : "extracted_with_value",