Repo // Deprecating Sindresorhus's Preferences package.

This commit is contained in:
ShikiSuen 2023-09-24 02:42:11 +08:00
parent 109ec7382d
commit 37104cb8a9
18 changed files with 8 additions and 1355 deletions

View File

@ -1,9 +0,0 @@
MIT License
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -1,22 +0,0 @@
// swift-tools-version:5.5
import PackageDescription
let package = Package(
name: "SSPreferences",
platforms: [
.macOS(.v10_13),
],
products: [
.library(
name: "SSPreferences",
targets: [
"SSPreferences",
]
),
],
targets: [
.target(
name: "SSPreferences"
),
]
)

View File

@ -1,11 +0,0 @@
# Sindresorhus Preferences
```
// (c) 2018 and onwards Sindre Sorhus (MIT License).
// ====================
// This code is released under the MIT license (SPDX-License-Identifier: MIT)
```
See its original repository for details: https://github.com/sindresorhus/Preferences/
Note: In vChewing IME, this module is renamed to SSPreferences due to potential namespace conflicts with InputMethodKit's own method of implementing system preferences pane. Also, this module has been heavily modded for vChewing-specific necessities.

View File

@ -1,90 +0,0 @@
// (c) 2018 and onwards Sindre Sorhus (MIT License).
// ====================
// This code is released under the MIT license (SPDX-License-Identifier: MIT)
import SwiftUI
@available(macOS 10.15, *)
public extension Settings {
/**
Function builder for `Settings` components used in order to restrict types of child views to be of type `Section`.
*/
@resultBuilder
enum SectionBuilder {
public static func buildBlock(_ sections: Section...) -> [Section] {
sections
}
}
/**
A view which holds `Settings.Section` views and does all the alignment magic similar to `NSGridView` from AppKit.
*/
struct Container: View {
private let sectionBuilder: () -> [Section]
private let contentWidth: Double
private let minimumLabelWidth: Double
@State private var maximumLabelWidth = 0.0
/**
Creates an instance of container component, which handles layout of stacked `Settings.Section` views.
Custom alignment requires content width to be specified beforehand.
- Parameters:
- contentWidth: A fixed width of the container's content (excluding paddings).
- minimumLabelWidth: A minimum width for labels within this container. By default, it will fit to the largest label.
- builder: A view builder that creates `Settings.Section`'s of this container.
*/
public init(
contentWidth: Double,
minimumLabelWidth: Double = 0,
@SectionBuilder builder: @escaping () -> [Section]
) {
sectionBuilder = builder
self.contentWidth = contentWidth
self.minimumLabelWidth = minimumLabelWidth
}
public var body: some View {
let sections = sectionBuilder()
return VStack(alignment: .settingsSectionLabel) {
ForEach(0 ..< sections.count, id: \.self) { index in
let labelWidth = max(minimumLabelWidth, maximumLabelWidth)
if sections[index].label != nil {
sections[index]
.frame(width: contentWidth, alignment: .leading)
} else {
sections[index].content
.alignmentGuide(.settingsSectionLabel) { $0[.leading] + labelWidth }
.frame(width: contentWidth, alignment: .leading)
}
if sections[index].bottomDivider, index < sections.count - 1 {
Divider()
.frame(height: 10)
.alignmentGuide(.settingsSectionLabel) { $0[.leading] + labelWidth }
}
}
}
.modifier(Section.LabelWidthModifier(maximumWidth: $maximumLabelWidth))
.frame(width: contentWidth, alignment: .leading)
.padding(.vertical, 20)
.padding(.horizontal, 30)
}
}
}
/**
Extension with custom alignment guide for section title labels.
*/
@available(macOS 10.15, *)
extension HorizontalAlignment {
private enum SettingsSectionLabelAlignment: AlignmentID {
// swiftlint:disable:next no_cgfloat
static func defaultValue(in context: ViewDimensions) -> CGFloat {
context[HorizontalAlignment.leading]
}
}
static let settingsSectionLabel = HorizontalAlignment(SettingsSectionLabelAlignment.self)
}

View File

@ -1,139 +0,0 @@
// (c) 2018 and onwards Sindre Sorhus (MIT License).
// ====================
// This code is released under the MIT license (SPDX-License-Identifier: MIT)
import Foundation
struct Localization {
enum Identifier {
case preferences
case settings
}
private static let localizedStrings: [Identifier: [String: String]] = [
.preferences: [
"ar": "تفضيلات",
"ca": "Preferències",
"cs": "Předvolby",
"da": "Indstillinger",
"de": "Einstellungen",
"el": "Προτιμήσεις",
"en": "Preferences",
"en-AU": "Preferences",
"en-GB": "Preferences",
"es": "Preferencias",
"es-419": "Preferencias",
"fi": "Asetukset",
"fr": "Préférences",
"fr-CA": "Préférences",
"he": "העדפות",
"hi": "प्राथमिकता",
"hr": "Postavke",
"hu": "Beállítások",
"id": "Preferensi",
"it": "Preferenze",
"ja": "環境設定",
"ko": "환경설정",
"ms": "Keutamaan",
"nl": "Voorkeuren",
"no": "Valg",
"pl": "Preferencje",
"pt": "Preferências",
"pt-PT": "Preferências",
"ro": "Preferințe",
"ru": "Настройки",
"sk": "Nastavenia",
"sv": "Inställningar",
"th": "การตั้งค่า",
"tr": "Tercihler",
"uk": "Параметри",
"vi": "Tùy chọn",
"zh-CN": "偏好设置",
"zh-HK": "偏好設定",
"zh-TW": "偏好設定",
],
.settings: [
"ar": "الإعدادات",
"ca": "Configuració",
"cs": "Nastavení",
"da": "Indstillinger",
"de": "Einstellungen",
"el": "Ρυθμίσεις",
"en": "Settings",
"en-AU": "Settings",
"en-GB": "Settings",
"es": "Ajustes",
"es-419": "Ajustes",
"fi": "Asetukset",
"fr": "Réglages",
"fr-CA": "Réglages",
"he": "הגדרות",
"hi": "समायोजन",
"hr": "Postavke",
"hu": "Beállítások",
"id": "Pengaturan",
"it": "Impostazioni",
"ja": "設定",
"ko": "설정",
"ms": "Tetapan",
"nl": "Instellingen",
"no": "Innstillinger",
"pl": "Ustawienia",
"pt": "Ajustes",
"pt-PT": "Definições",
"ro": "Configurări",
"ru": "Настройки",
"sk": "Nastavenia",
"sv": "Inställningar",
"th": "ค่าติดตั้ง",
"tr": "Ayarlar",
"uk": "Параметри",
"vi": "Cài đặt",
"zh-CN": "设置",
"zh-HK": "設定",
"zh-TW": "設定",
],
]
/**
Returns the localized version of the given string.
- Parameter identifier: Identifier of the string to localize.
- Note: If the system's locale can't be determined, the English localization of the string will be returned.
*/
static subscript(identifier: Identifier) -> String {
// Force-unwrapped since all of the involved code is under our control.
let localizedDict = Localization.localizedStrings[identifier]!
let defaultLocalizedString = localizedDict["en"]!
// Iterate through all user-preferred languages until we find one that has a valid language code.
let preferredLocale = Locale.preferredLanguages
// TODO: Use `.firstNonNil()` here when available.
.lazy
.map { Locale(identifier: $0) }
.first { $0.languageCode != nil }
?? .current
guard let languageCode = preferredLocale.languageCode else {
return defaultLocalizedString
}
// Chinese is the only language where different region codes result in different translations.
if languageCode == "zh" {
let regionCode = preferredLocale.regionCode ?? ""
if regionCode == "HK" || regionCode == "TW" {
return localizedDict["\(languageCode)-\(regionCode)"]!
} else {
// Fall back to "regular" zh-CN if neither the HK or TW region codes are found.
return localizedDict["\(languageCode)-CN"]!
}
} else {
if let localizedString = localizedDict[languageCode] {
return localizedString
}
}
return defaultLocalizedString
}
}

