ClientListMgr // Reimplement without XIB.
This commit is contained in:
parent
8679943b9d
commit
962e61f6f3
|
@ -0,0 +1,62 @@
|
|||
// (c) 2021 and onwards The vChewing Project (MIT-NTL License).
|
||||
// ====================
|
||||
// This code is released under the MIT license (SPDX-License-Identifier: MIT)
|
||||
// ... with NTL restriction stating that:
|
||||
// No trademark license is granted to use the trade names, trademarks, service
|
||||
// marks, or product names of Contributor, except as required to fulfill notice
|
||||
// requirements defined in MIT License.
|
||||
|
||||
import AppKit
|
||||
import Foundation
|
||||
|
||||
public class CtlClientListMgr: NSWindowController {
|
||||
let viewController = VwrClientListMgr()
|
||||
|
||||
public static var shared: CtlClientListMgr?
|
||||
public init() {
|
||||
super.init(
|
||||
window: .init(
|
||||
contentRect: CGRect(x: 401, y: 295, width: 770, height: 335),
|
||||
styleMask: [.titled, .closable, .miniaturizable],
|
||||
backing: .buffered,
|
||||
defer: true
|
||||
)
|
||||
)
|
||||
viewController.loadView()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
}
|
||||
|
||||
public static func show() {
|
||||
if shared == nil {
|
||||
shared = CtlClientListMgr()
|
||||
}
|
||||
guard let shared = shared, let sharedWindow = shared.window else { return }
|
||||
if !sharedWindow.isVisible {
|
||||
shared.windowDidLoad()
|
||||
}
|
||||
sharedWindow.setPosition(vertical: .center, horizontal: .right, padding: 20)
|
||||
sharedWindow.orderFrontRegardless() // 逼著視窗往最前方顯示
|
||||
sharedWindow.title = "Client Manager".localized
|
||||
sharedWindow.level = .statusBar
|
||||
if #available(macOS 10.10, *) {
|
||||
sharedWindow.titlebarAppearsTransparent = true
|
||||
}
|
||||
shared.showWindow(shared)
|
||||
NSApp.popup()
|
||||
}
|
||||
|
||||
override public func windowDidLoad() {
|
||||
super.windowDidLoad()
|
||||
let view = viewController.view
|
||||
window?.contentView = view
|
||||
if let window = window {
|
||||
var frame = window.frame
|
||||
frame.size = view.fittingSize
|
||||
window.setFrame(frame, display: true)
|
||||
}
|
||||
window?.setPosition(vertical: .center, horizontal: .right, padding: 20)
|
||||
}
|
||||
}
|
|
@ -7,47 +7,119 @@
|
|||
// requirements defined in MIT License.
|
||||
|
||||
import AppKit
|
||||
import MainAssembly
|
||||
import Foundation
|
||||
import Shared
|
||||
import UniformTypeIdentifiers
|
||||
|
||||
class CtlClientListMgr: NSWindowController, NSTableViewDelegate, NSTableViewDataSource {
|
||||
@IBOutlet var tblClients: NSTableView!
|
||||
@IBOutlet var btnRemoveClient: NSButton!
|
||||
@IBOutlet var btnAddClient: NSButton!
|
||||
@IBOutlet var lblClientMgrWindow: NSTextField!
|
||||
public class VwrClientListMgr: NSViewController {
|
||||
let windowWidth: CGFloat = 770
|
||||
let contentWidth: CGFloat = 750
|
||||
let buttonWidth: CGFloat = 150
|
||||
|
||||
public static var shared: CtlClientListMgr?
|
||||
lazy var tblClients: NSTableView = .init()
|
||||
lazy var btnAddClient = NSButton("Add Client", target: self, action: #selector(btnAddClientClicked(_:)))
|
||||
lazy var btnRemoveClient = NSButton("Remove Selected", target: self, action: #selector(btnRemoveClientClicked(_:)))
|
||||
lazy var tableColumn1Cell = NSButtonCell()
|
||||
lazy var tableColumn1 = NSTableColumn()
|
||||
lazy var tableColumn2Cell = NSTextFieldCell()
|
||||
lazy var tableColumn2 = NSTableColumn()
|
||||
|
||||
static func show() {
|
||||
if shared == nil { shared = CtlClientListMgr(windowNibName: "frmClientListMgr") }
|
||||
guard let shared = shared, let sharedWindow = shared.window else { return }
|
||||
sharedWindow.setPosition(vertical: .center, horizontal: .right, padding: 20)
|
||||
sharedWindow.orderFrontRegardless() // 逼著視窗往最前方顯示
|
||||
sharedWindow.level = .statusBar
|
||||
sharedWindow.titlebarAppearsTransparent = true
|
||||
shared.showWindow(shared)
|
||||
NSApp.popup()
|
||||
override public func loadView() {
|
||||
tblClients.reloadData()
|
||||
view = body ?? .init()
|
||||
(view as? NSStackView)?.alignment = .centerX
|
||||
view.makeSimpleConstraint(.width, relation: .equal, value: windowWidth)
|
||||
btnRemoveClient.keyEquivalent = .init(NSEvent.SpecialKey.delete.unicodeScalar)
|
||||
}
|
||||
|
||||
override func windowDidLoad() {
|
||||
super.windowDidLoad()
|
||||
window?.setPosition(vertical: .center, horizontal: .right, padding: 20)
|
||||
localize()
|
||||
var body: NSView? {
|
||||
NSStackView.build(.vertical, insets: .new(all: 14)) {
|
||||
makeScrollableTable()
|
||||
.makeSimpleConstraint(.height, relation: .equal, value: 232)
|
||||
NSStackView.build(.horizontal) {
|
||||
let descriptionWidth = contentWidth - buttonWidth - 20
|
||||
NSStackView.build(.vertical) {
|
||||
let strDescription = "Please manage the list of those clients here which are: 1) IMKTextInput-incompatible; 2) suspected from abusing the contents of the inline composition buffer. A client listed here, if checked, will use popup composition buffer with maximum 20 reading counts holdable."
|
||||
strDescription.makeNSLabel(descriptive: true, fixWidth: descriptionWidth)
|
||||
.makeSimpleConstraint(.width, relation: .greaterThanOrEqual, value: descriptionWidth)
|
||||
NSView()
|
||||
}
|
||||
NSStackView.build(.vertical) {
|
||||
btnAddClient
|
||||
.makeSimpleConstraint(.width, relation: .equal, value: buttonWidth)
|
||||
btnRemoveClient
|
||||
.makeSimpleConstraint(.width, relation: .equal, value: buttonWidth)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func makeScrollableTable() -> NSScrollView {
|
||||
let scrollContainer = NSScrollView()
|
||||
scrollContainer.scrollerStyle = .legacy
|
||||
scrollContainer.autohidesScrollers = true
|
||||
scrollContainer.documentView = tblClients
|
||||
if #available(macOS 11.0, *) {
|
||||
tblClients.style = .inset
|
||||
}
|
||||
tblClients.addTableColumn(tableColumn1)
|
||||
tblClients.addTableColumn(tableColumn2)
|
||||
tblClients.headerView = nil
|
||||
tblClients.delegate = self
|
||||
tblClients.registerForDraggedTypes([.fileURL])
|
||||
tblClients.allowsExpansionToolTips = true
|
||||
tblClients.allowsMultipleSelection = true
|
||||
tblClients.autoresizingMask = [.width, .height]
|
||||
tblClients.autosaveTableColumns = false
|
||||
tblClients.backgroundColor = NSColor.controlBackgroundColor
|
||||
tblClients.columnAutoresizingStyle = .lastColumnOnlyAutoresizingStyle
|
||||
tblClients.frame = CGRect(x: 0, y: 0, width: 728, height: 230)
|
||||
tblClients.gridColor = NSColor.clear
|
||||
tblClients.intercellSpacing = CGSize(width: 17, height: 0)
|
||||
tblClients.rowHeight = 24
|
||||
tblClients.setContentHuggingPriority(.defaultHigh, for: .vertical)
|
||||
tblClients.registerForDraggedTypes([.init(rawValue: kUTTypeFileURL as String)])
|
||||
tblClients.dataSource = self
|
||||
tblClients.action = #selector(onItemClicked(_:))
|
||||
tblClients.target = self
|
||||
tblClients.reloadData()
|
||||
if #available(macOS 11.0, *) { tblClients.style = .inset }
|
||||
|
||||
tableColumn1.identifier = NSUserInterfaceItemIdentifier("colPCBEnabled")
|
||||
tableColumn1.maxWidth = 20
|
||||
tableColumn1.minWidth = 20
|
||||
tableColumn1.resizingMask = [.autoresizingMask, .userResizingMask]
|
||||
tableColumn1.width = 20
|
||||
tableColumn1.dataCell = tableColumn1Cell
|
||||
|
||||
if #available(macOS 11.0, *) { tableColumn1Cell.controlSize = .large }
|
||||
tableColumn1Cell.font = NSFont.systemFont(ofSize: 13)
|
||||
tableColumn1Cell.setButtonType(.switch)
|
||||
tableColumn1Cell.bezelStyle = .rounded
|
||||
|
||||
tableColumn2.identifier = NSUserInterfaceItemIdentifier("colClient")
|
||||
tableColumn2.maxWidth = 1000
|
||||
tableColumn2.minWidth = 40
|
||||
tableColumn2.resizingMask = [.autoresizingMask, .userResizingMask]
|
||||
tableColumn2.width = 546
|
||||
tableColumn2.dataCell = tableColumn2Cell
|
||||
|
||||
tableColumn2Cell.backgroundColor = NSColor.controlBackgroundColor
|
||||
tableColumn2Cell.font = NSFont.systemFont(ofSize: 20)
|
||||
tableColumn2Cell.isEditable = true
|
||||
tableColumn2Cell.isSelectable = true
|
||||
tableColumn2Cell.lineBreakMode = .byTruncatingTail
|
||||
tableColumn2Cell.stringValue = "Text Cell"
|
||||
tableColumn2Cell.textColor = NSColor.controlTextColor
|
||||
tableColumn2Cell.isEditable = true
|
||||
|
||||
return scrollContainer
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UserDefaults Handlers
|
||||
// MARK: - UserDefaults Handlers.
|
||||
|
||||
extension CtlClientListMgr {
|
||||
public static var clientsList: [String] { PrefMgr.shared.clientsIMKTextInputIncapable.keys.sorted() }
|
||||
public static func removeClient(at index: Int) {
|
||||
public extension VwrClientListMgr {
|
||||
static var clientsList: [String] { PrefMgr.shared.clientsIMKTextInputIncapable.keys.sorted() }
|
||||
static func removeClient(at index: Int) {
|
||||
guard index < Self.clientsList.count else { return }
|
||||
let key = Self.clientsList[index]
|
||||
var dict = PrefMgr.shared.clientsIMKTextInputIncapable
|
||||
|
@ -56,15 +128,75 @@ extension CtlClientListMgr {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: - Implementations
|
||||
// MARK: - Common Operation Methods.
|
||||
|
||||
extension CtlClientListMgr {
|
||||
func numberOfRows(in _: NSTableView) -> Int {
|
||||
Self.clientsList.count
|
||||
extension VwrClientListMgr {
|
||||
func applyNewValue(_ newValue: String, highMitigation mitigation: Bool = true) {
|
||||
guard !newValue.isEmpty else { return }
|
||||
var dict = PrefMgr.shared.clientsIMKTextInputIncapable
|
||||
dict[newValue] = mitigation
|
||||
PrefMgr.shared.clientsIMKTextInputIncapable = dict
|
||||
tblClients.reloadData()
|
||||
btnRemoveClient.isEnabled = (0 ..< Self.clientsList.count).contains(
|
||||
tblClients.selectedRow)
|
||||
}
|
||||
|
||||
/// 檢查傳入的 NSDraggingInfo 當中的 URL 對應的物件是否是 App Bundle。
|
||||
/// - Parameters:
|
||||
/// - info: 傳入的 NSDraggingInfo 物件。
|
||||
/// - onError: 當不滿足判定條件時,執行給定的 lambda expression。
|
||||
/// - handler: 當滿足判定條件時,讓傳入的 lambda expression 處理已經整理出來的 URL 陣列。
|
||||
private func validatePasteboardForAppBundles(
|
||||
neta info: NSDraggingInfo, onError: @escaping () -> Void?, handler: (([URL]) -> Void)? = nil
|
||||
) {
|
||||
let board = info.draggingPasteboard
|
||||
let type = NSPasteboard.PasteboardType(rawValue: kUTTypeApplicationBundle as String)
|
||||
let options: [NSPasteboard.ReadingOptionKey: Any] = [
|
||||
.urlReadingFileURLsOnly: true,
|
||||
.urlReadingContentsConformToTypes: [type],
|
||||
]
|
||||
guard let urls = board.readObjects(forClasses: [NSURL.self], options: options) as? [URL], !urls.isEmpty else {
|
||||
onError()
|
||||
return
|
||||
}
|
||||
if let handler = handler {
|
||||
handler(urls)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - IBActions.
|
||||
|
||||
extension VwrClientListMgr {
|
||||
@IBAction func onItemClicked(_: Any!) {
|
||||
guard tblClients.clickedColumn == 0 else { return }
|
||||
PrefMgr.shared.clientsIMKTextInputIncapable[Self.clientsList[tblClients.clickedRow]]?.toggle()
|
||||
tblClients.reloadData()
|
||||
}
|
||||
|
||||
@IBAction func btnRemoveClientClicked(_: Any) {
|
||||
guard let minIndexSelected = tblClients.selectedRowIndexes.min() else { return }
|
||||
if minIndexSelected >= Self.clientsList.count { return }
|
||||
if minIndexSelected < 0 { return }
|
||||
var isLastRow = false
|
||||
tblClients.selectedRowIndexes.sorted().reversed().forEach { index in
|
||||
isLastRow = {
|
||||
if Self.clientsList.count < 2 { return false }
|
||||
return minIndexSelected == Self.clientsList.count - 1
|
||||
}()
|
||||
if index < Self.clientsList.count {
|
||||
Self.removeClient(at: index)
|
||||
}
|
||||
}
|
||||
if isLastRow {
|
||||
tblClients.selectRowIndexes(.init(arrayLiteral: minIndexSelected - 1), byExtendingSelection: false)
|
||||
}
|
||||
tblClients.reloadData()
|
||||
btnRemoveClient.isEnabled = (0 ..< Self.clientsList.count).contains(minIndexSelected)
|
||||
}
|
||||
|
||||
@IBAction func btnAddClientClicked(_: Any) {
|
||||
guard let window = window else { return }
|
||||
guard let window = NSApp.keyWindow else { return }
|
||||
let alert = NSAlert()
|
||||
alert.messageText = NSLocalizedString(
|
||||
"Please enter the client app bundle identifier(s) you want to register.", comment: ""
|
||||
|
@ -86,6 +218,7 @@ extension CtlClientListMgr {
|
|||
scrollview.verticalScroller?.scrollerStyle = .legacy
|
||||
scrollview.autoresizingMask = [.width, .height]
|
||||
let theTextView = NSTextView(frame: NSRect(x: 0, y: 0, width: contentSize.width, height: contentSize.height))
|
||||
scrollview.documentView = theTextView
|
||||
theTextView.minSize = NSSize(width: 0.0, height: contentSize.height)
|
||||
theTextView.maxSize = NSSize(width: maxFloat, height: maxFloat)
|
||||
theTextView.isVerticallyResizable = true
|
||||
|
@ -93,7 +226,6 @@ extension CtlClientListMgr {
|
|||
theTextView.autoresizingMask = .width
|
||||
theTextView.textContainer?.containerSize = NSSize(width: contentSize.width, height: maxFloat)
|
||||
theTextView.textContainer?.widthTracksTextView = true
|
||||
scrollview.documentView = theTextView
|
||||
theTextView.enclosingScrollView?.hasHorizontalScroller = true
|
||||
theTextView.isHorizontallyResizable = true
|
||||
theTextView.autoresizingMask = [.width, .height]
|
||||
|
@ -109,7 +241,7 @@ extension CtlClientListMgr {
|
|||
}()
|
||||
|
||||
alert.accessoryView = scrollview
|
||||
alert.beginSheetModal(for: window) { result in
|
||||
alert.beginSheetModal(at: window) { result in
|
||||
resultCheck: switch result {
|
||||
case .alertFirstButtonReturn, .alertSecondButtonReturn:
|
||||
theTextView.textContainer?.textView?.string.components(separatedBy: "\n").filter { !$0.isEmpty }.forEach {
|
||||
|
@ -126,12 +258,16 @@ extension CtlClientListMgr {
|
|||
)
|
||||
dlgOpenPath.showsResizeIndicator = true
|
||||
dlgOpenPath.allowsMultipleSelection = true
|
||||
dlgOpenPath.allowedContentTypes = [UTType.applicationBundle]
|
||||
if #available(macOS 11.0, *) {
|
||||
dlgOpenPath.allowedContentTypes = [.applicationBundle]
|
||||
} else {
|
||||
dlgOpenPath.allowedFileTypes = ["app"]
|
||||
}
|
||||
dlgOpenPath.allowsOtherFileTypes = false
|
||||
dlgOpenPath.showsHiddenFiles = true
|
||||
dlgOpenPath.canChooseFiles = true
|
||||
dlgOpenPath.canChooseDirectories = false
|
||||
dlgOpenPath.beginSheetModal(for: window) { result in
|
||||
dlgOpenPath.beginSheetModal(at: window) { result in
|
||||
switch result {
|
||||
case .OK:
|
||||
for url in dlgOpenPath.urls {
|
||||
|
@ -141,11 +277,11 @@ extension CtlClientListMgr {
|
|||
)
|
||||
let text = url.path + "\n\n" + NSLocalizedString("Please try again.", comment: "")
|
||||
guard let bundle = Bundle(url: url) else {
|
||||
self.window?.callAlert(title: title, text: text)
|
||||
NSApp.keyWindow?.callAlert(title: title, text: text)
|
||||
return
|
||||
}
|
||||
guard let identifier = bundle.bundleIdentifier else {
|
||||
self.window?.callAlert(title: title, text: text)
|
||||
NSApp.keyWindow?.callAlert(title: title, text: text)
|
||||
return
|
||||
}
|
||||
let isIdentifierAlreadyRegistered = Self.clientsList.contains(identifier)
|
||||
|
@ -171,49 +307,20 @@ extension CtlClientListMgr {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func applyNewValue(_ newValue: String, highMitigation mitigation: Bool = true) {
|
||||
guard !newValue.isEmpty else { return }
|
||||
var dict = PrefMgr.shared.clientsIMKTextInputIncapable
|
||||
dict[newValue] = mitigation
|
||||
PrefMgr.shared.clientsIMKTextInputIncapable = dict
|
||||
tblClients.reloadData()
|
||||
btnRemoveClient.isEnabled = (0 ..< Self.clientsList.count).contains(
|
||||
tblClients.selectedRow)
|
||||
// MARK: - TableView Extensions.
|
||||
|
||||
extension VwrClientListMgr: NSTableViewDelegate, NSTableViewDataSource {
|
||||
public func numberOfRows(in _: NSTableView) -> Int {
|
||||
Self.clientsList.count
|
||||
}
|
||||
|
||||
@IBAction func btnRemoveClientClicked(_: Any) {
|
||||
guard let minIndexSelected = tblClients.selectedRowIndexes.min() else { return }
|
||||
if minIndexSelected >= Self.clientsList.count { return }
|
||||
if minIndexSelected < 0 { return }
|
||||
var isLastRow = false
|
||||
tblClients.selectedRowIndexes.sorted().reversed().forEach { index in
|
||||
isLastRow = {
|
||||
if Self.clientsList.count < 2 { return false }
|
||||
return minIndexSelected == Self.clientsList.count - 1
|
||||
}()
|
||||
if index < Self.clientsList.count {
|
||||
Self.removeClient(at: index)
|
||||
}
|
||||
}
|
||||
if isLastRow {
|
||||
tblClients.selectRowIndexes(.init(arrayLiteral: minIndexSelected - 1), byExtendingSelection: false)
|
||||
}
|
||||
tblClients.reloadData()
|
||||
btnRemoveClient.isEnabled = (0 ..< Self.clientsList.count).contains(minIndexSelected)
|
||||
}
|
||||
|
||||
@objc func onItemClicked(_: Any!) {
|
||||
guard tblClients.clickedColumn == 0 else { return }
|
||||
PrefMgr.shared.clientsIMKTextInputIncapable[Self.clientsList[tblClients.clickedRow]]?.toggle()
|
||||
tblClients.reloadData()
|
||||
}
|
||||
|
||||
func tableView(_: NSTableView, shouldEdit _: NSTableColumn?, row _: Int) -> Bool {
|
||||
public func tableView(_: NSTableView, shouldEdit _: NSTableColumn?, row _: Int) -> Bool {
|
||||
false
|
||||
}
|
||||
|
||||
func tableView(_: NSTableView, objectValueFor column: NSTableColumn?, row: Int) -> Any? {
|
||||
public func tableView(_: NSTableView, objectValueFor column: NSTableColumn?, row: Int) -> Any? {
|
||||
defer {
|
||||
self.btnRemoveClient.isEnabled = (0 ..< Self.clientsList.count).contains(
|
||||
self.tblClients.selectedRow)
|
||||
|
@ -232,30 +339,7 @@ extension CtlClientListMgr {
|
|||
return Self.clientsList[row]
|
||||
}
|
||||
|
||||
/// 檢查傳入的 NSDraggingInfo 當中的 URL 對應的物件是否是 App Bundle。
|
||||
/// - Parameters:
|
||||
/// - info: 傳入的 NSDraggingInfo 物件。
|
||||
/// - onError: 當不滿足判定條件時,執行給定的 lambda expression。
|
||||
/// - handler: 當滿足判定條件時,讓傳入的 lambda expression 處理已經整理出來的 URL 陣列。
|
||||
private func validatePasteboardForAppBundles(
|
||||
neta info: NSDraggingInfo, onError: @escaping () -> Void?, handler: (([URL]) -> Void)? = nil
|
||||
) {
|
||||
let board = info.draggingPasteboard
|
||||
let type = UTType.applicationBundle
|
||||
let options: [NSPasteboard.ReadingOptionKey: Any] = [
|
||||
.urlReadingFileURLsOnly: true,
|
||||
.urlReadingContentsConformToTypes: [type],
|
||||
]
|
||||
guard let urls = board.readObjects(forClasses: [NSURL.self], options: options) as? [URL], !urls.isEmpty else {
|
||||
onError()
|
||||
return
|
||||
}
|
||||
if let handler = handler {
|
||||
handler(urls)
|
||||
}
|
||||
}
|
||||
|
||||
func tableView(
|
||||
public func tableView(
|
||||
_: NSTableView, validateDrop info: NSDraggingInfo, proposedRow _: Int,
|
||||
proposedDropOperation _: NSTableView.DropOperation
|
||||
) -> NSDragOperation {
|
||||
|
@ -266,7 +350,7 @@ extension CtlClientListMgr {
|
|||
return result
|
||||
}
|
||||
|
||||
func tableView(
|
||||
public func tableView(
|
||||
_: NSTableView, acceptDrop info: NSDraggingInfo,
|
||||
row _: Int, dropOperation _: NSTableView.DropOperation
|
||||
) -> Bool {
|
||||
|
@ -285,15 +369,11 @@ extension CtlClientListMgr {
|
|||
defer { if result { tblClients.reloadData() } }
|
||||
return result
|
||||
}
|
||||
|
||||
private func localize() {
|
||||
guard let window = window else { return }
|
||||
window.title = NSLocalizedString("Client Manager", comment: "")
|
||||
lblClientMgrWindow.stringValue = NSLocalizedString(
|
||||
"Please manage the list of those clients here which are: 1) IMKTextInput-incompatible; 2) suspected from abusing the contents of the inline composition buffer. A client listed here, if checked, will use popup composition buffer with maximum 20 reading counts holdable.",
|
||||
comment: ""
|
||||
)
|
||||
btnAddClient.title = NSLocalizedString("Add Client", comment: "")
|
||||
btnRemoveClient.title = NSLocalizedString("Remove Selected", comment: "")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Preview.
|
||||
|
||||
@available(macOS 14.0, *)
|
||||
#Preview(traits: .fixedLayout(width: 770, height: 335)) {
|
||||
VwrClientListMgr()
|
||||
}
|
|
@ -1,154 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="21507" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="21507"/>
|
||||
<capability name="System colors introduced in macOS 10.14" minToolsVersion="10.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<customObject id="-2" userLabel="File's Owner" customClass="CtlClientListMgr" customModule="vChewing" customModuleProvider="target">
|
||||
<connections>
|
||||
<outlet property="btnAddClient" destination="JcI-Ff-tDM" id="XbA-OK-zl8"/>
|
||||
<outlet property="btnRemoveClient" destination="UNR-wG-8n1" id="vkb-81-1hA"/>
|
||||
<outlet property="lblClientMgrWindow" destination="3ev-ns-FKY" id="VqL-RC-OdY"/>
|
||||
<outlet property="tblClients" destination="vIw-vc-WKJ" id="2lw-U5-JAh"/>
|
||||
<outlet property="window" destination="F0z-JX-Cv5" id="gIp-Ho-8D9"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||
<window title="Client Manager" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" animationBehavior="default" titlebarAppearsTransparent="YES" id="F0z-JX-Cv5">
|
||||
<windowStyleMask key="styleMask" titled="YES" closable="YES" fullSizeContentView="YES"/>
|
||||
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
||||
<rect key="contentRect" x="196" y="240" width="770" height="335"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="1920" height="1055"/>
|
||||
<view key="contentView" id="se5-gp-TjO" customClass="NSVisualEffectView" customModule="vChewing" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="770" height="335"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="3ev-ns-FKY" userLabel="lblClientMgrWindow">
|
||||
<rect key="frame" x="18" y="20" width="600" height="46"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="46" id="HDu-vv-Kcv"/>
|
||||
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="490" id="LhL-fH-PVL"/>
|
||||
</constraints>
|
||||
<textFieldCell key="cell" title="Placeholder for description texts." id="hvd-Wx-n7n">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<scrollView autohidesScrollers="YES" horizontalLineScroll="24" horizontalPageScroll="10" verticalLineScroll="24" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="MUP-Im-g6c">
|
||||
<rect key="frame" x="20" y="76" width="730" height="232"/>
|
||||
<clipView key="contentView" drawsBackground="NO" id="wKQ-IV-k62">
|
||||
<rect key="frame" x="1" y="1" width="728" height="230"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" tableStyle="inset" multipleSelection="NO" autosaveColumns="NO" rowHeight="24" id="vIw-vc-WKJ" userLabel="tblClients">
|
||||
<rect key="frame" x="0.0" y="0.0" width="728" height="230"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<size key="intercellSpacing" width="17" height="0.0"/>
|
||||
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="gridColor" name="separatorColor" catalog="System" colorSpace="catalog"/>
|
||||
<tableColumns>
|
||||
<tableColumn identifier="colPCBEnabled" width="20" minWidth="20" maxWidth="20" id="6D6-O8-Zrc">
|
||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border">
|
||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||
</tableHeaderCell>
|
||||
<buttonCell key="dataCell" type="check" bezelStyle="regularSquare" imagePosition="left" controlSize="large" inset="2" id="5Bp-jJ-Sf1">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||
</tableColumn>
|
||||
<tableColumn identifier="colClient" width="546" minWidth="40" maxWidth="1000" id="kWw-Lj-EFx">
|
||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border">
|
||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||
</tableHeaderCell>
|
||||
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="evF-0c-DTr">
|
||||
<font key="font" metaFont="system" size="20"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||
</tableColumn>
|
||||
</tableColumns>
|
||||
</tableView>
|
||||
</subviews>
|
||||
<nil key="backgroundColor"/>
|
||||
</clipView>
|
||||
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="6Wq-mB-m8K">
|
||||
<rect key="frame" x="1" y="215" width="622" height="16"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</scroller>
|
||||
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="rYb-HR-4th">
|
||||
<rect key="frame" x="224" y="17" width="15" height="102"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</scroller>
|
||||
</scrollView>
|
||||
<customView translatesAutoresizingMaskIntoConstraints="NO" id="qCN-8Z-3x3">
|
||||
<rect key="frame" x="624" y="20" width="126" height="46"/>
|
||||
<subviews>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="JcI-Ff-tDM" userLabel="btnAddClient">
|
||||
<rect key="frame" x="-7" y="19" width="140" height="32"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="126" id="YS5-dM-Awj"/>
|
||||
</constraints>
|
||||
<buttonCell key="cell" type="push" title="Add" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="7rO-JZ-kgV">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="btnAddClientClicked:" target="-2" id="klb-NT-gqr"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="UNR-wG-8n1" userLabel="btnRemoveClient">
|
||||
<rect key="frame" x="-7" y="-7" width="140" height="32"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="126" id="t4F-yV-r4E"/>
|
||||
</constraints>
|
||||
<buttonCell key="cell" type="push" title="Remove" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="wNc-xy-U4i">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
<string key="keyEquivalent" base64-UTF8="YES">
|
||||
CA
|
||||
</string>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="btnRemoveClientClicked:" target="-2" id="VbY-dw-fRP"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="JcI-Ff-tDM" firstAttribute="trailing" secondItem="UNR-wG-8n1" secondAttribute="trailing" id="3Fe-KL-u9x"/>
|
||||
<constraint firstAttribute="bottom" secondItem="UNR-wG-8n1" secondAttribute="bottom" id="5MS-Xg-KDM"/>
|
||||
<constraint firstItem="JcI-Ff-tDM" firstAttribute="top" secondItem="qCN-8Z-3x3" secondAttribute="top" id="DZc-Jc-kVr"/>
|
||||
<constraint firstAttribute="trailing" secondItem="JcI-Ff-tDM" secondAttribute="trailing" id="foA-xu-NF8"/>
|
||||
<constraint firstItem="JcI-Ff-tDM" firstAttribute="leading" secondItem="UNR-wG-8n1" secondAttribute="leading" id="hOp-FU-UKN"/>
|
||||
<constraint firstItem="JcI-Ff-tDM" firstAttribute="leading" secondItem="qCN-8Z-3x3" secondAttribute="leading" id="r1x-vo-9qt"/>
|
||||
</constraints>
|
||||
</customView>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="3ev-ns-FKY" firstAttribute="top" secondItem="MUP-Im-g6c" secondAttribute="bottom" constant="10" id="2tx-uw-Zdh"/>
|
||||
<constraint firstAttribute="bottom" secondItem="3ev-ns-FKY" secondAttribute="bottom" constant="20" symbolic="YES" id="9KE-bP-0Yi"/>
|
||||
<constraint firstItem="MUP-Im-g6c" firstAttribute="top" secondItem="se5-gp-TjO" secondAttribute="top" constant="27" id="BkF-ED-Uva"/>
|
||||
<constraint firstAttribute="trailing" secondItem="MUP-Im-g6c" secondAttribute="trailing" constant="20" symbolic="YES" id="E0U-Vh-g3t"/>
|
||||
<constraint firstItem="MUP-Im-g6c" firstAttribute="trailing" secondItem="qCN-8Z-3x3" secondAttribute="trailing" id="TqZ-q6-quu"/>
|
||||
<constraint firstItem="MUP-Im-g6c" firstAttribute="leading" secondItem="se5-gp-TjO" secondAttribute="leading" constant="20" symbolic="YES" id="WXt-cJ-HaW"/>
|
||||
<constraint firstItem="3ev-ns-FKY" firstAttribute="top" secondItem="qCN-8Z-3x3" secondAttribute="top" id="b4O-RW-K56"/>
|
||||
<constraint firstItem="MUP-Im-g6c" firstAttribute="leading" secondItem="3ev-ns-FKY" secondAttribute="leading" id="g8i-I5-2Dw"/>
|
||||
<constraint firstItem="3ev-ns-FKY" firstAttribute="bottom" secondItem="qCN-8Z-3x3" secondAttribute="bottom" id="ktC-2I-aWz"/>
|
||||
<constraint firstItem="qCN-8Z-3x3" firstAttribute="leading" secondItem="3ev-ns-FKY" secondAttribute="trailing" constant="8" symbolic="YES" id="opE-OC-8Dh"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="delegate" destination="-2" id="0bl-1N-AYu"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="284" y="176.5"/>
|
||||
</window>
|
||||
</objects>
|
||||
</document>
|
|
@ -8,8 +8,6 @@
|
|||
|
||||
/* Begin PBXBuildFile section */
|
||||
5B09307628B6FC3B0021F8C5 /* shortcuts.html in Resources */ = {isa = PBXBuildFile; fileRef = 5B09307828B6FC3B0021F8C5 /* shortcuts.html */; };
|
||||
5B0EF55D28CDBF7100F8F7CE /* frmClientListMgr.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5B0EF55C28CDBF7100F8F7CE /* frmClientListMgr.xib */; };
|
||||
5B0EF55F28CDBF8E00F8F7CE /* CtlClientListMgr.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B0EF55E28CDBF8E00F8F7CE /* CtlClientListMgr.swift */; };
|
||||
5B2CA1E62B130869002634EE /* vChewingFactoryDatabase.sqlite in Resources */ = {isa = PBXBuildFile; fileRef = 5B2CA1E52B130869002634EE /* vChewingFactoryDatabase.sqlite */; };
|
||||
5B30BF282944867800BD87A9 /* CtlRevLookupWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B30BF272944867800BD87A9 /* CtlRevLookupWindow.swift */; };
|
||||
5B40113928D7050D00A9D4CB /* Shared in Frameworks */ = {isa = PBXBuildFile; productRef = 5B40113828D7050D00A9D4CB /* Shared */; };
|
||||
|
@ -111,8 +109,6 @@
|
|||
5B09307A28B6FC400021F8C5 /* en */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; lineEnding = 0; name = en; path = en.lproj/shortcuts.html; sourceTree = "<group>"; };
|
||||
5B09307B28B6FC410021F8C5 /* ja */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; lineEnding = 0; name = ja; path = ja.lproj/shortcuts.html; sourceTree = "<group>"; };
|
||||
5B0C5EDF27C7D9870078037C /* dataCompiler.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = dataCompiler.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
|
||||
5B0EF55C28CDBF7100F8F7CE /* frmClientListMgr.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = frmClientListMgr.xib; sourceTree = "<group>"; };
|
||||
5B0EF55E28CDBF8E00F8F7CE /* CtlClientListMgr.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CtlClientListMgr.swift; sourceTree = "<group>"; };
|
||||
5B18BA6F27C7BD8B0056EB19 /* LICENSE-CHS.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = "LICENSE-CHS.txt"; sourceTree = "<group>"; };
|
||||
5B18BA7027C7BD8B0056EB19 /* Makefile */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = "<group>"; };
|
||||
5B18BA7127C7BD8B0056EB19 /* README-CHS.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = "README-CHS.md"; sourceTree = "<group>"; };
|
||||
|
@ -293,7 +289,6 @@
|
|||
5B62A33A27AE7C7500A19448 /* WindowControllers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5B0EF55E28CDBF8E00F8F7CE /* CtlClientListMgr.swift */,
|
||||
5B30BF272944867800BD87A9 /* CtlRevLookupWindow.swift */,
|
||||
);
|
||||
path = WindowControllers;
|
||||
|
@ -302,7 +297,6 @@
|
|||
5B62A33B27AE7C7F00A19448 /* WindowNIBs */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5B0EF55C28CDBF7100F8F7CE /* frmClientListMgr.xib */,
|
||||
6A187E2816004C5900466B2E /* MainMenu.xib */,
|
||||
);
|
||||
path = WindowNIBs;
|
||||
|
@ -597,7 +591,6 @@
|
|||
5B70F4EC2A0BE900005EA8C4 /* MenuIcon-TCVIM@2x.png in Resources */,
|
||||
5BF9DA2828840E6200DBD48E /* template-exclusions.txt in Resources */,
|
||||
5B7DA80328BF6BC600D7B2AD /* fixinstall.sh in Resources */,
|
||||
5B0EF55D28CDBF7100F8F7CE /* frmClientListMgr.xib in Resources */,
|
||||
5B70F4E92A0BE900005EA8C4 /* MenuIcon-TCVIM.png in Resources */,
|
||||
5BBBB76027AED54C0023B93A /* Fart.m4a in Resources */,
|
||||
6A2E40F6253A69DA00D1AE1D /* Images.xcassets in Resources */,
|
||||
|
@ -727,7 +720,6 @@
|
|||
files = (
|
||||
D427F76C278CA2B0004A2160 /* AppDelegateImpl.swift in Sources */,
|
||||
D47B92C027972AD100458394 /* main.swift in Sources */,
|
||||
5B0EF55F28CDBF8E00F8F7CE /* CtlClientListMgr.swift in Sources */,
|
||||
5B30BF282944867800BD87A9 /* CtlRevLookupWindow.swift in Sources */,
|
||||
5BB802DA27FABA8300CF1C19 /* SessionCtl_Menu.swift in Sources */,
|
||||
);
|
||||
|
|
Loading…
Reference in New Issue