mirror of
https://github.com/qvacua/vimr.git
synced 2024-12-25 23:02:35 +03:00
GH-296 Refactor pref data
This commit is contained in:
parent
f9a90ba355
commit
d372ca9682
@ -72,6 +72,7 @@
|
||||
4B6A709C1D6507A000E12030 /* Nimble.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 4B56F29B1D29926600C1F92E /* Nimble.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
4B6B0A781DA2A1A500212D6D /* FileOutlineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B6B0A771DA2A1A500212D6D /* FileOutlineView.swift */; };
|
||||
4B705BA11DDF7639005F844B /* ToolPrefData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B705BA01DDF7639005F844B /* ToolPrefData.swift */; };
|
||||
4B705BDA1DE04F83005F844B /* Pref38To128Converter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B705BD91DE04F83005F844B /* Pref38To128Converter.swift */; };
|
||||
4B854A1D1D31447C00E08DE1 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B854A1C1D31447C00E08DE1 /* main.m */; };
|
||||
4B8AC0441DBCB3A2007CCC9B /* NeoVimObjectsExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B8AC0431DBCB3A1007CCC9B /* NeoVimObjectsExtensions.swift */; };
|
||||
4B97E2CC1D33F53D00FC0660 /* MainWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4B97E2CE1D33F53D00FC0660 /* MainWindow.xib */; };
|
||||
@ -296,6 +297,7 @@
|
||||
4B6A70951D6100E300E12030 /* SwiftCommons.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftCommons.swift; sourceTree = "<group>"; };
|
||||
4B6B0A771DA2A1A500212D6D /* FileOutlineView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileOutlineView.swift; sourceTree = "<group>"; };
|
||||
4B705BA01DDF7639005F844B /* ToolPrefData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ToolPrefData.swift; sourceTree = "<group>"; };
|
||||
4B705BD91DE04F83005F844B /* Pref38To128Converter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Pref38To128Converter.swift; sourceTree = "<group>"; };
|
||||
4B854A1A1D31447C00E08DE1 /* NeoVimServer */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = NeoVimServer; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
4B854A1C1D31447C00E08DE1 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
|
||||
4B8AC0431DBCB3A1007CCC9B /* NeoVimObjectsExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NeoVimObjectsExtensions.swift; sourceTree = "<group>"; };
|
||||
@ -470,6 +472,7 @@
|
||||
4B238BEB1D3ED54D00CBDD98 /* AppearancePrefPane.swift */,
|
||||
4BBDD3EC1D967DA2008FCC92 /* AdvancedPrefPane.swift */,
|
||||
4B37ADBA1D6EC11600970D55 /* TestPane.swift */,
|
||||
4B705BD91DE04F83005F844B /* Pref38To128Converter.swift */,
|
||||
);
|
||||
name = Preferences;
|
||||
sourceTree = "<group>";
|
||||
@ -1070,6 +1073,7 @@
|
||||
4BAD81D51D80B5D8004F91AE /* OpenQuicklyFileViewRow.swift in Sources */,
|
||||
4B4546871D468CEC00A1E27F /* PrefStore.swift in Sources */,
|
||||
4B4FC8E61D3E8739009352C3 /* PrefWindowComponent.swift in Sources */,
|
||||
4B705BDA1DE04F83005F844B /* Pref38To128Converter.swift in Sources */,
|
||||
4B6A70961D6100E300E12030 /* SwiftCommons.swift in Sources */,
|
||||
4B56CE301D5FC27100337673 /* GeneralPrefPane.swift in Sources */,
|
||||
4BF8EED81D85574800CAC08A /* PrefUtils.swift in Sources */,
|
||||
|
@ -7,16 +7,44 @@ import Cocoa
|
||||
import PureLayout
|
||||
import RxSwift
|
||||
|
||||
struct AdvancedPrefData: Equatable {
|
||||
let useSnapshotUpdateChannel: Bool
|
||||
let useInteractiveZsh: Bool
|
||||
}
|
||||
struct AdvancedPrefData: Equatable, StandardPrefData {
|
||||
|
||||
func == (left: AdvancedPrefData, right: AdvancedPrefData) -> Bool {
|
||||
fileprivate static let useSnapshotUpdateChannel = "use-snapshot-update-channel"
|
||||
fileprivate static let useInteractiveZsh = "use-interactive-zsh"
|
||||
|
||||
static func ==(left: AdvancedPrefData, right: AdvancedPrefData) -> Bool {
|
||||
return left.useSnapshotUpdateChannel == right.useSnapshotUpdateChannel &&
|
||||
left.useInteractiveZsh == right.useInteractiveZsh
|
||||
}
|
||||
|
||||
static let `default` = AdvancedPrefData(useSnapshotUpdateChannel: false, useInteractiveZsh: false)
|
||||
|
||||
let useSnapshotUpdateChannel: Bool
|
||||
let useInteractiveZsh: Bool
|
||||
|
||||
init(useSnapshotUpdateChannel: Bool, useInteractiveZsh: Bool) {
|
||||
self.useSnapshotUpdateChannel = useSnapshotUpdateChannel
|
||||
self.useInteractiveZsh = useInteractiveZsh
|
||||
}
|
||||
|
||||
init?(dict: [String: Any]) {
|
||||
guard let useSnapshot = PrefUtils.bool(from: dict, for: AdvancedPrefData.useSnapshotUpdateChannel),
|
||||
let useInteractiveZsh = PrefUtils.bool(from: dict, for: AdvancedPrefData.useInteractiveZsh)
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
|
||||
self.init(useSnapshotUpdateChannel: useSnapshot, useInteractiveZsh: useInteractiveZsh)
|
||||
}
|
||||
|
||||
func dict() -> [String: Any] {
|
||||
return [
|
||||
AdvancedPrefData.useSnapshotUpdateChannel: self.useSnapshotUpdateChannel,
|
||||
AdvancedPrefData.useInteractiveZsh: self.useInteractiveZsh,
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
class AdvancedPrefPane: PrefPane {
|
||||
|
||||
override var displayName: String {
|
||||
|
@ -7,18 +7,57 @@ import Cocoa
|
||||
import PureLayout
|
||||
import RxSwift
|
||||
|
||||
struct AppearancePrefData: Equatable {
|
||||
let editorFont: NSFont
|
||||
let editorLinespacing: CGFloat
|
||||
let editorUsesLigatures: Bool
|
||||
}
|
||||
struct AppearancePrefData: Equatable, StandardPrefData {
|
||||
|
||||
func == (left: AppearancePrefData, right: AppearancePrefData) -> Bool {
|
||||
fileprivate static let editorFontName = "editor-font-name"
|
||||
fileprivate static let editorFontSize = "editor-font-size"
|
||||
fileprivate static let editorLinespacing = "editor-linespacing"
|
||||
fileprivate static let editorUsesLigatures = "editor-uses-ligatures"
|
||||
|
||||
static func ==(left: AppearancePrefData, right: AppearancePrefData) -> Bool {
|
||||
return left.editorUsesLigatures == right.editorUsesLigatures
|
||||
&& left.editorFont.isEqual(to: right.editorFont)
|
||||
&& left.editorLinespacing == right.editorLinespacing
|
||||
}
|
||||
|
||||
static let `default` = AppearancePrefData(editorFont: NeoVimView.defaultFont,
|
||||
editorLinespacing: NeoVimView.defaultLinespacing,
|
||||
editorUsesLigatures: false)
|
||||
|
||||
var editorFont: NSFont
|
||||
var editorLinespacing: CGFloat
|
||||
var editorUsesLigatures: Bool
|
||||
|
||||
init(editorFont: NSFont, editorLinespacing: CGFloat, editorUsesLigatures: Bool) {
|
||||
self.editorFont = editorFont
|
||||
self.editorLinespacing = editorLinespacing
|
||||
self.editorUsesLigatures = editorUsesLigatures
|
||||
}
|
||||
|
||||
init?(dict: [String: Any]) {
|
||||
guard let editorFontName = dict[AppearancePrefData.editorFontName] as? String,
|
||||
let fEditorFontSize = PrefUtils.float(from: dict, for: AppearancePrefData.editorFontSize),
|
||||
let fEditorLinespacing = PrefUtils.float(from: dict, for: AppearancePrefData.editorLinespacing),
|
||||
let editorUsesLigatures = PrefUtils.bool(from: dict, for: AppearancePrefData.editorUsesLigatures)
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
|
||||
self.init(editorFont: PrefUtils.saneFont(editorFontName, fontSize: CGFloat(fEditorFontSize)),
|
||||
editorLinespacing: CGFloat(fEditorLinespacing),
|
||||
editorUsesLigatures: editorUsesLigatures)
|
||||
}
|
||||
|
||||
func dict() -> [String: Any] {
|
||||
return [
|
||||
AppearancePrefData.editorFontName: self.editorFont.fontName,
|
||||
AppearancePrefData.editorFontSize: Float(self.editorFont.pointSize),
|
||||
AppearancePrefData.editorLinespacing: Float(self.editorLinespacing),
|
||||
AppearancePrefData.editorUsesLigatures: self.editorUsesLigatures,
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
class AppearancePrefPane: PrefPane, NSComboBoxDelegate, NSControlTextEditingDelegate {
|
||||
|
||||
override var displayName: String {
|
||||
@ -264,12 +303,12 @@ extension AppearancePrefPane {
|
||||
fileprivate func cappedLinespacing(_ linespacing: Float) -> CGFloat {
|
||||
let cgfLinespacing = CGFloat(linespacing)
|
||||
|
||||
guard cgfLinespacing >= PrefStore.minEditorLinespacing else {
|
||||
return PrefStore.defaultEditorLinespacing
|
||||
guard cgfLinespacing >= NeoVimView.minLinespacing else {
|
||||
return NeoVimView.defaultLinespacing
|
||||
}
|
||||
|
||||
guard cgfLinespacing <= PrefStore.maxEditorLinespacing else {
|
||||
return PrefStore.maxEditorLinespacing
|
||||
guard cgfLinespacing <= NeoVimView.maxLinespacing else {
|
||||
return NeoVimView.maxLinespacing
|
||||
}
|
||||
|
||||
return cgfLinespacing
|
||||
@ -278,12 +317,12 @@ extension AppearancePrefPane {
|
||||
fileprivate func cappedFontSize(_ size: Int) -> CGFloat {
|
||||
let cgfSize = CGFloat(size)
|
||||
|
||||
guard cgfSize >= PrefStore.minEditorFontSize else {
|
||||
return PrefStore.defaultEditorFontSize
|
||||
guard cgfSize >= NeoVimView.minFontSize else {
|
||||
return NeoVimView.defaultFont.pointSize
|
||||
}
|
||||
|
||||
guard cgfSize <= PrefStore.maxEditorFontSize else {
|
||||
return PrefStore.maxEditorFontSize
|
||||
guard cgfSize <= NeoVimView.maxFontSize else {
|
||||
return NeoVimView.maxFontSize
|
||||
}
|
||||
|
||||
return cgfSize
|
||||
|
@ -7,19 +7,61 @@ import Cocoa
|
||||
import PureLayout
|
||||
import RxSwift
|
||||
|
||||
struct GeneralPrefData: Equatable {
|
||||
let openNewWindowWhenLaunching: Bool
|
||||
let openNewWindowOnReactivation: Bool
|
||||
struct GeneralPrefData: Equatable, StandardPrefData {
|
||||
|
||||
let ignorePatterns: Set<FileItemIgnorePattern>
|
||||
}
|
||||
fileprivate static let openNewWindowWhenLaunching = "open-new-window-when-launching"
|
||||
fileprivate static let openNewWindowOnReactivation = "open-new-window-on-reactivation"
|
||||
fileprivate static let ignorePatterns = "ignore-patterns"
|
||||
|
||||
func == (left: GeneralPrefData, right: GeneralPrefData) -> Bool {
|
||||
fileprivate static let defaultIgnorePatterns = Set(
|
||||
[ "*/.git", "*.o", "*.d", "*.dia" ].map(FileItemIgnorePattern.init)
|
||||
)
|
||||
|
||||
static func ==(left: GeneralPrefData, right: GeneralPrefData) -> Bool {
|
||||
return left.openNewWindowWhenLaunching == right.openNewWindowWhenLaunching
|
||||
&& left.openNewWindowOnReactivation == right.openNewWindowOnReactivation
|
||||
&& left.ignorePatterns == right.ignorePatterns
|
||||
}
|
||||
|
||||
static let `default` = GeneralPrefData(openNewWindowWhenLaunching: true,
|
||||
openNewWindowOnReactivation: true,
|
||||
ignorePatterns: GeneralPrefData.defaultIgnorePatterns)
|
||||
|
||||
var openNewWindowWhenLaunching: Bool
|
||||
var openNewWindowOnReactivation: Bool
|
||||
var ignorePatterns: Set<FileItemIgnorePattern>
|
||||
|
||||
init(openNewWindowWhenLaunching: Bool,
|
||||
openNewWindowOnReactivation: Bool,
|
||||
ignorePatterns: Set<FileItemIgnorePattern>)
|
||||
{
|
||||
self.openNewWindowWhenLaunching = openNewWindowWhenLaunching
|
||||
self.openNewWindowOnReactivation = openNewWindowOnReactivation
|
||||
self.ignorePatterns = ignorePatterns
|
||||
}
|
||||
|
||||
init?(dict: [String: Any]) {
|
||||
guard let openNewWinWhenLaunching = PrefUtils.bool(from: dict, for: GeneralPrefData.openNewWindowWhenLaunching),
|
||||
let openNewWinOnReactivation = PrefUtils.bool(from: dict, for: GeneralPrefData.openNewWindowOnReactivation),
|
||||
let ignorePatternsStr = dict[GeneralPrefData.ignorePatterns] as? String
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
|
||||
self.init(openNewWindowWhenLaunching: openNewWinWhenLaunching,
|
||||
openNewWindowOnReactivation: openNewWinOnReactivation,
|
||||
ignorePatterns: PrefUtils.ignorePatterns(fromString: ignorePatternsStr))
|
||||
}
|
||||
|
||||
func dict() -> [String: Any] {
|
||||
return [
|
||||
GeneralPrefData.openNewWindowWhenLaunching: self.openNewWindowWhenLaunching,
|
||||
GeneralPrefData.openNewWindowOnReactivation: self.openNewWindowOnReactivation,
|
||||
GeneralPrefData.ignorePatterns: PrefUtils.ignorePatternString(fromSet: self.ignorePatterns),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
class GeneralPrefPane: PrefPane, NSTextFieldDelegate {
|
||||
|
||||
override var displayName: String {
|
||||
|
@ -14,19 +14,62 @@ enum MainWindowAction {
|
||||
case close(mainWindow: MainWindowComponent, mainWindowPrefData: MainWindowPrefData)
|
||||
}
|
||||
|
||||
struct MainWindowPrefData {
|
||||
struct MainWindowPrefData: StandardPrefData {
|
||||
|
||||
let isAllToolsVisible: Bool
|
||||
let isToolButtonsVisible: Bool
|
||||
fileprivate static let isAllToolsVisible = "is-all-tools-visible"
|
||||
fileprivate static let isToolButtonsVisible = "is-tool-buttons-visible"
|
||||
fileprivate static let toolPrefDatas = "tool-pref-datas"
|
||||
|
||||
// FIXME: REMOVE!
|
||||
let isFileBrowserVisible: Bool
|
||||
let fileBrowserWidth: Float
|
||||
static let `default` = MainWindowPrefData(isAllToolsVisible: true,
|
||||
isToolButtonsVisible: true,
|
||||
toolPrefDatas: [ ToolPrefData.defaults[.fileBrowser]! ])
|
||||
|
||||
var isAllToolsVisible: Bool
|
||||
var isToolButtonsVisible: Bool
|
||||
var toolPrefDatas: [ToolPrefData]
|
||||
|
||||
init(isAllToolsVisible: Bool, isToolButtonsVisible: Bool, toolPrefDatas: [ToolPrefData]) {
|
||||
self.isAllToolsVisible = isAllToolsVisible
|
||||
self.isToolButtonsVisible = isToolButtonsVisible
|
||||
self.toolPrefDatas = toolPrefDatas
|
||||
}
|
||||
|
||||
enum ToolIdentifier: String {
|
||||
init?(dict: [String: Any]) {
|
||||
|
||||
case fileBrowser = "com.qvacua.vimr.tool.file-browser"
|
||||
guard let isAllToolsVisible = PrefUtils.bool(from: dict, for: MainWindowPrefData.isAllToolsVisible),
|
||||
let isToolButtonsVisible = PrefUtils.bool(from: dict, for: MainWindowPrefData.isToolButtonsVisible),
|
||||
let toolDataDicts = dict[MainWindowPrefData.toolPrefDatas] as? [[String: Any]]
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Add default tool pref data for missing identifiers.
|
||||
let toolDatas = toolDataDicts.map { ToolPrefData(dict: $0) }.flatMap { $0 }
|
||||
let missingToolDatas = Set(ToolIdentifier.all)
|
||||
.subtracting(toolDatas.map { $0.identifier })
|
||||
.map { ToolPrefData.defaults[$0] }
|
||||
.flatMap { $0 }
|
||||
|
||||
self.init(isAllToolsVisible: isAllToolsVisible,
|
||||
isToolButtonsVisible: isToolButtonsVisible,
|
||||
toolPrefDatas: [ toolDatas, missingToolDatas ].flatMap { $0 })
|
||||
}
|
||||
|
||||
func dict() -> [String: Any] {
|
||||
return [
|
||||
MainWindowPrefData.isAllToolsVisible: self.isAllToolsVisible,
|
||||
MainWindowPrefData.isToolButtonsVisible: self.isToolButtonsVisible,
|
||||
MainWindowPrefData.toolPrefDatas: self.toolPrefDatas.map { $0.dict() },
|
||||
]
|
||||
}
|
||||
|
||||
func toolPrefData(for identifier: ToolIdentifier) -> ToolPrefData {
|
||||
guard let tool = self.toolPrefDatas.filter({ $0.identifier == identifier }).first else {
|
||||
preconditionFailure("[ERROR] No tool for \(identifier) found!")
|
||||
}
|
||||
|
||||
return tool
|
||||
}
|
||||
}
|
||||
|
||||
class MainWindowComponent: WindowComponent, NSWindowDelegate, NSUserInterfaceValidations, WorkspaceDelegate {
|
||||
@ -103,7 +146,6 @@ class MainWindowComponent: WindowComponent, NSWindowDelegate, NSUserInterfaceVal
|
||||
toolIdentifier: .fileBrowser,
|
||||
minimumDimension: 100)
|
||||
self.tools[.fileBrowser] = fileBrowserTool
|
||||
self.workspace.append(tool: fileBrowserTool, location: .left)
|
||||
|
||||
self.addReactions()
|
||||
|
||||
@ -120,7 +162,9 @@ class MainWindowComponent: WindowComponent, NSWindowDelegate, NSUserInterfaceVal
|
||||
|
||||
// By default the tool buttons are shown and no tools are shown.
|
||||
let mainWindowData = initialData.mainWindow
|
||||
fileBrowserTool.dimension = CGFloat(mainWindowData.fileBrowserWidth)
|
||||
let fileBrowserToolData = mainWindowData.toolPrefData(for: .fileBrowser)
|
||||
self.workspace.append(tool: fileBrowserTool, location: fileBrowserToolData.location)
|
||||
fileBrowserTool.dimension = CGFloat(fileBrowserToolData.dimension)
|
||||
|
||||
if !mainWindowData.isAllToolsVisible {
|
||||
self.toggleAllTools(self)
|
||||
@ -130,7 +174,7 @@ class MainWindowComponent: WindowComponent, NSWindowDelegate, NSUserInterfaceVal
|
||||
self.toggleToolButtons(self)
|
||||
}
|
||||
|
||||
if mainWindowData.isFileBrowserVisible {
|
||||
if fileBrowserToolData.isVisible {
|
||||
fileBrowserTool.toggle()
|
||||
}
|
||||
|
||||
@ -360,13 +404,13 @@ extension MainWindowComponent {
|
||||
|
||||
@IBAction func makeFontBigger(_ sender: Any?) {
|
||||
let curFont = self.neoVimView.font
|
||||
let font = self.fontManager.convert(curFont, toSize: min(curFont.pointSize + 1, PrefStore.maxEditorFontSize))
|
||||
let font = self.fontManager.convert(curFont, toSize: min(curFont.pointSize + 1, NeoVimView.maxFontSize))
|
||||
self.neoVimView.font = font
|
||||
}
|
||||
|
||||
@IBAction func makeFontSmaller(_ sender: Any?) {
|
||||
let curFont = self.neoVimView.font
|
||||
let font = self.fontManager.convert(curFont, toSize: max(curFont.pointSize - 1, PrefStore.minEditorFontSize))
|
||||
let font = self.fontManager.convert(curFont, toSize: max(curFont.pointSize - 1, NeoVimView.minFontSize))
|
||||
self.neoVimView.font = font
|
||||
}
|
||||
}
|
||||
@ -407,10 +451,13 @@ extension MainWindowComponent {
|
||||
self.fileItemService.unmonitor(url: self._cwd)
|
||||
|
||||
let fileBrowser = self.tools[.fileBrowser]!
|
||||
let fileBrowserData = ToolPrefData(identifier: .fileBrowser,
|
||||
location: fileBrowser.location,
|
||||
isVisible: fileBrowser.isSelected,
|
||||
dimension: fileBrowser.dimension)
|
||||
let prefData = MainWindowPrefData(isAllToolsVisible: self.workspace.isAllToolsVisible,
|
||||
isToolButtonsVisible: self.workspace.isToolButtonsVisible,
|
||||
isFileBrowserVisible: self.workspace.bars[.left]?.isOpen ?? true,
|
||||
fileBrowserWidth: Float(fileBrowser.dimension))
|
||||
toolPrefDatas: [ fileBrowserData ])
|
||||
|
||||
self.publish(event: MainWindowAction.close(mainWindow: self, mainWindowPrefData: prefData))
|
||||
}
|
||||
|
88
VimR/Pref38To128Converter.swift
Normal file
88
VimR/Pref38To128Converter.swift
Normal file
@ -0,0 +1,88 @@
|
||||
/**
|
||||
* Tae Won Ha - http://taewon.de - @hataewon
|
||||
* See LICENSE
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
fileprivate class PrefKeys38 {
|
||||
|
||||
static let openNewWindowWhenLaunching = "open-new-window-when-launching"
|
||||
static let openNewWindowOnReactivation = "open-new-window-on-reactivation"
|
||||
static let openQuicklyIgnorePatterns = "open-quickly-ignore-patterns"
|
||||
|
||||
static let editorFontName = "editor-font-name"
|
||||
static let editorFontSize = "editor-font-size"
|
||||
static let editorLinespacing = "editor-linespacing"
|
||||
static let editorUsesLigatures = "editor-uses-ligatures"
|
||||
|
||||
static let useSnapshotUpdateChannel = "use-snapshot-update-channel"
|
||||
static let useInteractiveZsh = "use-interactive-zsh"
|
||||
|
||||
static let isAllToolsVisible = "is-all-tools-visible"
|
||||
static let isToolButtonsShown = "is-tool-buttons-visible"
|
||||
|
||||
static let isFileBrowserOpen = "is-file-browser-visible"
|
||||
static let fileBrowserWidth = "file-browser-width"
|
||||
}
|
||||
|
||||
class Pref38To128Converter {
|
||||
|
||||
static let fromVersion = "38"
|
||||
static let toVersion = "128"
|
||||
|
||||
fileprivate let userDefaults = UserDefaults.standard
|
||||
fileprivate let fontManager = NSFontManager.shared()
|
||||
|
||||
func prefData128(from dict38: [String: Any]) -> PrefData {
|
||||
|
||||
let editorFontName = dict38[PrefKeys38.editorFontName] as? String ?? NeoVimView.defaultFont.fontName
|
||||
|
||||
let editorFontSize = CGFloat(
|
||||
PrefUtils.float(from: dict38, for: PrefKeys38.editorFontSize) ?? Float(NeoVimView.defaultFont.pointSize)
|
||||
)
|
||||
let editorFont = PrefUtils.saneFont(editorFontName, fontSize: editorFontSize)
|
||||
|
||||
let usesLigatures = PrefUtils.bool(from: dict38, for: PrefKeys38.editorUsesLigatures, default: false)
|
||||
let linespacingAsStr = PrefUtils.value(from: dict38, for: PrefKeys38.editorLinespacing, default: "1")
|
||||
let linespacing = PrefUtils.saneLinespacing(Float(linespacingAsStr) ?? 1)
|
||||
let openNewWindowWhenLaunching = PrefUtils.bool(from: dict38,
|
||||
for: PrefKeys38.openNewWindowWhenLaunching,
|
||||
default: true)
|
||||
let openNewWindowOnReactivation = PrefUtils.bool(from: dict38,
|
||||
for: PrefKeys38.openNewWindowOnReactivation,
|
||||
default: true)
|
||||
|
||||
let ignorePatternsList = (dict38[PrefKeys38.openQuicklyIgnorePatterns] as? String) ?? "*/.git, *.o, *.d, *.dia"
|
||||
let ignorePatterns = PrefUtils.ignorePatterns(fromString: ignorePatternsList)
|
||||
|
||||
let useSnapshotUpdate = PrefUtils.bool(from: dict38, for: PrefKeys38.useSnapshotUpdateChannel, default: false)
|
||||
let useInteractiveZsh = PrefUtils.bool(from: dict38, for: PrefKeys38.useInteractiveZsh, default: false)
|
||||
|
||||
let isAllToolsVisible = PrefUtils.bool(from: dict38, for: PrefKeys38.isAllToolsVisible, default: true)
|
||||
let isToolButtonsVisible = PrefUtils.bool(from: dict38, for: PrefKeys38.isToolButtonsShown, default: true)
|
||||
let isFileBrowserVisible = PrefUtils.bool(from: dict38, for: PrefKeys38.isFileBrowserOpen, default: true)
|
||||
let fileBrowserWidth = PrefUtils.float(from: dict38, for: PrefKeys38.fileBrowserWidth, default: 200)
|
||||
|
||||
let fileBrowserData = ToolPrefData(identifier: .fileBrowser,
|
||||
location: .left,
|
||||
isVisible: isFileBrowserVisible,
|
||||
dimension: CGFloat(fileBrowserWidth))
|
||||
|
||||
return PrefData(
|
||||
general: GeneralPrefData(
|
||||
openNewWindowWhenLaunching: openNewWindowWhenLaunching,
|
||||
openNewWindowOnReactivation: openNewWindowOnReactivation,
|
||||
ignorePatterns: ignorePatterns
|
||||
),
|
||||
appearance: AppearancePrefData(editorFont: editorFont,
|
||||
editorLinespacing: linespacing,
|
||||
editorUsesLigatures: usesLigatures),
|
||||
advanced: AdvancedPrefData(useSnapshotUpdateChannel: useSnapshotUpdate,
|
||||
useInteractiveZsh: useInteractiveZsh),
|
||||
mainWindow: MainWindowPrefData(isAllToolsVisible: isAllToolsVisible,
|
||||
isToolButtonsVisible: isToolButtonsVisible,
|
||||
toolPrefDatas: [ fileBrowserData ])
|
||||
)
|
||||
}
|
||||
}
|
@ -6,181 +6,96 @@
|
||||
import Cocoa
|
||||
import RxSwift
|
||||
|
||||
struct PrefData {
|
||||
protocol StandardPrefData {
|
||||
|
||||
init?(dict: [String: Any])
|
||||
func dict() -> [String: Any]
|
||||
}
|
||||
|
||||
struct PrefData: StandardPrefData {
|
||||
|
||||
fileprivate static let general = "general"
|
||||
fileprivate static let appearance = "appearance"
|
||||
fileprivate static let advanced = "advanced"
|
||||
fileprivate static let mainWindow = "mainWindow"
|
||||
|
||||
static let `default` = PrefData(general: .default, appearance: .default, advanced: .default, mainWindow: .default)
|
||||
|
||||
var general: GeneralPrefData
|
||||
var appearance: AppearancePrefData
|
||||
var advanced: AdvancedPrefData
|
||||
|
||||
var mainWindow: MainWindowPrefData
|
||||
|
||||
init(general: GeneralPrefData,
|
||||
appearance: AppearancePrefData,
|
||||
advanced: AdvancedPrefData,
|
||||
mainWindow: MainWindowPrefData)
|
||||
{
|
||||
self.general = general
|
||||
self.appearance = appearance
|
||||
self.advanced = advanced
|
||||
self.mainWindow = mainWindow
|
||||
}
|
||||
|
||||
private class PrefKeys {
|
||||
|
||||
static let openNewWindowWhenLaunching = "open-new-window-when-launching"
|
||||
static let openNewWindowOnReactivation = "open-new-window-on-reactivation"
|
||||
static let openQuicklyIgnorePatterns = "open-quickly-ignore-patterns"
|
||||
|
||||
static let editorFontName = "editor-font-name"
|
||||
static let editorFontSize = "editor-font-size"
|
||||
static let editorLinespacing = "editor-linespacing"
|
||||
static let editorUsesLigatures = "editor-uses-ligatures"
|
||||
|
||||
static let useSnapshotUpdateChannel = "use-snapshot-update-channel"
|
||||
static let useInteractiveZsh = "use-interactive-zsh"
|
||||
|
||||
static let isAllToolsVisible = "is-all-tools-visible"
|
||||
static let isToolButtonsShown = "is-tool-buttons-visible"
|
||||
|
||||
static let isFileBrowserOpen = "is-file-browser-visible"
|
||||
static let fileBrowserWidth = "file-browser-width"
|
||||
init?(dict: [String: Any]) {
|
||||
guard let generalDict: [String: Any] = PrefUtils.value(from: dict, for: PrefData.general),
|
||||
let appearanceDict: [String: Any] = PrefUtils.value(from: dict, for: PrefData.appearance),
|
||||
let advancedDict: [String: Any] = PrefUtils.value(from: dict, for: PrefData.advanced),
|
||||
let mainWindowDict: [String: Any] = PrefUtils.value(from: dict, for: PrefData.mainWindow)
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
|
||||
guard let general = GeneralPrefData(dict: generalDict),
|
||||
let appearance = AppearancePrefData(dict: appearanceDict),
|
||||
let advanced = AdvancedPrefData(dict: advancedDict),
|
||||
let mainWindow = MainWindowPrefData(dict: mainWindowDict)
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
|
||||
self.init(general: general, appearance: appearance, advanced: advanced, mainWindow: mainWindow)
|
||||
}
|
||||
|
||||
func dict() -> [String: Any] {
|
||||
return [
|
||||
PrefData.general: self.general.dict(),
|
||||
PrefData.appearance: self.appearance.dict(),
|
||||
PrefData.advanced: self.advanced.dict(),
|
||||
PrefData.mainWindow: self.mainWindow.dict(),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: We should generalize the persisting of pref data.
|
||||
/**
|
||||
To reset prefs
|
||||
To reset prefs for 38
|
||||
$ defaults write com.qvacua.vimr 38 -dict editor-font-name InputMonoCompressed-Regular editor-font-size 13 editor-uses-ligatures 0 open-new-window-on-reactivation 1 open-new-window-when-launching 1
|
||||
$ defaults read ~/Library/Preferences/com.qvacua.VimR
|
||||
*/
|
||||
class PrefStore: StandardFlow {
|
||||
|
||||
fileprivate static let compatibleVersion = "38"
|
||||
|
||||
fileprivate static let defaultEditorFont = NeoVimView.defaultFont
|
||||
static let minEditorFontSize = NeoVimView.minFontSize
|
||||
static let maxEditorFontSize = NeoVimView.maxFontSize
|
||||
static let defaultEditorFontSize = NeoVimView.defaultFont.pointSize
|
||||
|
||||
static let defaultEditorLinespacing = NeoVimView.defaultLinespacing
|
||||
static let minEditorLinespacing = NeoVimView.minLinespacing
|
||||
static let maxEditorLinespacing = NeoVimView.maxLinespacing
|
||||
fileprivate static let compatibleVersion = "128"
|
||||
fileprivate static let lastCompatibleVersion = "38"
|
||||
|
||||
fileprivate let userDefaults = UserDefaults.standard
|
||||
fileprivate let fontManager = NSFontManager.shared()
|
||||
|
||||
var data = PrefData(
|
||||
general: GeneralPrefData(openNewWindowWhenLaunching: true,
|
||||
openNewWindowOnReactivation: true,
|
||||
ignorePatterns: Set([ "*/.git", "*.o", "*.d", "*.dia" ].map(FileItemIgnorePattern.init))),
|
||||
appearance: AppearancePrefData(editorFont: PrefStore.defaultEditorFont,
|
||||
editorLinespacing: 1,
|
||||
editorUsesLigatures: false),
|
||||
advanced: AdvancedPrefData(useSnapshotUpdateChannel: false,
|
||||
useInteractiveZsh: false),
|
||||
mainWindow: MainWindowPrefData(isAllToolsVisible: true,
|
||||
isToolButtonsVisible: true,
|
||||
isFileBrowserVisible: true,
|
||||
fileBrowserWidth: 200)
|
||||
)
|
||||
var data = PrefData.default
|
||||
|
||||
override init(source: Observable<Any>) {
|
||||
super.init(source: source)
|
||||
|
||||
if let prefs = self.userDefaults.dictionary(forKey: PrefStore.compatibleVersion) {
|
||||
self.data = self.prefDataFromDict(prefs)
|
||||
} else {
|
||||
self.userDefaults.setValue(self.prefsDict(self.data), forKey: PrefStore.compatibleVersion)
|
||||
}
|
||||
// 128
|
||||
self.data = PrefData(dict: prefs) ?? .default
|
||||
} else if let dictLastVersion = self.userDefaults.dictionary(forKey: PrefStore.lastCompatibleVersion) {
|
||||
// 38
|
||||
self.data = Pref38To128Converter().prefData128(from: dictLastVersion)
|
||||
}
|
||||
|
||||
fileprivate func prefDataFromDict(_ prefs: [String: Any]) -> PrefData {
|
||||
|
||||
let editorFontName = prefs[PrefKeys.editorFontName] as? String ?? PrefStore.defaultEditorFont.fontName
|
||||
let editorFontSize = CGFloat(
|
||||
(prefs[PrefKeys.editorFontSize] as? NSNumber)?.floatValue ?? Float(PrefStore.defaultEditorFont.pointSize)
|
||||
)
|
||||
let editorFont = self.saneFont(editorFontName, fontSize: editorFontSize)
|
||||
|
||||
let usesLigatures = self.bool(from: prefs, for: PrefKeys.editorUsesLigatures, default: false)
|
||||
let linespacing = self.saneLinespacing(Float((prefs[PrefKeys.editorLinespacing] as? String) ?? "1") ?? 1)
|
||||
let openNewWindowWhenLaunching = self.bool(from: prefs, for: PrefKeys.openNewWindowWhenLaunching, default: true)
|
||||
let openNewWindowOnReactivation = self.bool(from: prefs, for: PrefKeys.openNewWindowOnReactivation, default: true)
|
||||
|
||||
let ignorePatternsList = (prefs[PrefKeys.openQuicklyIgnorePatterns] as? String) ?? "*/.git, *.o, *.d, *.dia"
|
||||
let ignorePatterns = PrefUtils.ignorePatterns(fromString: ignorePatternsList)
|
||||
|
||||
let useSnapshotUpdate = self.bool(from: prefs, for: PrefKeys.useSnapshotUpdateChannel, default: false)
|
||||
let useInteractiveZsh = self.bool(from: prefs, for: PrefKeys.useInteractiveZsh, default: false)
|
||||
|
||||
let isAllToolsVisible = self.bool(from: prefs, for: PrefKeys.isAllToolsVisible, default: true)
|
||||
let isToolButtonsVisible = self.bool(from: prefs, for: PrefKeys.isToolButtonsShown, default: true)
|
||||
let isFileBrowserVisible = self.bool(from: prefs, for: PrefKeys.isFileBrowserOpen, default: true)
|
||||
let fileBrowserWidth = (prefs[PrefKeys.fileBrowserWidth] as? NSNumber)?.floatValue ?? Float(200)
|
||||
|
||||
return PrefData(
|
||||
general: GeneralPrefData(
|
||||
openNewWindowWhenLaunching: openNewWindowWhenLaunching,
|
||||
openNewWindowOnReactivation: openNewWindowOnReactivation,
|
||||
ignorePatterns: ignorePatterns
|
||||
),
|
||||
appearance: AppearancePrefData(editorFont: editorFont,
|
||||
editorLinespacing: linespacing,
|
||||
editorUsesLigatures: usesLigatures),
|
||||
advanced: AdvancedPrefData(useSnapshotUpdateChannel: useSnapshotUpdate,
|
||||
useInteractiveZsh: useInteractiveZsh),
|
||||
mainWindow: MainWindowPrefData(isAllToolsVisible: isAllToolsVisible,
|
||||
isToolButtonsVisible: isToolButtonsVisible,
|
||||
isFileBrowserVisible: isFileBrowserVisible,
|
||||
fileBrowserWidth: fileBrowserWidth)
|
||||
)
|
||||
}
|
||||
|
||||
fileprivate func bool(from prefs: [String: Any], for key: String, default defaultValue: Bool) -> Bool {
|
||||
return (prefs[key] as? NSNumber)?.boolValue ?? defaultValue
|
||||
}
|
||||
|
||||
fileprivate func saneFont(_ fontName: String, fontSize: CGFloat) -> NSFont {
|
||||
var editorFont = NSFont(name: fontName, size: fontSize) ?? PrefStore.defaultEditorFont
|
||||
if !editorFont.isFixedPitch {
|
||||
editorFont = fontManager.convert(PrefStore.defaultEditorFont, toSize: editorFont.pointSize)
|
||||
}
|
||||
if editorFont.pointSize < PrefStore.minEditorFontSize || editorFont.pointSize > PrefStore.maxEditorFontSize {
|
||||
editorFont = fontManager.convert(editorFont, toSize: PrefStore.defaultEditorFont.pointSize)
|
||||
}
|
||||
|
||||
return editorFont
|
||||
}
|
||||
|
||||
fileprivate func saneLinespacing(_ fLinespacing: Float) -> CGFloat {
|
||||
let linespacing = CGFloat(fLinespacing)
|
||||
guard linespacing >= PrefStore.minEditorLinespacing && linespacing <= PrefStore.maxEditorLinespacing else {
|
||||
return PrefStore.defaultEditorLinespacing
|
||||
}
|
||||
|
||||
return linespacing
|
||||
}
|
||||
|
||||
fileprivate func prefsDict(_ prefData: PrefData) -> [String: Any] {
|
||||
let generalData = prefData.general
|
||||
let appearanceData = prefData.appearance
|
||||
let advancedData = prefData.advanced
|
||||
let mainWindowData = prefData.mainWindow
|
||||
|
||||
let ignorePatterns = PrefUtils.ignorePatternString(fromSet: generalData.ignorePatterns) as Any
|
||||
|
||||
let prefs: [String: Any] = [
|
||||
// General
|
||||
PrefKeys.openNewWindowWhenLaunching: generalData.openNewWindowWhenLaunching as Any,
|
||||
PrefKeys.openNewWindowOnReactivation: generalData.openNewWindowOnReactivation as Any,
|
||||
PrefKeys.openQuicklyIgnorePatterns: ignorePatterns,
|
||||
|
||||
// Appearance
|
||||
PrefKeys.editorFontName: appearanceData.editorFont.fontName as Any,
|
||||
PrefKeys.editorFontSize: appearanceData.editorFont.pointSize as Any,
|
||||
PrefKeys.editorLinespacing: String(format: "%.2f", appearanceData.editorLinespacing) as Any,
|
||||
PrefKeys.editorUsesLigatures: appearanceData.editorUsesLigatures as Any,
|
||||
|
||||
// Advanced
|
||||
PrefKeys.useSnapshotUpdateChannel: advancedData.useSnapshotUpdateChannel as Any,
|
||||
PrefKeys.useInteractiveZsh: advancedData.useInteractiveZsh as Any,
|
||||
|
||||
// MainWindow
|
||||
PrefKeys.isAllToolsVisible: mainWindowData.isAllToolsVisible,
|
||||
PrefKeys.isToolButtonsShown: mainWindowData.isToolButtonsVisible,
|
||||
PrefKeys.isFileBrowserOpen: mainWindowData.isFileBrowserVisible,
|
||||
PrefKeys.fileBrowserWidth: mainWindowData.fileBrowserWidth
|
||||
]
|
||||
|
||||
return prefs
|
||||
// We write self.data here for when PrefData(dict: prefs) was nil or the prefs last compatible version was
|
||||
// converted.
|
||||
self.userDefaults.setValue(self.data.dict(), forKey: PrefStore.compatibleVersion)
|
||||
}
|
||||
|
||||
override func subscription(source: Observable<Any>) -> Disposable {
|
||||
@ -198,7 +113,7 @@ class PrefStore: StandardFlow {
|
||||
return
|
||||
}
|
||||
|
||||
self.userDefaults.setValue(self.prefsDict(self.data), forKey: PrefStore.compatibleVersion)
|
||||
self.userDefaults.setValue(self.data.dict(), forKey: PrefStore.compatibleVersion)
|
||||
self.publish(event: self.data)
|
||||
})
|
||||
}
|
||||
|
@ -34,4 +34,57 @@ class PrefUtils {
|
||||
.sorted()
|
||||
.joined(separator: ", ")
|
||||
}
|
||||
|
||||
static func value<T>(from dict: [String: Any], for key: String) -> T? {
|
||||
return dict[key] as? T
|
||||
}
|
||||
|
||||
static func value<T>(from dict: [String: Any], for key: String, default defaultValue: T) -> T {
|
||||
return dict[key] as? T ?? defaultValue
|
||||
}
|
||||
|
||||
static func float(from dict: [String: Any], for key: String, default defaultValue: Float) -> Float {
|
||||
return (dict[key] as? NSNumber)?.floatValue ?? defaultValue
|
||||
}
|
||||
|
||||
static func float(from dict: [String: Any], for key: String) -> Float? {
|
||||
guard let number = dict[key] as? NSNumber else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return number.floatValue
|
||||
}
|
||||
|
||||
static func bool(from dict: [String: Any], for key: String) -> Bool? {
|
||||
guard let number = dict[key] as? NSNumber else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return number.boolValue
|
||||
}
|
||||
|
||||
static func bool(from dict: [String: Any], for key: String, default defaultValue: Bool) -> Bool {
|
||||
return (dict[key] as? NSNumber)?.boolValue ?? defaultValue
|
||||
}
|
||||
|
||||
static func saneFont(_ fontName: String, fontSize: CGFloat) -> NSFont {
|
||||
var editorFont = NSFont(name: fontName, size: fontSize) ?? NeoVimView.defaultFont
|
||||
if !editorFont.isFixedPitch {
|
||||
editorFont = NSFontManager.shared().convert(NeoVimView.defaultFont, toSize: editorFont.pointSize)
|
||||
}
|
||||
if editorFont.pointSize < NeoVimView.minFontSize || editorFont.pointSize > NeoVimView.maxFontSize {
|
||||
editorFont = NSFontManager.shared().convert(editorFont, toSize: NeoVimView.defaultFont.pointSize)
|
||||
}
|
||||
|
||||
return editorFont
|
||||
}
|
||||
|
||||
static func saneLinespacing(_ fLinespacing: Float) -> CGFloat {
|
||||
let linespacing = CGFloat(fLinespacing)
|
||||
guard linespacing >= NeoVimView.minLinespacing && linespacing <= NeoVimView.maxLinespacing else {
|
||||
return NeoVimView.defaultLinespacing
|
||||
}
|
||||
|
||||
return linespacing
|
||||
}
|
||||
}
|
||||
|
@ -5,44 +5,60 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
protocol StandardPrefData {
|
||||
enum ToolIdentifier: String {
|
||||
|
||||
init?(dict: [String: Any])
|
||||
func dict() -> [String: Any]
|
||||
case fileBrowser = "com.qvacua.vimr.tool.file-browser"
|
||||
|
||||
static let all = [ fileBrowser ]
|
||||
}
|
||||
|
||||
struct ToolPrefData: StandardPrefData {
|
||||
|
||||
fileprivate static let identifier = "identifier"
|
||||
fileprivate static let isVisible = "isVisible"
|
||||
fileprivate static let location = "location"
|
||||
fileprivate static let isVisible = "is-visible"
|
||||
fileprivate static let dimension = "dimension"
|
||||
|
||||
let identifier: ToolIdentifier
|
||||
let isVisible: Bool
|
||||
let dimension: Float
|
||||
static let defaults: [ToolIdentifier: ToolPrefData] = [
|
||||
.fileBrowser: ToolPrefData(identifier: .fileBrowser, location: .left, isVisible: true, dimension: 200),
|
||||
]
|
||||
|
||||
init(identifier: ToolIdentifier, isVisible: Bool, dimension: Float) {
|
||||
var identifier: ToolIdentifier
|
||||
var location: WorkspaceBarLocation
|
||||
var isVisible: Bool
|
||||
var dimension: CGFloat
|
||||
|
||||
init(identifier: ToolIdentifier, location: WorkspaceBarLocation, isVisible: Bool, dimension: CGFloat) {
|
||||
self.identifier = identifier
|
||||
self.location = location
|
||||
self.isVisible = isVisible
|
||||
self.dimension = dimension
|
||||
}
|
||||
|
||||
func dict() -> [String: Any] {
|
||||
return [
|
||||
ToolPrefData.identifier: self.identifier,
|
||||
ToolPrefData.identifier: self.identifier.rawValue,
|
||||
ToolPrefData.location: self.location.rawValue,
|
||||
ToolPrefData.isVisible: self.isVisible,
|
||||
ToolPrefData.dimension: self.dimension,
|
||||
ToolPrefData.dimension: Float(self.dimension),
|
||||
]
|
||||
}
|
||||
|
||||
init?(dict: [String: Any]) {
|
||||
guard let identifier = dict[ToolPrefData.identifier] as? ToolIdentifier,
|
||||
let isVisible = dict[ToolPrefData.isVisible] as? Bool,
|
||||
let dimension = dict[ToolPrefData.dimension] as? Float
|
||||
guard let identifierRawValue = dict[ToolPrefData.identifier] as? String,
|
||||
let locationRawValue = dict[ToolPrefData.location] as? String,
|
||||
let isVisible = PrefUtils.bool(from: dict, for: ToolPrefData.isVisible),
|
||||
let fDimension = PrefUtils.float(from: dict, for: ToolPrefData.dimension)
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
|
||||
self.init(identifier: identifier, isVisible: isVisible, dimension: dimension)
|
||||
guard let identifier = ToolIdentifier(rawValue: identifierRawValue),
|
||||
let location = WorkspaceBarLocation(rawValue: locationRawValue)
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
|
||||
self.init(identifier: identifier, location: location, isVisible: isVisible, dimension: CGFloat(fDimension))
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@
|
||||
import Cocoa
|
||||
import PureLayout
|
||||
|
||||
enum WorkspaceBarLocation {
|
||||
enum WorkspaceBarLocation: String {
|
||||
case top
|
||||
case right
|
||||
case bottom
|
||||
|
Loading…
Reference in New Issue
Block a user