View File

@ -1,99 +0,0 @@
// (c) 2018 and onwards Sindre Sorhus (MIT License).
// ====================
// This code is released under the MIT license (SPDX-License-Identifier: MIT)
import SwiftUI
/**
Represents a type that can be converted to `SettingsPane`.
Acts as type-eraser for `Settings.Pane<T>`.
*/
public protocol SettingsPaneConvertible {
/**
Convert `self` to equivalent `SettingsPane`.
*/
func asPreferencePane() -> SettingsPane
}
@available(macOS 10.15, *)
public extension Settings {
/**
Create a SwiftUI-based settings pane.
SwiftUI equivalent of the `SettingsPane` protocol.
*/
struct Pane<Content: View>: View, SettingsPaneConvertible {
let identifier: PaneIdentifier
let title: String
let toolbarIcon: NSImage
let content: Content
public init(
identifier: PaneIdentifier,
title: String,
toolbarIcon: NSImage,
contentView: () -> Content
) {
self.identifier = identifier
self.title = title
self.toolbarIcon = toolbarIcon
content = contentView()
}
public var body: some View { content }
public func asPreferencePane() -> SettingsPane {
PaneHostingController(pane: self)
}
}
/**
Hosting controller enabling `Settings.Pane` to be used alongside AppKit `NSViewController`'s.
*/
final class PaneHostingController<Content: View>: NSHostingController<Content>, SettingsPane {
public let preferencePaneIdentifier: PaneIdentifier
public let preferencePaneTitle: String
public let toolbarItemIcon: NSImage
init(
identifier: PaneIdentifier,
title: String,
toolbarIcon: NSImage,
content: Content
) {
preferencePaneIdentifier = identifier
preferencePaneTitle = title
toolbarItemIcon = toolbarIcon
super.init(rootView: content)
}
public convenience init(pane: Pane<Content>) {
self.init(
identifier: pane.identifier,
title: pane.title,
toolbarIcon: pane.toolbarIcon,
content: pane.content
)
}
@available(*, unavailable)
@objc
dynamic required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
}
@available(macOS 10.15, *)
public extension View {
/**
Applies font and color for a label used for describing a setting.
*/
func preferenceDescription(maxWidth: CGFloat? = nil) -> some View {
controlSize(.small)
.frame(maxWidth: maxWidth, alignment: .leading)
// TODO: Use `.foregroundStyle` when targeting macOS 12.
.foregroundColor(.secondary)
}
}

View File

@ -1,147 +0,0 @@
// (c) 2018 and onwards Sindre Sorhus (MIT License).
// ====================
// This code is released under the MIT license (SPDX-License-Identifier: MIT)
import SwiftUI
@available(macOS 10.15, *)
public extension Settings {
/**
Represents a section with right-aligned title and optional bottom divider.
*/
@available(macOS 10.15, *)
struct Section: View {
/**
Preference key holding max width of section labels.
*/
private struct LabelWidthPreferenceKey: PreferenceKey {
typealias Value = Double
static var defaultValue = 0.0
static func reduce(value: inout Double, nextValue: () -> Double) {
let next = nextValue()
value = next > value ? next : value
}
}
/**
Convenience overlay for finding a label's dimensions using `GeometryReader`.
*/
private struct LabelOverlay: View {
var body: some View {
GeometryReader { geometry in
Color.clear
.preference(
key: LabelWidthPreferenceKey.self,
value: geometry.size.width
)
}
}
}
/**
Convenience modifier for applying `LabelWidthPreferenceKey`.
*/
struct LabelWidthModifier: ViewModifier {
@Binding var maximumWidth: Double
func body(content: Content) -> some View {
content
.onPreferenceChange(LabelWidthPreferenceKey.self) { newMaximumWidth in
maximumWidth = newMaximumWidth
}
}
}
public private(set) var label: AnyView?
public let content: AnyView
public let bottomDivider: Bool
public let verticalAlignment: VerticalAlignment
/**
A section is responsible for controlling a single setting.
- Parameters:
- bottomDivider: Whether to place a `Divider` after the section content. Default is `false`.
- verticalAlignement: The vertical alignment of the section content.
- label: A view describing the setting handled by this section.
- content: A content view.
*/
public init(
bottomDivider: Bool = false,
verticalAlignment: VerticalAlignment = .firstTextBaseline,
label: @escaping () -> some View,
@ViewBuilder content: @escaping () -> some View
) {
self.label = label()
.overlay(LabelOverlay())
.eraseToAnyView() // TODO: Remove use of `AnyView`.
self.bottomDivider = bottomDivider
self.verticalAlignment = verticalAlignment
let stack = VStack(alignment: .leading) { content() }
self.content = stack.eraseToAnyView()
}
/**
A section is responsible for controlling a single setting without title label.
- Parameters:
- bottomDivider: Whether to place a `Divider` after the section content. Default is `false`.
- verticalAlignement: The vertical alignment of the section content.
- label: A view describing the setting handled by this section.
- content: A content view.
*/
public init(
bottomDivider: Bool = false,
verticalAlignment: VerticalAlignment = .firstTextBaseline,
@ViewBuilder content: @escaping () -> some View
) {
label = nil
self.bottomDivider = bottomDivider
self.verticalAlignment = verticalAlignment
let stack = VStack(alignment: .leading) { content() }
self.content = stack.eraseToAnyView()
}
/**
Creates instance of section, responsible for controling a single setting with `Text` as a `Label`.
- Parameters:
- title: A string describing the setting handled by this section.
- bottomDivider: Whether to place a `Divider` after the section content. Default is `false`.
- verticalAlignement: The vertical alignment of the section content.
- content: A content view.
*/
public init(
title: String,
bottomDivider: Bool = false,
verticalAlignment: VerticalAlignment = .firstTextBaseline,
@ViewBuilder content: @escaping () -> some View
) {
let textLabel = {
Text(title)
.font(.system(size: 13.0))
.overlay(LabelOverlay())
.eraseToAnyView()
}
self.init(
bottomDivider: bottomDivider,
verticalAlignment: verticalAlignment,
label: textLabel,
content: content
)
}
public var body: some View {
HStack(alignment: verticalAlignment) {
if let label = label {
label.alignmentGuide(.settingsSectionLabel) { $0[.trailing] }
}
content
Spacer()
}
}
}
}

View File

@ -1,143 +0,0 @@
// (c) 2018 and onwards Sindre Sorhus (MIT License).
// ====================
// This code is released under the MIT license (SPDX-License-Identifier: MIT)
import AppKit
extension NSToolbarItem.Identifier {
static let toolbarSegmentedControlItem = Self("toolbarSegmentedControlItem")
}
extension NSUserInterfaceItemIdentifier {
static let toolbarSegmentedControl = Self("toolbarSegmentedControl")
}
final class SegmentedControlStyleViewController: NSViewController, SettingsStyleController {
var segmentedControl: NSSegmentedControl! {
get { view as? NSSegmentedControl }
set {
view = newValue
}
}
var isKeepingWindowCentered: Bool { true }
weak var delegate: SettingsStyleControllerDelegate?
private var panes: [SettingsPane]!
required init(panes: [SettingsPane]) {
super.init(nibName: nil, bundle: nil)
self.panes = panes
}
@available(*, unavailable)
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func loadView() {
view = createSegmentedControl(panes: panes)
}
fileprivate func createSegmentedControl(panes: [SettingsPane]) -> NSSegmentedControl {
let segmentedControl = NSSegmentedControl()
segmentedControl.segmentCount = panes.count
segmentedControl.segmentStyle = .texturedSquare
segmentedControl.target = self
segmentedControl.action = #selector(segmentedControlAction)
segmentedControl.identifier = .toolbarSegmentedControl
if let cell = segmentedControl.cell as? NSSegmentedCell {
cell.controlSize = .regular
cell.trackingMode = .selectOne
}
let segmentSize: CGSize = {
let insets = CGSize(width: 36, height: 12)
var maxSize = CGSize.zero
for pane in panes {
let title = pane.preferencePaneTitle
let titleSize = title.size(
withAttributes: [
.font: NSFont.systemFont(ofSize: NSFont.systemFontSize(for: .regular)),
]
)
maxSize = CGSize(
width: max(titleSize.width, maxSize.width),
height: max(titleSize.height, maxSize.height)
)
}
return CGSize(
width: maxSize.width + insets.width,
height: maxSize.height + insets.height
)
}()
let segmentBorderWidth = Double(panes.count) + 1
let segmentWidth = segmentSize.width * Double(panes.count) + segmentBorderWidth
let segmentHeight = segmentSize.height
segmentedControl.frame = CGRect(x: 0, y: 0, width: segmentWidth, height: segmentHeight)
for (index, pane) in panes.enumerated() {
segmentedControl.setLabel(pane.preferencePaneTitle, forSegment: index)
segmentedControl.setWidth(segmentSize.width, forSegment: index)
if let cell = segmentedControl.cell as? NSSegmentedCell {
cell.setTag(index, forSegment: index)
}
}
return segmentedControl
}
@IBAction private func segmentedControlAction(_ control: NSSegmentedControl) {
delegate?.activateTab(index: control.selectedSegment, animated: true)
}
func selectTab(index: Int) {
segmentedControl.selectedSegment = index
}
func toolbarItemIdentifiers() -> [NSToolbarItem.Identifier] {
[
.flexibleSpace,
.toolbarSegmentedControlItem,
.flexibleSpace,
]
}
func toolbarItem(paneIdentifier: Settings.PaneIdentifier) -> NSToolbarItem? {
let toolbarItemIdentifier = paneIdentifier.toolbarItemIdentifier
precondition(toolbarItemIdentifier == .toolbarSegmentedControlItem)
// When the segments outgrow the window, we need to provide a group of
// NSToolbarItems with custom menu item labels and action handling for the
// context menu that pops up at the right edge of the window.
let toolbarItemGroup = NSToolbarItemGroup(itemIdentifier: toolbarItemIdentifier)
toolbarItemGroup.view = segmentedControl
toolbarItemGroup.subitems = panes.enumerated().map { index, settingsPane in
let item = NSToolbarItem(itemIdentifier: .init("segment-\(settingsPane.preferencePaneTitle)"))
item.label = settingsPane.preferencePaneTitle
let menuItem = NSMenuItem(
title: settingsPane.preferencePaneTitle,
action: #selector(segmentedControlMenuAction),
keyEquivalent: ""
)
menuItem.tag = index
menuItem.target = self
item.menuFormRepresentation = menuItem
return item
}
return toolbarItemGroup
}
@IBAction private func segmentedControlMenuAction(_ menuItem: NSMenuItem) {
delegate?.activateTab(index: menuItem.tag, animated: true)
}
}

View File

@ -1,15 +0,0 @@
// (c) 2018 and onwards Sindre Sorhus (MIT License).
// ====================
// This code is released under the MIT license (SPDX-License-Identifier: MIT)
/**
The namespace for this package.
*/
public enum Settings {}
// TODO: Remove in the next major version.
// Preserve backwards compatibility.
public typealias Preferences = Settings
public typealias PreferencePane = SettingsPane
public typealias PreferencePaneConvertible = SettingsPaneConvertible
public typealias PreferencesWindowController = SettingsWindowController

View File

@ -1,43 +0,0 @@
// (c) 2018 and onwards Sindre Sorhus (MIT License).
// ====================
// This code is released under the MIT license (SPDX-License-Identifier: MIT)
import AppKit
public extension Settings {
struct PaneIdentifier: Hashable, RawRepresentable, Codable {
public let rawValue: String
public init(rawValue: String) {
self.rawValue = rawValue
}
}
}
public protocol SettingsPane: NSViewController {
var preferencePaneIdentifier: Settings.PaneIdentifier { get }
var preferencePaneTitle: String { get }
var toolbarItemIcon: NSImage { get }
}
public extension SettingsPane {
var toolbarItemIdentifier: NSToolbarItem.Identifier {
preferencePaneIdentifier.toolbarItemIdentifier
}
var toolbarItemIcon: NSImage { .empty }
}
public extension Settings.PaneIdentifier {
init(_ rawValue: String) {
self.init(rawValue: rawValue)
}
init(fromToolbarItemIdentifier itemIdentifier: NSToolbarItem.Identifier) {
self.init(rawValue: itemIdentifier.rawValue)
}
var toolbarItemIdentifier: NSToolbarItem.Identifier {
NSToolbarItem.Identifier(rawValue)
}
}

View File

@ -1,19 +0,0 @@
// (c) 2018 and onwards Sindre Sorhus (MIT License).
// ====================
// This code is released under the MIT license (SPDX-License-Identifier: MIT)
import AppKit
protocol SettingsStyleController: AnyObject {
var delegate: SettingsStyleControllerDelegate? { get set }
var isKeepingWindowCentered: Bool { get }
func toolbarItemIdentifiers() -> [NSToolbarItem.Identifier]
func toolbarItem(paneIdentifier: Settings.PaneIdentifier) -> NSToolbarItem?
func selectTab(index: Int)
}
protocol SettingsStyleControllerDelegate: AnyObject {
func activateTab(paneIdentifier: Settings.PaneIdentifier, animated: Bool)
func activateTab(index: Int, animated: Bool)
}

View File

@ -1,242 +0,0 @@
// (c) 2018 and onwards Sindre Sorhus (MIT License).
// ====================
// This code is released under the MIT license (SPDX-License-Identifier: MIT)
import AppKit
final class SettingsTabViewController: NSViewController, SettingsStyleControllerDelegate {
private var activeTab: Int?
private var panes = [SettingsPane]()
private var style: Settings.Style?
internal var settingsPanesCount: Int { panes.count }
private var settingsStyleController: SettingsStyleController!
private var isKeepingWindowCentered: Bool { settingsStyleController.isKeepingWindowCentered }
private var toolbarItemIdentifiers: [NSToolbarItem.Identifier] {
settingsStyleController?.toolbarItemIdentifiers() ?? []
}
var window: NSWindow! { view.window }
var isAnimated = true
var activeViewController: NSViewController? {
guard let activeTab else {
return nil
}
return panes[activeTab]
}
override func loadView() {
view = NSView()
view.translatesAutoresizingMaskIntoConstraints = false
}
func configure(panes: [SettingsPane], style: Settings.Style) {
self.panes = panes
self.style = style
children = panes
let toolbar = NSToolbar(identifier: "SettingsToolbar")
toolbar.allowsUserCustomization = false
toolbar.displayMode = .iconAndLabel
toolbar.showsBaselineSeparator = true
toolbar.delegate = self
switch style {
case .segmentedControl:
settingsStyleController = SegmentedControlStyleViewController(panes: panes)
case .toolbarItems:
settingsStyleController = ToolbarItemStyleViewController(
panes: panes,
toolbar: toolbar,
centerToolbarItems: false
)
}
settingsStyleController.delegate = self
// Called last so that `settingsStyleController` can be asked for items.
window.toolbar = toolbar
}
func activateTab(paneIdentifier: Settings.PaneIdentifier, animated: Bool) {
guard let index = (panes.firstIndex { $0.preferencePaneIdentifier == paneIdentifier }) else {
return activateTab(index: 0, animated: animated)
}
activateTab(index: index, animated: animated)
}
func activateTab(index: Int, animated: Bool) {
defer {
activeTab = index
settingsStyleController.selectTab(index: index)
updateWindowTitle(tabIndex: index)
}
if activeTab == nil {
immediatelyDisplayTab(index: index)
} else {
guard index != activeTab else {
return
}
animateTabTransition(index: index, animated: animated)
}
}
func restoreInitialTab() {
if activeTab == nil {
activateTab(index: 0, animated: false)
}
}
private func updateWindowTitle(tabIndex: Int) {
window.title = {
if panes.count > 1 {
return panes[tabIndex].preferencePaneTitle
} else {
let settings: String
if #available(macOS 13, *) {
settings = Localization[.settings]
} else {
settings = Localization[.preferences]
}
let appName = Bundle.main.appName
return "\(appName) \(settings)"
}
}()
}
/**
Cached constraints that pin `childViewController` views to the content view.
*/
private var activeChildViewConstraints = [NSLayoutConstraint]()
private func immediatelyDisplayTab(index: Int) {
let toViewController = panes[index]
view.addSubview(toViewController.view)
activeChildViewConstraints = toViewController.view.constrainToSuperviewBounds()
setWindowFrame(for: toViewController, animated: false)
}
private func animateTabTransition(index: Int, animated: Bool) {
guard let activeTab else {
assertionFailure("animateTabTransition called before a tab was displayed; transition only works from one tab to another")
immediatelyDisplayTab(index: index)
return
}
let fromViewController = panes[activeTab]
let toViewController = panes[index]
// View controller animations only work on macOS 10.14 and newer.
let options: NSViewController.TransitionOptions
if #available(macOS 10.14, *) {
options = animated && isAnimated ? [.crossfade] : []
} else {
options = []
}
view.removeConstraints(activeChildViewConstraints)
transition(
from: fromViewController,
to: toViewController,
options: options
) { [weak self] in
guard let self = self else { return }
activeChildViewConstraints = toViewController.view.constrainToSuperviewBounds()
}
}
override func transition(
from fromViewController: NSViewController,
to toViewController: NSViewController,
options: NSViewController.TransitionOptions = [],
completionHandler completion: (() -> Void)? = nil
) {
let isAnimated = options
.isDisjoint(with: [
.crossfade,
.slideUp,
.slideDown,
.slideForward,
.slideBackward,
.slideLeft,
.slideRight,
])
if isAnimated {
NSAnimationContext.runAnimationGroup({ context in
context.allowsImplicitAnimation = true
context.duration = 0.25
context.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
setWindowFrame(for: toViewController, animated: true)
super.transition(
from: fromViewController,
to: toViewController,
options: options,
completionHandler: completion
)
}, completionHandler: nil)
} else {
super.transition(
from: fromViewController,
to: toViewController,
options: options,
completionHandler: completion
)
}
}
private func setWindowFrame(for viewController: NSViewController, animated: Bool = false) {
guard let window else {
preconditionFailure()
}
let contentSize = viewController.view.fittingSize
let newWindowSize = window.frameRect(forContentRect: CGRect(origin: .zero, size: contentSize)).size
var frame = window.frame
frame.origin.y += frame.height - newWindowSize.height
frame.size = newWindowSize
if isKeepingWindowCentered {
let horizontalDiff = (window.frame.width - newWindowSize.width) / 2
frame.origin.x += horizontalDiff
}
let animatableWindow = animated ? window.animator() : window
animatableWindow.setFrame(frame, display: false)
}
}
extension SettingsTabViewController: NSToolbarDelegate {
func toolbarDefaultItemIdentifiers(_: NSToolbar) -> [NSToolbarItem.Identifier] {
toolbarItemIdentifiers
}
func toolbarAllowedItemIdentifiers(_: NSToolbar) -> [NSToolbarItem.Identifier] {
toolbarItemIdentifiers
}
func toolbarSelectableItemIdentifiers(_: NSToolbar) -> [NSToolbarItem.Identifier] {
style == .segmentedControl ? [] : toolbarItemIdentifiers
}
public func toolbar(
_: NSToolbar,
itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier,
willBeInsertedIntoToolbar _: Bool
) -> NSToolbarItem? {
if itemIdentifier == .flexibleSpace {
return nil
}
return settingsStyleController.toolbarItem(paneIdentifier: .init(fromToolbarItemIdentifier: itemIdentifier))
}
}

View File

@ -1,174 +0,0 @@
// (c) 2018 and onwards Sindre Sorhus (MIT License).
// ====================
// This code is released under the MIT license (SPDX-License-Identifier: MIT)
import AppKit
extension NSWindow.FrameAutosaveName {
static let settings: NSWindow.FrameAutosaveName = "com.sindresorhus.Preferences.FrameAutosaveName"
}
public final class SettingsWindowController: NSWindowController {
private let tabViewController = SettingsTabViewController()
public var isAnimated: Bool {
get { tabViewController.isAnimated }
set {
tabViewController.isAnimated = newValue
}
}
public var hidesToolbarForSingleItem: Bool {
didSet {
updateToolbarVisibility()
}
}
private func updateToolbarVisibility() {
window?.toolbar?.isVisible = (hidesToolbarForSingleItem == false)
|| (tabViewController.settingsPanesCount > 1)
}
public init(
preferencePanes: [SettingsPane],
style: Settings.Style = .toolbarItems,
animated: Bool = true,
hidesToolbarForSingleItem: Bool = true
) {
precondition(!preferencePanes.isEmpty, "You need to set at least one view controller")
let window = UserInteractionPausableWindow(
contentRect: preferencePanes[0].view.bounds,
styleMask: [
.titled,
.closable,
],
backing: .buffered,
defer: true
)
self.hidesToolbarForSingleItem = hidesToolbarForSingleItem
super.init(window: window)
window.contentViewController = tabViewController
window.titleVisibility = {
switch style {
case .toolbarItems:
return .visible
case .segmentedControl:
return preferencePanes.count <= 1 ? .visible : .hidden
}
}()
if #available(macOS 11.0, *), style == .toolbarItems {
window.toolbarStyle = .preference
}
tabViewController.isAnimated = animated
tabViewController.configure(panes: preferencePanes, style: style)
updateToolbarVisibility()
}
@available(*, unavailable)
override public init(window _: NSWindow?) {
fatalError("init(window:) is not supported, use init(preferences:style:animated:)")
}
@available(*, unavailable)
public required init?(coder _: NSCoder) {
fatalError("init(coder:) is not supported, use init(preferences:style:animated:)")
}
/**
Show the settings window and brings it to front.
If you pass a `Settings.PaneIdentifier`, the window will activate the corresponding tab.
- Parameter preferencePane: Identifier of the settings pane to display, or `nil` to show the tab that was open when the user last closed the window.
- Note: Unless you need to open a specific pane, prefer not to pass a parameter at all or `nil`.
- See `close()` to close the window again.
- See `showWindow(_:)` to show the window without the convenience of activating the app.
*/
public func show(preferencePane paneIdentifier: Settings.PaneIdentifier? = nil) {
if let paneIdentifier {
tabViewController.activateTab(paneIdentifier: paneIdentifier, animated: false)
} else {
tabViewController.restoreInitialTab()
}
showWindow(self)
restoreWindowPosition()
#if compiler(>=5.9) && canImport(AppKit, _version: "14.0")
if #available(macOS 14.0, *) {
NSApp.activate()
} else {
NSApp.activate(ignoringOtherApps: true)
}
#else
NSApp.activate(ignoringOtherApps: true)
#endif
}
private func restoreWindowPosition() {
guard
let window,
let screenContainingWindow = window.screen
else {
return
}
window.setFrameOrigin(CGPoint(
x: screenContainingWindow.visibleFrame.midX - window.frame.width / 2,
y: screenContainingWindow.visibleFrame.midY - window.frame.height / 2
))
window.setFrameUsingName(.settings)
window.setFrameAutosaveName(.settings)
}
}
public extension SettingsWindowController {
/**
Returns the active pane if it responds to the given action.
*/
override func supplementalTarget(forAction action: Selector, sender: Any?) -> Any? {
if let target = super.supplementalTarget(forAction: action, sender: sender) {
return target
}
guard let activeViewController = tabViewController.activeViewController else {
return nil
}
if let target = NSApp.target(forAction: action, to: activeViewController, from: sender) as? NSResponder, target.responds(to: action) {
return target
}
if let target = activeViewController.supplementalTarget(forAction: action, sender: sender) as? NSResponder, target.responds(to: action) {
return target
}
return nil
}
}
@available(macOS 10.15, *)
public extension SettingsWindowController {
/**
Create a settings window from only SwiftUI-based settings panes.
*/
convenience init(
panes: [SettingsPaneConvertible],
style: Settings.Style = .toolbarItems,
animated: Bool = true,
hidesToolbarForSingleItem: Bool = true
) {
self.init(
preferencePanes: panes.map { $0.asPreferencePane() },
style: style,
animated: animated,
hidesToolbarForSingleItem: hidesToolbarForSingleItem
)
}
}

View File

@ -1,12 +0,0 @@
// (c) 2018 and onwards Sindre Sorhus (MIT License).
// ====================
// This code is released under the MIT license (SPDX-License-Identifier: MIT)
import AppKit
public extension Settings {
enum Style {
case toolbarItems
case segmentedControl
}
}

View File

@ -1,61 +0,0 @@
// (c) 2018 and onwards Sindre Sorhus (MIT License).
// ====================
// This code is released under the MIT license (SPDX-License-Identifier: MIT)
import AppKit
final class ToolbarItemStyleViewController: NSObject, SettingsStyleController {
let toolbar: NSToolbar
let centerToolbarItems: Bool
let panes: [SettingsPane]
var isKeepingWindowCentered: Bool { centerToolbarItems }
weak var delegate: SettingsStyleControllerDelegate?
init(panes: [SettingsPane], toolbar: NSToolbar, centerToolbarItems: Bool) {
self.panes = panes
self.toolbar = toolbar
self.centerToolbarItems = centerToolbarItems
}
func toolbarItemIdentifiers() -> [NSToolbarItem.Identifier] {
var toolbarItemIdentifiers = [NSToolbarItem.Identifier]()
if centerToolbarItems {
toolbarItemIdentifiers.append(.flexibleSpace)
}
for pane in panes {
toolbarItemIdentifiers.append(pane.toolbarItemIdentifier)
}
if centerToolbarItems {
toolbarItemIdentifiers.append(.flexibleSpace)
}
return toolbarItemIdentifiers
}
func toolbarItem(paneIdentifier: Settings.PaneIdentifier) -> NSToolbarItem? {
guard let pane = (panes.first { $0.preferencePaneIdentifier == paneIdentifier }) else {
preconditionFailure()
}
let toolbarItem = NSToolbarItem(itemIdentifier: paneIdentifier.toolbarItemIdentifier)
toolbarItem.label = pane.preferencePaneTitle
toolbarItem.image = pane.toolbarItemIcon
toolbarItem.target = self
toolbarItem.action = #selector(toolbarItemSelected)
return toolbarItem
}
@IBAction private func toolbarItemSelected(_ toolbarItem: NSToolbarItem) {
delegate?.activateTab(
paneIdentifier: .init(fromToolbarItemIdentifier: toolbarItem.itemIdentifier),
animated: true
)
}
func selectTab(index: Int) {
toolbar.selectedItemIdentifier = panes[index].toolbarItemIdentifier
}
}

View File

@ -1,112 +0,0 @@
// (c) 2018 and onwards Sindre Sorhus (MIT License).
// ====================
// This code is released under the MIT license (SPDX-License-Identifier: MIT)
import SwiftUI
extension NSImage {
static var empty: NSImage { NSImage(size: .zero) }
}
extension NSView {
@discardableResult
func constrainToSuperviewBounds() -> [NSLayoutConstraint] {
guard let superview else {
preconditionFailure("superview has to be set first")
}
var result = [NSLayoutConstraint]()
result.append(contentsOf: NSLayoutConstraint.constraints(withVisualFormat: "H:|-0-[subview]-0-|", options: .directionLeadingToTrailing, metrics: nil, views: ["subview": self]))
result.append(contentsOf: NSLayoutConstraint.constraints(withVisualFormat: "V:|-0-[subview]-0-|", options: .directionLeadingToTrailing, metrics: nil, views: ["subview": self]))
translatesAutoresizingMaskIntoConstraints = false
superview.addConstraints(result)
return result
}
}
extension NSEvent {
/**
Events triggered by user interaction.
*/
static let userInteractionEvents: [EventType] = [
.leftMouseDown,
.leftMouseUp,
.rightMouseDown,
.rightMouseUp,
.leftMouseDragged,
.rightMouseDragged,
.keyDown,
.keyUp,
.scrollWheel,
.tabletPoint,
.otherMouseDown,
.otherMouseUp,
.otherMouseDragged,
.gesture,
.magnify,
.swipe,
.rotate,
.beginGesture,
.endGesture,
.smartMagnify,
.pressure,
.quickLook,
.directTouch,
]
/**
Whether the event was triggered by user interaction.
*/
var isUserInteraction: Bool { Self.userInteractionEvents.contains(type) }
}
extension Bundle {
var appName: String {
string(forInfoDictionaryKey: "CFBundleDisplayName")
?? string(forInfoDictionaryKey: "CFBundleName")
?? string(forInfoDictionaryKey: "CFBundleExecutable")
?? "<Unknown App Name>"
}
private func string(forInfoDictionaryKey key: String) -> String? {
// `object(forInfoDictionaryKey:)` prefers localized info dictionary over the regular one automatically
object(forInfoDictionaryKey: key) as? String
}
}
/**
A window that allows you to disable all user interactions via `isUserInteractionEnabled`.
Used to avoid breaking animations when the user clicks too fast. Disable user interactions during animations and you're set.
*/
class UserInteractionPausableWindow: NSWindow { // swiftlint:disable:this final_class
var isUserInteractionEnabled = true
override func sendEvent(_ event: NSEvent) {
guard isUserInteractionEnabled || !event.isUserInteraction else {
return
}
super.sendEvent(event)
}
override func responds(to selector: Selector!) -> Bool {
// Deactivate toolbar interactions from the Main Menu.
if selector == #selector(NSWindow.toggleToolbarShown(_:)) {
return false
}
return super.responds(to: selector)
}
}
@available(macOS 10.15, *)
extension View {
/**
Equivalent to `.eraseToAnyPublisher()` from the Combine framework.
*/
func eraseToAnyView() -> AnyView {
AnyView(self)
}
}

View File

@ -203,7 +203,7 @@
</tr> </tr>
</table> </table>
<div>&nbsp;</div> <div>&nbsp;</div>
<div>These shortcuts are for toggling certain function modes. You can disable these shortcuts in vChewing SSPreferences.</div> <div>These shortcuts are for toggling certain function modes. You can disable these shortcuts in vChewing Preferences.</div>
<table> <table>
<tr> <tr>
<td> <td>
@ -521,7 +521,7 @@
<div>Disassembly the previous reading (i.e. the reading at the rear of the cursor) and remove its intonation. Will remove the previous reading if it is not able to be disassembled.</div> <div>Disassembly the previous reading (i.e. the reading at the rear of the cursor) and remove its intonation. Will remove the previous reading if it is not able to be disassembled.</div>
</td> </td>
<td> <td>
<div>Requiring that ICB is not empty and the IME is not in marking state. Its behavior can be specified in vChewing SSPreferences.</div> <div>Requiring that ICB is not empty and the IME is not in marking state. Its behavior can be specified in vChewing Preferences.</div>
</td> </td>
</tr> </tr>
<tr> <tr>
@ -532,7 +532,7 @@
<div>Attempt to disassembly the previous reading (i.e. the reading at the rear of the cursor), remove its intonation, and override its intonation with the intonation key pressed. Will insert standalone intonation mark if previous reading doesn't exist or is not able to be disassembled.</div> <div>Attempt to disassembly the previous reading (i.e. the reading at the rear of the cursor), remove its intonation, and override its intonation with the intonation key pressed. Will insert standalone intonation mark if previous reading doesn't exist or is not able to be disassembled.</div>
</td> </td>
<td> <td>
<div>Requiring that ICB is not empty and the IME is not in marking state. Its behavior can be specified in vChewing SSPreferences.</div> <div>Requiring that ICB is not empty and the IME is not in marking state. Its behavior can be specified in vChewing Preferences.</div>
</td> </td>
</tr> </tr>
<tr> <tr>
@ -543,7 +543,7 @@
<div>Dump the current ICB to W3C Ruby HTML format. The pronunciations are written in textbook format.</div> <div>Dump the current ICB to W3C Ruby HTML format. The pronunciations are written in textbook format.</div>
</td> </td>
<td> <td>
<div>Requiring that ICB is not empty and the IME is not in marking state. One can specify whether it dumps Hanyu-Pinyin or Phonabets in vChewing SSPreferences.</div> <div>Requiring that ICB is not empty and the IME is not in marking state. One can specify whether it dumps Hanyu-Pinyin or Phonabets in vChewing Preferences.</div>
</td> </td>
</tr> </tr>
<tr> <tr>
@ -656,7 +656,7 @@
<div>Call candidate window.</div> <div>Call candidate window.</div>
</td> </td>
<td> <td>
<div>As above but can be disabled in vChewing SSPreferences.</div> <div>As above but can be disabled in vChewing Preferences.</div>
</td> </td>
</tr> </tr>
<tr> <tr>
@ -668,7 +668,7 @@
<div>Quit the marking state if in the marking state.</div> <div>Quit the marking state if in the marking state.</div>
</td> </td>
<td> <td>
<div>Its behavior (when not in marking state) can be disabled in vChewing SSPreferences. If so, it will only clear the unfinished pronunciation in the PCB.</div> <div>Its behavior (when not in marking state) can be disabled in vChewing Preferences. If so, it will only clear the unfinished pronunciation in the PCB.</div>
</td> </td>
</tr> </tr>
<tr> <tr>
@ -680,7 +680,7 @@
<div>Toggle alphanumerical input mode.</div> <div>Toggle alphanumerical input mode.</div>
</td> </td>
<td> <td>
<div>Can be disabled in vChewing SSPreferences.</div> <div>Can be disabled in vChewing Preferences.</div>
</td> </td>
</tr> </tr>
<tr> <tr>
@ -752,7 +752,7 @@
<div>⇧␣</div> <div>⇧␣</div>
</td> </td>
<td> <td>
<div>Revolve pages or candidates clockwise, according to its settings in vChewing SSPreferences.</div> <div>Revolve pages or candidates clockwise, according to its settings in vChewing Preferences.</div>
</td> </td>
<td> <td>
<div>It revolves candidates when there is only one page.</div> <div>It revolves candidates when there is only one page.</div>

View File

@ -25,7 +25,6 @@
5B40113928D7050D00A9D4CB /* Shared in Frameworks */ = {isa = PBXBuildFile; productRef = 5B40113828D7050D00A9D4CB /* Shared */; }; 5B40113928D7050D00A9D4CB /* Shared in Frameworks */ = {isa = PBXBuildFile; productRef = 5B40113828D7050D00A9D4CB /* Shared */; };
5B40113C28D71C0100A9D4CB /* Uninstaller in Frameworks */ = {isa = PBXBuildFile; productRef = 5B40113B28D71C0100A9D4CB /* Uninstaller */; }; 5B40113C28D71C0100A9D4CB /* Uninstaller in Frameworks */ = {isa = PBXBuildFile; productRef = 5B40113B28D71C0100A9D4CB /* Uninstaller */; };
5B5A603028E81CC50001AE8D /* SwiftUIBackports in Frameworks */ = {isa = PBXBuildFile; productRef = 5B5A602F28E81CC50001AE8D /* SwiftUIBackports */; }; 5B5A603028E81CC50001AE8D /* SwiftUIBackports in Frameworks */ = {isa = PBXBuildFile; productRef = 5B5A602F28E81CC50001AE8D /* SwiftUIBackports */; };
5B5C8ED828FC0EA9002C93A5 /* SSPreferences in Frameworks */ = {isa = PBXBuildFile; productRef = 5B5C8ED728FC0EA9002C93A5 /* SSPreferences */; };
5B62A33D27AE7CC100A19448 /* CtlAboutWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A33C27AE7CC100A19448 /* CtlAboutWindow.swift */; }; 5B62A33D27AE7CC100A19448 /* CtlAboutWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A33C27AE7CC100A19448 /* CtlAboutWindow.swift */; };
5B65FB342A9518DA007EEFB0 /* MainAssembly in Frameworks */ = {isa = PBXBuildFile; productRef = 5B65FB332A9518DA007EEFB0 /* MainAssembly */; }; 5B65FB342A9518DA007EEFB0 /* MainAssembly in Frameworks */ = {isa = PBXBuildFile; productRef = 5B65FB332A9518DA007EEFB0 /* MainAssembly */; };
5B69938C293B811F0057CB8E /* VwrPrefPanePhrases.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B69938B293B811F0057CB8E /* VwrPrefPanePhrases.swift */; }; 5B69938C293B811F0057CB8E /* VwrPrefPanePhrases.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B69938B293B811F0057CB8E /* VwrPrefPanePhrases.swift */; };
@ -189,7 +188,6 @@
5B312684287800DE001AA720 /* FAQ.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = FAQ.md; sourceTree = "<group>"; }; 5B312684287800DE001AA720 /* FAQ.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = FAQ.md; sourceTree = "<group>"; };
5B40113A28D71B8700A9D4CB /* vChewing_Uninstaller */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = vChewing_Uninstaller; path = Packages/vChewing_Uninstaller; sourceTree = "<group>"; }; 5B40113A28D71B8700A9D4CB /* vChewing_Uninstaller */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = vChewing_Uninstaller; path = Packages/vChewing_Uninstaller; sourceTree = "<group>"; };
5B5A602E28E81CB00001AE8D /* ShapsBenkau_SwiftUIBackports */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = ShapsBenkau_SwiftUIBackports; path = Packages/ShapsBenkau_SwiftUIBackports; sourceTree = "<group>"; }; 5B5A602E28E81CB00001AE8D /* ShapsBenkau_SwiftUIBackports */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = ShapsBenkau_SwiftUIBackports; path = Packages/ShapsBenkau_SwiftUIBackports; sourceTree = "<group>"; };
5B5C8ED628FC0E8E002C93A5 /* Sindresorhus_SSPreferences */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = Sindresorhus_SSPreferences; path = Packages/Sindresorhus_SSPreferences; sourceTree = "<group>"; };
5B62A33C27AE7CC100A19448 /* CtlAboutWindow.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = CtlAboutWindow.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; }; 5B62A33C27AE7CC100A19448 /* CtlAboutWindow.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = CtlAboutWindow.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5B65B919284D0185007C558B /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; }; 5B65B919284D0185007C558B /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
5B65FB322A9518C9007EEFB0 /* vChewing_MainAssembly */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = vChewing_MainAssembly; path = Packages/vChewing_MainAssembly; sourceTree = "<group>"; }; 5B65FB322A9518C9007EEFB0 /* vChewing_MainAssembly */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = vChewing_MainAssembly; path = Packages/vChewing_MainAssembly; sourceTree = "<group>"; };
@ -313,7 +311,6 @@
5BC5E02128DDEFE00094E427 /* TooltipUI in Frameworks */, 5BC5E02128DDEFE00094E427 /* TooltipUI in Frameworks */,
5B765F09293A253C00122315 /* PhraseEditorUI in Frameworks */, 5B765F09293A253C00122315 /* PhraseEditorUI in Frameworks */,
5BDB7A3928D4824A001AC277 /* BookmarkManager in Frameworks */, 5BDB7A3928D4824A001AC277 /* BookmarkManager in Frameworks */,
5B5C8ED828FC0EA9002C93A5 /* SSPreferences in Frameworks */,
5B5A603028E81CC50001AE8D /* SwiftUIBackports in Frameworks */, 5B5A603028E81CC50001AE8D /* SwiftUIBackports in Frameworks */,
5B963CA328D5C23600DCEE88 /* SwiftExtension in Frameworks */, 5B963CA328D5C23600DCEE88 /* SwiftExtension in Frameworks */,
5B40113928D7050D00A9D4CB /* Shared in Frameworks */, 5B40113928D7050D00A9D4CB /* Shared in Frameworks */,
@ -522,7 +519,6 @@
5BDB7A3428D47587001AC277 /* Qwertyyb_ShiftKeyUpChecker */, 5BDB7A3428D47587001AC277 /* Qwertyyb_ShiftKeyUpChecker */,
5BDB7A3528D47587001AC277 /* RMJay_LineReader */, 5BDB7A3528D47587001AC277 /* RMJay_LineReader */,
5B5A602E28E81CB00001AE8D /* ShapsBenkau_SwiftUIBackports */, 5B5A602E28E81CB00001AE8D /* ShapsBenkau_SwiftUIBackports */,
5B5C8ED628FC0E8E002C93A5 /* Sindresorhus_SSPreferences */,
5BA8C30128DEFE4F004C5CC4 /* vChewing_CandidateWindow */, 5BA8C30128DEFE4F004C5CC4 /* vChewing_CandidateWindow */,
5B963C9B28D5BE4100DCEE88 /* vChewing_CocoaExtension */, 5B963C9B28D5BE4100DCEE88 /* vChewing_CocoaExtension */,
5BDB7A3228D47587001AC277 /* vChewing_Hotenka */, 5BDB7A3228D47587001AC277 /* vChewing_Hotenka */,
@ -715,7 +711,6 @@
5BC5E02328DE07860094E427 /* PopupCompositionBuffer */, 5BC5E02328DE07860094E427 /* PopupCompositionBuffer */,
5BA8C30228DF0360004C5CC4 /* CandidateWindow */, 5BA8C30228DF0360004C5CC4 /* CandidateWindow */,
5B5A602F28E81CC50001AE8D /* SwiftUIBackports */, 5B5A602F28E81CC50001AE8D /* SwiftUIBackports */,
5B5C8ED728FC0EA9002C93A5 /* SSPreferences */,
5B765F08293A253C00122315 /* PhraseEditorUI */, 5B765F08293A253C00122315 /* PhraseEditorUI */,
5B65FB332A9518DA007EEFB0 /* MainAssembly */, 5B65FB332A9518DA007EEFB0 /* MainAssembly */,
); );
@ -1573,10 +1568,6 @@
isa = XCSwiftPackageProductDependency; isa = XCSwiftPackageProductDependency;
productName = SwiftUIBackports; productName = SwiftUIBackports;
}; };
5B5C8ED728FC0EA9002C93A5 /* SSPreferences */ = {
isa = XCSwiftPackageProductDependency;
productName = SSPreferences;
};
5B65FB332A9518DA007EEFB0 /* MainAssembly */ = { 5B65FB332A9518DA007EEFB0 /* MainAssembly */ = {
isa = XCSwiftPackageProductDependency; isa = XCSwiftPackageProductDependency;
productName = MainAssembly; productName = MainAssembly;