├── .gitignore ├── LICENSE ├── README.md ├── SidebarApp.xcodeproj └── project.pbxproj └── SidebarApp ├── About Window ├── AboutCommand.swift ├── AboutView.swift ├── AboutWindow.swift └── Attributions Window │ ├── AttributionsView.swift │ └── AttributionsWindow.swift ├── Assets.xcassets ├── AccentColor.colorset │ └── Contents.json ├── AppIcon.appiconset │ └── Contents.json └── Contents.json ├── Custom Views ├── AlwaysOnTop.swift └── DropTarget.swift ├── Export ├── ExportCommands.swift └── MyExportDocument.swift ├── MainScene.swift ├── MainView.swift ├── Menu Bar Button ├── MenuBarButton.swift └── MenuBarPopup.swift ├── My Menu Commands └── MyCommands.swift ├── Panes ├── EmptyPane.swift ├── HelloWorldPane.swift ├── MoreStuffPane.swift ├── Pane Helper │ ├── Pane.swift │ └── PaneBackground.swift └── WhatsUpPane.swift ├── Preview Content └── Preview Assets.xcassets │ └── Contents.json ├── Settings ├── GeneralSettingsView.swift └── SettingsWindow.swift ├── Sidebar ├── Sections │ ├── GeneralSidebarSection.swift │ └── MoreSidebarSection.swift ├── Sidebar.swift ├── SidebarFooter.swift └── SidebarPane.swift ├── SidebarApp.entitlements ├── SidebarApp.swift └── Utilities ├── AppKit Extensions └── NSWindow+AlwaysOnTop.swift ├── Bundle Extensions ├── Bundle+Copyright.swift ├── Bundle+Name.swift └── Bundle+Version.swift └── WindowReflection.swift /.gitignore: -------------------------------------------------------------------------------- 1 | ################################ 2 | ### Xcode project .gitignore ### 3 | ################################ 4 | 5 | ### macOS ### 6 | 7 | .DS_Store 8 | 9 | ### User settings and such ### 10 | 11 | xcuserdata/ 12 | *.xcworkspace 13 | !default.xcworkspace 14 | 15 | ### Build Artifacts ### 16 | 17 | *.hmap 18 | *.ipa 19 | *.dSYM.zip 20 | *.dSYM 21 | 22 | *.xcworkspace 23 | !default.xcworkspace 24 | xcuserdata 25 | profile 26 | *.moved-aside 27 | DerivedData 28 | .idea/ 29 | *.xccheckout 30 | build/ 31 | 32 | ### Swift Package Manager ### 33 | 34 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata 35 | .build/ 36 | 37 | ### Playgrounds ### 38 | 39 | timeline.xctimeline 40 | playground.xcworkspace 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Simon Weniger 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sidebar App 2 | 3 | This is a template repository for quickly creating boilerplate code for a SwiftUI macOS app. 4 | 5 | # License 6 | 7 | This app is released into the public domain under The Unlicense. See LICENSE file for more information. 8 | 9 | # Overview 10 | 11 | This is a Github template repository that you can create your new repository from by clicking the big green "Use this template" button on Github. 12 | 13 | Here's a screenshot: 14 | 15 | ![Sidebar App](https://user-images.githubusercontent.com/384210/169694882-42e7bb8c-c576-42a8-a6ac-bb2794c76f95.png) 16 | 17 | What you get is an Xcode project with boilerplate code for a SwiftUI-based macOS app with a few things already set up: 18 | 19 | - A sidebar plus a button and menu options for toggling the sidebar. 20 | - A menu bar button that shows a SwiftUI view when left-clicked or a menu when right clicked. 21 | - A siderbar search box, but also a detail view pane specific searchbox in the window toolbar. 22 | - A detail view pane with an example drop target for dropping files onto the window. 23 | - A custom About window containing a SwiftUI view, and an attributions view, also in SwiftUI. 24 | - A menu option for toggling whether the window should always float on top of other windows. 25 | - A custom menu for arbitrary menu options. 26 | - An Export menu option replacement. 27 | - A tabbed settings window. 28 | -------------------------------------------------------------------------------- /SidebarApp.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 55; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 4180AEAE2839753E0054FEA9 /* SidebarApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4180AEAD2839753E0054FEA9 /* SidebarApp.swift */; }; 11 | 4180AEB22839753F0054FEA9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4180AEB12839753F0054FEA9 /* Assets.xcassets */; }; 12 | 4180AEB52839753F0054FEA9 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4180AEB42839753F0054FEA9 /* Preview Assets.xcassets */; }; 13 | 4180AEBD283975670054FEA9 /* MainScene.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4180AEBC283975670054FEA9 /* MainScene.swift */; }; 14 | 4180AEC0283976740054FEA9 /* SettingsWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4180AEBF283976740054FEA9 /* SettingsWindow.swift */; }; 15 | 4180AEC2283976F80054FEA9 /* GeneralSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4180AEC1283976F80054FEA9 /* GeneralSettingsView.swift */; }; 16 | 4180AEC4283979530054FEA9 /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4180AEC3283979530054FEA9 /* MainView.swift */; }; 17 | 4180AEC7283979FA0054FEA9 /* Sidebar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4180AEC6283979FA0054FEA9 /* Sidebar.swift */; }; 18 | 4180AEC928397A0B0054FEA9 /* EmptyPane.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4180AEC828397A0B0054FEA9 /* EmptyPane.swift */; }; 19 | 4180AECC28397C6F0054FEA9 /* HelloWorldPane.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4180AECB28397C6F0054FEA9 /* HelloWorldPane.swift */; }; 20 | 4180AECE28397C880054FEA9 /* WhatsUpPane.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4180AECD28397C880054FEA9 /* WhatsUpPane.swift */; }; 21 | 4180AED028397DB00054FEA9 /* MoreStuffPane.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4180AECF28397DB00054FEA9 /* MoreStuffPane.swift */; }; 22 | 4180AED228397F100054FEA9 /* GeneralSidebarSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4180AED128397F100054FEA9 /* GeneralSidebarSection.swift */; }; 23 | 4180AED428397F790054FEA9 /* SidebarPane.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4180AED328397F790054FEA9 /* SidebarPane.swift */; }; 24 | 4180AED7283980560054FEA9 /* MoreSidebarSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4180AED6283980560054FEA9 /* MoreSidebarSection.swift */; }; 25 | 4180AED9283983150054FEA9 /* SidebarFooter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4180AED8283983150054FEA9 /* SidebarFooter.swift */; }; 26 | 4180AEDC283984740054FEA9 /* ExportCommands.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4180AEDB283984740054FEA9 /* ExportCommands.swift */; }; 27 | 4180AEDE283985190054FEA9 /* MyExportDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4180AEDD283985190054FEA9 /* MyExportDocument.swift */; }; 28 | 4180AEE128398F460054FEA9 /* PaneBackground.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4180AEE028398F460054FEA9 /* PaneBackground.swift */; }; 29 | 4180AEE328398FE50054FEA9 /* Pane.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4180AEE228398FE50054FEA9 /* Pane.swift */; }; 30 | 4180AEE728399E160054FEA9 /* MyCommands.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4180AEE628399E160054FEA9 /* MyCommands.swift */; }; 31 | 4180AEEB2839A0280054FEA9 /* AboutCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4180AEEA2839A0280054FEA9 /* AboutCommand.swift */; }; 32 | 4180AEEE2839A0660054FEA9 /* Bundle+Name.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4180AEED2839A0660054FEA9 /* Bundle+Name.swift */; }; 33 | 4180AEF12839A0830054FEA9 /* Bundle+Version.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4180AEF02839A0830054FEA9 /* Bundle+Version.swift */; }; 34 | 4180AEF32839A0950054FEA9 /* Bundle+Copyright.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4180AEF22839A0950054FEA9 /* Bundle+Copyright.swift */; }; 35 | 4180AEF52839A1370054FEA9 /* NSWindow+AlwaysOnTop.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4180AEF42839A1370054FEA9 /* NSWindow+AlwaysOnTop.swift */; }; 36 | 4180AEF92839A21F0054FEA9 /* AboutWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4180AEF82839A21F0054FEA9 /* AboutWindow.swift */; }; 37 | 4180AEFB2839A32F0054FEA9 /* AboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4180AEFA2839A32F0054FEA9 /* AboutView.swift */; }; 38 | 4180AEFE2839A3B30054FEA9 /* AttributionsWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4180AEFD2839A3B30054FEA9 /* AttributionsWindow.swift */; }; 39 | 4180AF022839A4BA0054FEA9 /* AttributionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4180AF012839A4BA0054FEA9 /* AttributionsView.swift */; }; 40 | 4180AF052839A89A0054FEA9 /* DropTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4180AF042839A89A0054FEA9 /* DropTarget.swift */; }; 41 | 4180AF072839AF320054FEA9 /* WindowReflection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4180AF062839AF320054FEA9 /* WindowReflection.swift */; }; 42 | 4180AF092839B0870054FEA9 /* AlwaysOnTop.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4180AF082839B0870054FEA9 /* AlwaysOnTop.swift */; }; 43 | 4180AF0F283A50660054FEA9 /* MenuBarButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4180AF0E283A50660054FEA9 /* MenuBarButton.swift */; }; 44 | 4180AF11283A50770054FEA9 /* MenuBarPopup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4180AF10283A50770054FEA9 /* MenuBarPopup.swift */; }; 45 | /* End PBXBuildFile section */ 46 | 47 | /* Begin PBXFileReference section */ 48 | 4180AEAA2839753E0054FEA9 /* SidebarApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SidebarApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; 49 | 4180AEAD2839753E0054FEA9 /* SidebarApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarApp.swift; sourceTree = ""; }; 50 | 4180AEB12839753F0054FEA9 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 51 | 4180AEB42839753F0054FEA9 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 52 | 4180AEB62839753F0054FEA9 /* SidebarApp.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = SidebarApp.entitlements; sourceTree = ""; }; 53 | 4180AEBC283975670054FEA9 /* MainScene.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainScene.swift; sourceTree = ""; }; 54 | 4180AEBF283976740054FEA9 /* SettingsWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsWindow.swift; sourceTree = ""; }; 55 | 4180AEC1283976F80054FEA9 /* GeneralSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneralSettingsView.swift; sourceTree = ""; }; 56 | 4180AEC3283979530054FEA9 /* MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainView.swift; sourceTree = ""; }; 57 | 4180AEC6283979FA0054FEA9 /* Sidebar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Sidebar.swift; sourceTree = ""; }; 58 | 4180AEC828397A0B0054FEA9 /* EmptyPane.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyPane.swift; sourceTree = ""; }; 59 | 4180AECB28397C6F0054FEA9 /* HelloWorldPane.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HelloWorldPane.swift; sourceTree = ""; }; 60 | 4180AECD28397C880054FEA9 /* WhatsUpPane.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WhatsUpPane.swift; sourceTree = ""; }; 61 | 4180AECF28397DB00054FEA9 /* MoreStuffPane.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoreStuffPane.swift; sourceTree = ""; }; 62 | 4180AED128397F100054FEA9 /* GeneralSidebarSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneralSidebarSection.swift; sourceTree = ""; }; 63 | 4180AED328397F790054FEA9 /* SidebarPane.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarPane.swift; sourceTree = ""; }; 64 | 4180AED6283980560054FEA9 /* MoreSidebarSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoreSidebarSection.swift; sourceTree = ""; }; 65 | 4180AED8283983150054FEA9 /* SidebarFooter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarFooter.swift; sourceTree = ""; }; 66 | 4180AEDB283984740054FEA9 /* ExportCommands.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExportCommands.swift; sourceTree = ""; }; 67 | 4180AEDD283985190054FEA9 /* MyExportDocument.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyExportDocument.swift; sourceTree = ""; }; 68 | 4180AEE028398F460054FEA9 /* PaneBackground.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaneBackground.swift; sourceTree = ""; }; 69 | 4180AEE228398FE50054FEA9 /* Pane.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Pane.swift; sourceTree = ""; }; 70 | 4180AEE628399E160054FEA9 /* MyCommands.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyCommands.swift; sourceTree = ""; }; 71 | 4180AEEA2839A0280054FEA9 /* AboutCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutCommand.swift; sourceTree = ""; }; 72 | 4180AEED2839A0660054FEA9 /* Bundle+Name.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Bundle+Name.swift"; sourceTree = ""; }; 73 | 4180AEF02839A0830054FEA9 /* Bundle+Version.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Bundle+Version.swift"; sourceTree = ""; }; 74 | 4180AEF22839A0950054FEA9 /* Bundle+Copyright.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Bundle+Copyright.swift"; sourceTree = ""; }; 75 | 4180AEF42839A1370054FEA9 /* NSWindow+AlwaysOnTop.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSWindow+AlwaysOnTop.swift"; sourceTree = ""; }; 76 | 4180AEF82839A21F0054FEA9 /* AboutWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutWindow.swift; sourceTree = ""; }; 77 | 4180AEFA2839A32F0054FEA9 /* AboutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutView.swift; sourceTree = ""; }; 78 | 4180AEFD2839A3B30054FEA9 /* AttributionsWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttributionsWindow.swift; sourceTree = ""; }; 79 | 4180AF012839A4BA0054FEA9 /* AttributionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttributionsView.swift; sourceTree = ""; }; 80 | 4180AF042839A89A0054FEA9 /* DropTarget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropTarget.swift; sourceTree = ""; }; 81 | 4180AF062839AF320054FEA9 /* WindowReflection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowReflection.swift; sourceTree = ""; }; 82 | 4180AF082839B0870054FEA9 /* AlwaysOnTop.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlwaysOnTop.swift; sourceTree = ""; }; 83 | 4180AF0E283A50660054FEA9 /* MenuBarButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuBarButton.swift; sourceTree = ""; }; 84 | 4180AF10283A50770054FEA9 /* MenuBarPopup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuBarPopup.swift; sourceTree = ""; }; 85 | /* End PBXFileReference section */ 86 | 87 | /* Begin PBXFrameworksBuildPhase section */ 88 | 4180AEA72839753E0054FEA9 /* Frameworks */ = { 89 | isa = PBXFrameworksBuildPhase; 90 | buildActionMask = 2147483647; 91 | files = ( 92 | ); 93 | runOnlyForDeploymentPostprocessing = 0; 94 | }; 95 | /* End PBXFrameworksBuildPhase section */ 96 | 97 | /* Begin PBXGroup section */ 98 | 4180AEA12839753E0054FEA9 = { 99 | isa = PBXGroup; 100 | children = ( 101 | 4180AEAC2839753E0054FEA9 /* SidebarApp */, 102 | 4180AEAB2839753E0054FEA9 /* Products */, 103 | ); 104 | sourceTree = ""; 105 | }; 106 | 4180AEAB2839753E0054FEA9 /* Products */ = { 107 | isa = PBXGroup; 108 | children = ( 109 | 4180AEAA2839753E0054FEA9 /* SidebarApp.app */, 110 | ); 111 | name = Products; 112 | sourceTree = ""; 113 | }; 114 | 4180AEAC2839753E0054FEA9 /* SidebarApp */ = { 115 | isa = PBXGroup; 116 | children = ( 117 | 4180AEEC2839A0560054FEA9 /* Utilities */, 118 | 4180AF032839A87B0054FEA9 /* Custom Views */, 119 | 4180AEBE283976620054FEA9 /* Settings */, 120 | 4180AEE528399DDE0054FEA9 /* Export */, 121 | 4180AEF72839A2010054FEA9 /* About Window */, 122 | 4180AF0D283A504E0054FEA9 /* Menu Bar Button */, 123 | 4180AEE428399DC50054FEA9 /* My Menu Commands */, 124 | 4180AECA28397C3A0054FEA9 /* Panes */, 125 | 4180AEC5283979E40054FEA9 /* Sidebar */, 126 | 4180AEC3283979530054FEA9 /* MainView.swift */, 127 | 4180AEBC283975670054FEA9 /* MainScene.swift */, 128 | 4180AEAD2839753E0054FEA9 /* SidebarApp.swift */, 129 | 4180AEB12839753F0054FEA9 /* Assets.xcassets */, 130 | 4180AEB62839753F0054FEA9 /* SidebarApp.entitlements */, 131 | 4180AEB32839753F0054FEA9 /* Preview Content */, 132 | ); 133 | path = SidebarApp; 134 | sourceTree = ""; 135 | }; 136 | 4180AEB32839753F0054FEA9 /* Preview Content */ = { 137 | isa = PBXGroup; 138 | children = ( 139 | 4180AEB42839753F0054FEA9 /* Preview Assets.xcassets */, 140 | ); 141 | path = "Preview Content"; 142 | sourceTree = ""; 143 | }; 144 | 4180AEBE283976620054FEA9 /* Settings */ = { 145 | isa = PBXGroup; 146 | children = ( 147 | 4180AEBF283976740054FEA9 /* SettingsWindow.swift */, 148 | 4180AEC1283976F80054FEA9 /* GeneralSettingsView.swift */, 149 | ); 150 | path = Settings; 151 | sourceTree = ""; 152 | }; 153 | 4180AEC5283979E40054FEA9 /* Sidebar */ = { 154 | isa = PBXGroup; 155 | children = ( 156 | 4180AED528397F8B0054FEA9 /* Sections */, 157 | 4180AEC6283979FA0054FEA9 /* Sidebar.swift */, 158 | 4180AED328397F790054FEA9 /* SidebarPane.swift */, 159 | 4180AED8283983150054FEA9 /* SidebarFooter.swift */, 160 | ); 161 | path = Sidebar; 162 | sourceTree = ""; 163 | }; 164 | 4180AECA28397C3A0054FEA9 /* Panes */ = { 165 | isa = PBXGroup; 166 | children = ( 167 | 4180AEDF28398F320054FEA9 /* Pane Helper */, 168 | 4180AECB28397C6F0054FEA9 /* HelloWorldPane.swift */, 169 | 4180AECD28397C880054FEA9 /* WhatsUpPane.swift */, 170 | 4180AECF28397DB00054FEA9 /* MoreStuffPane.swift */, 171 | 4180AEC828397A0B0054FEA9 /* EmptyPane.swift */, 172 | ); 173 | path = Panes; 174 | sourceTree = ""; 175 | }; 176 | 4180AED528397F8B0054FEA9 /* Sections */ = { 177 | isa = PBXGroup; 178 | children = ( 179 | 4180AED128397F100054FEA9 /* GeneralSidebarSection.swift */, 180 | 4180AED6283980560054FEA9 /* MoreSidebarSection.swift */, 181 | ); 182 | path = Sections; 183 | sourceTree = ""; 184 | }; 185 | 4180AEDF28398F320054FEA9 /* Pane Helper */ = { 186 | isa = PBXGroup; 187 | children = ( 188 | 4180AEE028398F460054FEA9 /* PaneBackground.swift */, 189 | 4180AEE228398FE50054FEA9 /* Pane.swift */, 190 | ); 191 | path = "Pane Helper"; 192 | sourceTree = ""; 193 | }; 194 | 4180AEE428399DC50054FEA9 /* My Menu Commands */ = { 195 | isa = PBXGroup; 196 | children = ( 197 | 4180AEE628399E160054FEA9 /* MyCommands.swift */, 198 | ); 199 | path = "My Menu Commands"; 200 | sourceTree = ""; 201 | }; 202 | 4180AEE528399DDE0054FEA9 /* Export */ = { 203 | isa = PBXGroup; 204 | children = ( 205 | 4180AEDB283984740054FEA9 /* ExportCommands.swift */, 206 | 4180AEDD283985190054FEA9 /* MyExportDocument.swift */, 207 | ); 208 | path = Export; 209 | sourceTree = ""; 210 | }; 211 | 4180AEEC2839A0560054FEA9 /* Utilities */ = { 212 | isa = PBXGroup; 213 | children = ( 214 | 4180AEF62839A13A0054FEA9 /* AppKit Extensions */, 215 | 4180AEEF2839A0720054FEA9 /* Bundle Extensions */, 216 | 4180AF062839AF320054FEA9 /* WindowReflection.swift */, 217 | ); 218 | path = Utilities; 219 | sourceTree = ""; 220 | }; 221 | 4180AEEF2839A0720054FEA9 /* Bundle Extensions */ = { 222 | isa = PBXGroup; 223 | children = ( 224 | 4180AEED2839A0660054FEA9 /* Bundle+Name.swift */, 225 | 4180AEF02839A0830054FEA9 /* Bundle+Version.swift */, 226 | 4180AEF22839A0950054FEA9 /* Bundle+Copyright.swift */, 227 | ); 228 | path = "Bundle Extensions"; 229 | sourceTree = ""; 230 | }; 231 | 4180AEF62839A13A0054FEA9 /* AppKit Extensions */ = { 232 | isa = PBXGroup; 233 | children = ( 234 | 4180AEF42839A1370054FEA9 /* NSWindow+AlwaysOnTop.swift */, 235 | ); 236 | path = "AppKit Extensions"; 237 | sourceTree = ""; 238 | }; 239 | 4180AEF72839A2010054FEA9 /* About Window */ = { 240 | isa = PBXGroup; 241 | children = ( 242 | 4180AEFC2839A3A80054FEA9 /* Attributions Window */, 243 | 4180AEEA2839A0280054FEA9 /* AboutCommand.swift */, 244 | 4180AEF82839A21F0054FEA9 /* AboutWindow.swift */, 245 | 4180AEFA2839A32F0054FEA9 /* AboutView.swift */, 246 | ); 247 | path = "About Window"; 248 | sourceTree = ""; 249 | }; 250 | 4180AEFC2839A3A80054FEA9 /* Attributions Window */ = { 251 | isa = PBXGroup; 252 | children = ( 253 | 4180AEFD2839A3B30054FEA9 /* AttributionsWindow.swift */, 254 | 4180AF012839A4BA0054FEA9 /* AttributionsView.swift */, 255 | ); 256 | path = "Attributions Window"; 257 | sourceTree = ""; 258 | }; 259 | 4180AF032839A87B0054FEA9 /* Custom Views */ = { 260 | isa = PBXGroup; 261 | children = ( 262 | 4180AF042839A89A0054FEA9 /* DropTarget.swift */, 263 | 4180AF082839B0870054FEA9 /* AlwaysOnTop.swift */, 264 | ); 265 | path = "Custom Views"; 266 | sourceTree = ""; 267 | }; 268 | 4180AF0D283A504E0054FEA9 /* Menu Bar Button */ = { 269 | isa = PBXGroup; 270 | children = ( 271 | 4180AF0E283A50660054FEA9 /* MenuBarButton.swift */, 272 | 4180AF10283A50770054FEA9 /* MenuBarPopup.swift */, 273 | ); 274 | path = "Menu Bar Button"; 275 | sourceTree = ""; 276 | }; 277 | /* End PBXGroup section */ 278 | 279 | /* Begin PBXNativeTarget section */ 280 | 4180AEA92839753E0054FEA9 /* SidebarApp */ = { 281 | isa = PBXNativeTarget; 282 | buildConfigurationList = 4180AEB92839753F0054FEA9 /* Build configuration list for PBXNativeTarget "SidebarApp" */; 283 | buildPhases = ( 284 | 4180AEA62839753E0054FEA9 /* Sources */, 285 | 4180AEA72839753E0054FEA9 /* Frameworks */, 286 | 4180AEA82839753E0054FEA9 /* Resources */, 287 | ); 288 | buildRules = ( 289 | ); 290 | dependencies = ( 291 | ); 292 | name = SidebarApp; 293 | productName = SidebarApp; 294 | productReference = 4180AEAA2839753E0054FEA9 /* SidebarApp.app */; 295 | productType = "com.apple.product-type.application"; 296 | }; 297 | /* End PBXNativeTarget section */ 298 | 299 | /* Begin PBXProject section */ 300 | 4180AEA22839753E0054FEA9 /* Project object */ = { 301 | isa = PBXProject; 302 | attributes = { 303 | BuildIndependentTargetsInParallel = 1; 304 | LastSwiftUpdateCheck = 1340; 305 | LastUpgradeCheck = 1500; 306 | TargetAttributes = { 307 | 4180AEA92839753E0054FEA9 = { 308 | CreatedOnToolsVersion = 13.4; 309 | }; 310 | }; 311 | }; 312 | buildConfigurationList = 4180AEA52839753E0054FEA9 /* Build configuration list for PBXProject "SidebarApp" */; 313 | compatibilityVersion = "Xcode 13.0"; 314 | developmentRegion = en; 315 | hasScannedForEncodings = 0; 316 | knownRegions = ( 317 | en, 318 | Base, 319 | ); 320 | mainGroup = 4180AEA12839753E0054FEA9; 321 | productRefGroup = 4180AEAB2839753E0054FEA9 /* Products */; 322 | projectDirPath = ""; 323 | projectRoot = ""; 324 | targets = ( 325 | 4180AEA92839753E0054FEA9 /* SidebarApp */, 326 | ); 327 | }; 328 | /* End PBXProject section */ 329 | 330 | /* Begin PBXResourcesBuildPhase section */ 331 | 4180AEA82839753E0054FEA9 /* Resources */ = { 332 | isa = PBXResourcesBuildPhase; 333 | buildActionMask = 2147483647; 334 | files = ( 335 | 4180AEB52839753F0054FEA9 /* Preview Assets.xcassets in Resources */, 336 | 4180AEB22839753F0054FEA9 /* Assets.xcassets in Resources */, 337 | ); 338 | runOnlyForDeploymentPostprocessing = 0; 339 | }; 340 | /* End PBXResourcesBuildPhase section */ 341 | 342 | /* Begin PBXSourcesBuildPhase section */ 343 | 4180AEA62839753E0054FEA9 /* Sources */ = { 344 | isa = PBXSourcesBuildPhase; 345 | buildActionMask = 2147483647; 346 | files = ( 347 | 4180AF022839A4BA0054FEA9 /* AttributionsView.swift in Sources */, 348 | 4180AEC928397A0B0054FEA9 /* EmptyPane.swift in Sources */, 349 | 4180AEBD283975670054FEA9 /* MainScene.swift in Sources */, 350 | 4180AEF12839A0830054FEA9 /* Bundle+Version.swift in Sources */, 351 | 4180AEDC283984740054FEA9 /* ExportCommands.swift in Sources */, 352 | 4180AEFB2839A32F0054FEA9 /* AboutView.swift in Sources */, 353 | 4180AEEE2839A0660054FEA9 /* Bundle+Name.swift in Sources */, 354 | 4180AECE28397C880054FEA9 /* WhatsUpPane.swift in Sources */, 355 | 4180AF052839A89A0054FEA9 /* DropTarget.swift in Sources */, 356 | 4180AF092839B0870054FEA9 /* AlwaysOnTop.swift in Sources */, 357 | 4180AEC4283979530054FEA9 /* MainView.swift in Sources */, 358 | 4180AEE328398FE50054FEA9 /* Pane.swift in Sources */, 359 | 4180AF11283A50770054FEA9 /* MenuBarPopup.swift in Sources */, 360 | 4180AEC7283979FA0054FEA9 /* Sidebar.swift in Sources */, 361 | 4180AF072839AF320054FEA9 /* WindowReflection.swift in Sources */, 362 | 4180AEC2283976F80054FEA9 /* GeneralSettingsView.swift in Sources */, 363 | 4180AEF52839A1370054FEA9 /* NSWindow+AlwaysOnTop.swift in Sources */, 364 | 4180AEF92839A21F0054FEA9 /* AboutWindow.swift in Sources */, 365 | 4180AEE128398F460054FEA9 /* PaneBackground.swift in Sources */, 366 | 4180AED7283980560054FEA9 /* MoreSidebarSection.swift in Sources */, 367 | 4180AEDE283985190054FEA9 /* MyExportDocument.swift in Sources */, 368 | 4180AF0F283A50660054FEA9 /* MenuBarButton.swift in Sources */, 369 | 4180AEF32839A0950054FEA9 /* Bundle+Copyright.swift in Sources */, 370 | 4180AED028397DB00054FEA9 /* MoreStuffPane.swift in Sources */, 371 | 4180AEC0283976740054FEA9 /* SettingsWindow.swift in Sources */, 372 | 4180AED9283983150054FEA9 /* SidebarFooter.swift in Sources */, 373 | 4180AEFE2839A3B30054FEA9 /* AttributionsWindow.swift in Sources */, 374 | 4180AEE728399E160054FEA9 /* MyCommands.swift in Sources */, 375 | 4180AEEB2839A0280054FEA9 /* AboutCommand.swift in Sources */, 376 | 4180AED228397F100054FEA9 /* GeneralSidebarSection.swift in Sources */, 377 | 4180AEAE2839753E0054FEA9 /* SidebarApp.swift in Sources */, 378 | 4180AED428397F790054FEA9 /* SidebarPane.swift in Sources */, 379 | 4180AECC28397C6F0054FEA9 /* HelloWorldPane.swift in Sources */, 380 | ); 381 | runOnlyForDeploymentPostprocessing = 0; 382 | }; 383 | /* End PBXSourcesBuildPhase section */ 384 | 385 | /* Begin XCBuildConfiguration section */ 386 | 4180AEB72839753F0054FEA9 /* Debug */ = { 387 | isa = XCBuildConfiguration; 388 | buildSettings = { 389 | ALWAYS_SEARCH_USER_PATHS = NO; 390 | CLANG_ANALYZER_NONNULL = YES; 391 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 392 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; 393 | CLANG_ENABLE_MODULES = YES; 394 | CLANG_ENABLE_OBJC_ARC = YES; 395 | CLANG_ENABLE_OBJC_WEAK = YES; 396 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 397 | CLANG_WARN_BOOL_CONVERSION = YES; 398 | CLANG_WARN_COMMA = YES; 399 | CLANG_WARN_CONSTANT_CONVERSION = YES; 400 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 401 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 402 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 403 | CLANG_WARN_EMPTY_BODY = YES; 404 | CLANG_WARN_ENUM_CONVERSION = YES; 405 | CLANG_WARN_INFINITE_RECURSION = YES; 406 | CLANG_WARN_INT_CONVERSION = YES; 407 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 408 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 409 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 410 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 411 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 412 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 413 | CLANG_WARN_STRICT_PROTOTYPES = YES; 414 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 415 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 416 | CLANG_WARN_UNREACHABLE_CODE = YES; 417 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 418 | COPY_PHASE_STRIP = NO; 419 | DEAD_CODE_STRIPPING = YES; 420 | DEBUG_INFORMATION_FORMAT = dwarf; 421 | ENABLE_STRICT_OBJC_MSGSEND = YES; 422 | ENABLE_TESTABILITY = YES; 423 | ENABLE_USER_SCRIPT_SANDBOXING = YES; 424 | GCC_C_LANGUAGE_STANDARD = gnu11; 425 | GCC_DYNAMIC_NO_PIC = NO; 426 | GCC_NO_COMMON_BLOCKS = YES; 427 | GCC_OPTIMIZATION_LEVEL = 0; 428 | GCC_PREPROCESSOR_DEFINITIONS = ( 429 | "DEBUG=1", 430 | "$(inherited)", 431 | ); 432 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 433 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 434 | GCC_WARN_UNDECLARED_SELECTOR = YES; 435 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 436 | GCC_WARN_UNUSED_FUNCTION = YES; 437 | GCC_WARN_UNUSED_VARIABLE = YES; 438 | MACOSX_DEPLOYMENT_TARGET = 13.0; 439 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 440 | MTL_FAST_MATH = YES; 441 | ONLY_ACTIVE_ARCH = YES; 442 | SDKROOT = macosx; 443 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 444 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 445 | }; 446 | name = Debug; 447 | }; 448 | 4180AEB82839753F0054FEA9 /* Release */ = { 449 | isa = XCBuildConfiguration; 450 | buildSettings = { 451 | ALWAYS_SEARCH_USER_PATHS = NO; 452 | CLANG_ANALYZER_NONNULL = YES; 453 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 454 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; 455 | CLANG_ENABLE_MODULES = YES; 456 | CLANG_ENABLE_OBJC_ARC = YES; 457 | CLANG_ENABLE_OBJC_WEAK = YES; 458 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 459 | CLANG_WARN_BOOL_CONVERSION = YES; 460 | CLANG_WARN_COMMA = YES; 461 | CLANG_WARN_CONSTANT_CONVERSION = YES; 462 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 463 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 464 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 465 | CLANG_WARN_EMPTY_BODY = YES; 466 | CLANG_WARN_ENUM_CONVERSION = YES; 467 | CLANG_WARN_INFINITE_RECURSION = YES; 468 | CLANG_WARN_INT_CONVERSION = YES; 469 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 470 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 471 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 472 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 473 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 474 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 475 | CLANG_WARN_STRICT_PROTOTYPES = YES; 476 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 477 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 478 | CLANG_WARN_UNREACHABLE_CODE = YES; 479 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 480 | COPY_PHASE_STRIP = NO; 481 | DEAD_CODE_STRIPPING = YES; 482 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 483 | ENABLE_NS_ASSERTIONS = NO; 484 | ENABLE_STRICT_OBJC_MSGSEND = YES; 485 | ENABLE_USER_SCRIPT_SANDBOXING = YES; 486 | GCC_C_LANGUAGE_STANDARD = gnu11; 487 | GCC_NO_COMMON_BLOCKS = YES; 488 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 489 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 490 | GCC_WARN_UNDECLARED_SELECTOR = YES; 491 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 492 | GCC_WARN_UNUSED_FUNCTION = YES; 493 | GCC_WARN_UNUSED_VARIABLE = YES; 494 | MACOSX_DEPLOYMENT_TARGET = 13.0; 495 | MTL_ENABLE_DEBUG_INFO = NO; 496 | MTL_FAST_MATH = YES; 497 | SDKROOT = macosx; 498 | SWIFT_COMPILATION_MODE = wholemodule; 499 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 500 | }; 501 | name = Release; 502 | }; 503 | 4180AEBA2839753F0054FEA9 /* Debug */ = { 504 | isa = XCBuildConfiguration; 505 | buildSettings = { 506 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 507 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 508 | CODE_SIGN_ENTITLEMENTS = SidebarApp/SidebarApp.entitlements; 509 | CODE_SIGN_STYLE = Manual; 510 | COMBINE_HIDPI_IMAGES = YES; 511 | CURRENT_PROJECT_VERSION = 1; 512 | DEAD_CODE_STRIPPING = YES; 513 | DEVELOPMENT_ASSET_PATHS = "\"SidebarApp/Preview Content\""; 514 | DEVELOPMENT_TEAM = ""; 515 | ENABLE_HARDENED_RUNTIME = YES; 516 | ENABLE_PREVIEWS = YES; 517 | GENERATE_INFOPLIST_FILE = YES; 518 | INFOPLIST_KEY_NSHumanReadableCopyright = ""; 519 | LD_RUNPATH_SEARCH_PATHS = ( 520 | "$(inherited)", 521 | "@executable_path/../Frameworks", 522 | ); 523 | MACOSX_DEPLOYMENT_TARGET = 13.0; 524 | MARKETING_VERSION = 1.0; 525 | PRODUCT_BUNDLE_IDENTIFIER = com.example.mac.SidebarApp; 526 | PRODUCT_NAME = "$(TARGET_NAME)"; 527 | PROVISIONING_PROFILE_SPECIFIER = ""; 528 | SWIFT_EMIT_LOC_STRINGS = YES; 529 | SWIFT_VERSION = 5.0; 530 | }; 531 | name = Debug; 532 | }; 533 | 4180AEBB2839753F0054FEA9 /* Release */ = { 534 | isa = XCBuildConfiguration; 535 | buildSettings = { 536 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 537 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 538 | CODE_SIGN_ENTITLEMENTS = SidebarApp/SidebarApp.entitlements; 539 | CODE_SIGN_STYLE = Manual; 540 | COMBINE_HIDPI_IMAGES = YES; 541 | CURRENT_PROJECT_VERSION = 1; 542 | DEAD_CODE_STRIPPING = YES; 543 | DEVELOPMENT_ASSET_PATHS = "\"SidebarApp/Preview Content\""; 544 | DEVELOPMENT_TEAM = ""; 545 | ENABLE_HARDENED_RUNTIME = YES; 546 | ENABLE_PREVIEWS = YES; 547 | GENERATE_INFOPLIST_FILE = YES; 548 | INFOPLIST_KEY_NSHumanReadableCopyright = ""; 549 | LD_RUNPATH_SEARCH_PATHS = ( 550 | "$(inherited)", 551 | "@executable_path/../Frameworks", 552 | ); 553 | MACOSX_DEPLOYMENT_TARGET = 13.0; 554 | MARKETING_VERSION = 1.0; 555 | PRODUCT_BUNDLE_IDENTIFIER = com.example.mac.SidebarApp; 556 | PRODUCT_NAME = "$(TARGET_NAME)"; 557 | PROVISIONING_PROFILE_SPECIFIER = ""; 558 | SWIFT_EMIT_LOC_STRINGS = YES; 559 | SWIFT_VERSION = 5.0; 560 | }; 561 | name = Release; 562 | }; 563 | /* End XCBuildConfiguration section */ 564 | 565 | /* Begin XCConfigurationList section */ 566 | 4180AEA52839753E0054FEA9 /* Build configuration list for PBXProject "SidebarApp" */ = { 567 | isa = XCConfigurationList; 568 | buildConfigurations = ( 569 | 4180AEB72839753F0054FEA9 /* Debug */, 570 | 4180AEB82839753F0054FEA9 /* Release */, 571 | ); 572 | defaultConfigurationIsVisible = 0; 573 | defaultConfigurationName = Release; 574 | }; 575 | 4180AEB92839753F0054FEA9 /* Build configuration list for PBXNativeTarget "SidebarApp" */ = { 576 | isa = XCConfigurationList; 577 | buildConfigurations = ( 578 | 4180AEBA2839753F0054FEA9 /* Debug */, 579 | 4180AEBB2839753F0054FEA9 /* Release */, 580 | ); 581 | defaultConfigurationIsVisible = 0; 582 | defaultConfigurationName = Release; 583 | }; 584 | /* End XCConfigurationList section */ 585 | }; 586 | rootObject = 4180AEA22839753E0054FEA9 /* Project object */; 587 | } 588 | -------------------------------------------------------------------------------- /SidebarApp/About Window/AboutCommand.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct AboutCommand: Commands { 4 | 5 | var body: some Commands { 6 | // Replace the About window menu option. 7 | CommandGroup(replacing: .appInfo) { 8 | Button { 9 | AboutWindow.show() 10 | } label: { 11 | Text("About \(Bundle.main.name)") 12 | } 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /SidebarApp/About Window/AboutView.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct AboutView: View { 4 | 5 | let icon: NSImage 6 | let name: String 7 | let version: String 8 | let build: String 9 | let copyright: String 10 | let developerName: String 11 | 12 | var body: some View { 13 | VStack { 14 | HStack(alignment: .top) { 15 | Image(nsImage: icon) 16 | .padding() 17 | VStack(alignment: .leading) { 18 | HStack(alignment: .firstTextBaseline) { 19 | Text(name) 20 | .font(.title) 21 | .bold() 22 | Spacer() 23 | Text("Version \(version)") + Text(" (\(build))") 24 | } 25 | Divider() 26 | .padding(.top, -8) 27 | Text("Developed by") 28 | .bold() 29 | .padding(.bottom, 2) 30 | Text(developerName) 31 | .padding(.bottom, 12) 32 | Spacer() 33 | Text(copyright) 34 | .font(.system(size: 10)) 35 | .foregroundColor(.secondary) 36 | }.padding(EdgeInsets(top: 20, leading: 0, bottom: 0, trailing: 30)) 37 | } 38 | HStack { 39 | Spacer() 40 | Button { 41 | AttributionsWindow.show() 42 | } label: { 43 | Text("Attributions") 44 | } 45 | }.padding() 46 | .background(Color(.sRGB, white: 0.0, opacity: 0.05)) 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /SidebarApp/About Window/AboutWindow.swift: -------------------------------------------------------------------------------- 1 | import AppKit 2 | import SwiftUI 3 | 4 | class AboutWindow: NSWindowController { 5 | 6 | static func show() { 7 | AboutWindow().window?.makeKeyAndOrderFront(nil) 8 | } 9 | 10 | convenience init() { 11 | 12 | let window = Self.makeWindow() 13 | 14 | window.backgroundColor = NSColor.controlBackgroundColor 15 | 16 | self.init(window: window) 17 | 18 | let contentView = makeAboutView() 19 | 20 | window.titleVisibility = .hidden 21 | window.titlebarAppearsTransparent = true 22 | window.center() 23 | window.title = "About Bootstrapp" 24 | window.contentView = NSHostingView(rootView: contentView) 25 | window.alwaysOnTop = true 26 | } 27 | 28 | private static func makeWindow() -> NSWindow { 29 | let contentRect = NSRect(x: 0, y: 0, width: 500, height: 260) 30 | let styleMask: NSWindow.StyleMask = [ 31 | .titled, 32 | .closable, 33 | .fullSizeContentView 34 | ] 35 | return NSWindow(contentRect: contentRect, 36 | styleMask: styleMask, 37 | backing: .buffered, 38 | defer: false) 39 | } 40 | 41 | private func makeAboutView() -> some View { 42 | AboutView( 43 | icon: NSApp.applicationIconImage ?? NSImage(), 44 | name: Bundle.main.name, 45 | version: Bundle.main.version, 46 | build: Bundle.main.buildVersion, 47 | copyright: Bundle.main.copyright, 48 | developerName: "") 49 | .frame(width: 500, height: 260) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /SidebarApp/About Window/Attributions Window/AttributionsView.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct AttributionsView: View { 4 | var body: some View { 5 | ScrollView(.vertical) { 6 | HStack { 7 | VStack(alignment: .leading, spacing: 20) { 8 | Text("Attributions") 9 | .font(.title) 10 | .bold() 11 | Text("") 12 | .multilineTextAlignment(.leading) 13 | } 14 | Spacer() 15 | } 16 | .frame(maxWidth: .infinity) 17 | } 18 | .frame(maxWidth: .infinity, maxHeight: .infinity) 19 | .padding() 20 | } 21 | } 22 | 23 | struct AttributionsView_Previews: PreviewProvider { 24 | static var previews: some View { 25 | AttributionsView() 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /SidebarApp/About Window/Attributions Window/AttributionsWindow.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import SwiftUI 3 | 4 | class AttributionsWindow: NSWindowController { 5 | 6 | static func show() { 7 | AttributionsWindow().window?.makeKeyAndOrderFront(nil) 8 | } 9 | 10 | convenience init() { 11 | 12 | let window = Self.makeWindow() 13 | 14 | window.backgroundColor = NSColor.controlBackgroundColor 15 | 16 | self.init(window: window) 17 | 18 | let contentView = AttributionsView() 19 | .frame(minWidth: 500, minHeight: 300) 20 | .frame(maxWidth: .infinity, maxHeight: .infinity) 21 | 22 | window.titleVisibility = .hidden 23 | window.titlebarAppearsTransparent = true 24 | window.center() 25 | window.title = "Attributions" 26 | window.contentView = NSHostingView(rootView: contentView) 27 | window.alwaysOnTop = true 28 | } 29 | 30 | private static func makeWindow() -> NSWindow { 31 | let contentRect = NSRect(x: 0, y: 0, width: 500, height: 300) 32 | let styleMask: NSWindow.StyleMask = [ 33 | .titled, 34 | .miniaturizable, 35 | .resizable, 36 | .closable, 37 | .fullSizeContentView 38 | ] 39 | return NSWindow(contentRect: contentRect, 40 | styleMask: styleMask, 41 | backing: .buffered, 42 | defer: false) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /SidebarApp/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /SidebarApp/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "scale" : "1x", 6 | "size" : "16x16" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "scale" : "2x", 11 | "size" : "16x16" 12 | }, 13 | { 14 | "idiom" : "mac", 15 | "scale" : "1x", 16 | "size" : "32x32" 17 | }, 18 | { 19 | "idiom" : "mac", 20 | "scale" : "2x", 21 | "size" : "32x32" 22 | }, 23 | { 24 | "idiom" : "mac", 25 | "scale" : "1x", 26 | "size" : "128x128" 27 | }, 28 | { 29 | "idiom" : "mac", 30 | "scale" : "2x", 31 | "size" : "128x128" 32 | }, 33 | { 34 | "idiom" : "mac", 35 | "scale" : "1x", 36 | "size" : "256x256" 37 | }, 38 | { 39 | "idiom" : "mac", 40 | "scale" : "2x", 41 | "size" : "256x256" 42 | }, 43 | { 44 | "idiom" : "mac", 45 | "scale" : "1x", 46 | "size" : "512x512" 47 | }, 48 | { 49 | "idiom" : "mac", 50 | "scale" : "2x", 51 | "size" : "512x512" 52 | } 53 | ], 54 | "info" : { 55 | "author" : "xcode", 56 | "version" : 1 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /SidebarApp/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /SidebarApp/Custom Views/AlwaysOnTop.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | // MARK: - Always on Top 4 | 5 | /// Set this view as a background on e.g. the main view. 6 | struct AlwaysOnTop: View { 7 | 8 | static let settingsKey = "window.setting.isAlwaysOnTop" 9 | 10 | @AppStorage(Self.settingsKey) var isAlwaysOnTop: Bool = false 11 | 12 | @State var window: NSWindow? 13 | 14 | var body: some View { 15 | WindowReflection(window: $window) 16 | .onReceive(window.publisher) { window in 17 | window.alwaysOnTop = isAlwaysOnTop 18 | } 19 | .onChange(of: isAlwaysOnTop) { isOnTop in 20 | window?.alwaysOnTop = isOnTop 21 | } 22 | } 23 | } 24 | 25 | // MARK: - Always on Top Command 26 | 27 | struct AlwaysOnTopCommand: Commands { 28 | 29 | var body: some Commands { 30 | CommandGroup(after: .windowArrangement) { 31 | // There is a SwiftUI bug that keeps the checkmark from updating. 32 | AlwaysOnTopCheckbox("Toggle Always on Top") 33 | } 34 | } 35 | } 36 | 37 | // MARK: - Always on Top Checkbox 38 | 39 | struct AlwaysOnTopCheckbox: View { 40 | 41 | let title: LocalizedStringKey 42 | 43 | @AppStorage(AlwaysOnTop.settingsKey) var isAlwaysOnTop: Bool = false 44 | 45 | init(_ title: LocalizedStringKey = "Always on top") { 46 | self.title = title 47 | } 48 | 49 | var body: some View { 50 | Toggle(title, isOn: $isAlwaysOnTop) 51 | .toggleStyle(CheckboxToggleStyle()) 52 | } 53 | } 54 | 55 | // MARK: - Preview 56 | 57 | struct AlwaysOnTopCheckbox_Previews: PreviewProvider { 58 | static var previews: some View { 59 | AlwaysOnTopCheckbox() 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /SidebarApp/Custom Views/DropTarget.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | import UniformTypeIdentifiers 3 | 4 | struct DropTarget: View { 5 | 6 | let delegate: DropDelegate 7 | 8 | let types: [UTType] 9 | 10 | var body: some View { 11 | ZStack { 12 | RoundedRectangle(cornerRadius: 8) 13 | .fill(Color.black.opacity(0.2)) 14 | RoundedRectangle(cornerRadius: 8) 15 | .strokeBorder(Color.white.opacity(0.2), style: StrokeStyle(lineWidth: 2, dash: [8, 4], dashPhase: 0)) 16 | .frame(maxWidth: .infinity, maxHeight: .infinity) 17 | } 18 | .onDrop(of: types, delegate: delegate) 19 | } 20 | } 21 | 22 | struct DropTarget_Previews: PreviewProvider, DropDelegate { 23 | 24 | static var previews: some View { 25 | DropTarget(delegate: Self(), types: [UTType.fileURL]) 26 | } 27 | 28 | func performDrop(info: DropInfo) -> Bool { false } 29 | } 30 | -------------------------------------------------------------------------------- /SidebarApp/Export/ExportCommands.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | /// In a finished app, this would be in a model object somewhere. 4 | private let myExportDocument = MyExportDocument(entries: [ 5 | MyDataEntry(name: "Wayne Gretzky", number: 99), 6 | MyDataEntry(name: "Mario Lemieux", number: 66), 7 | MyDataEntry(name: "Peter Forsberg", number: 21), 8 | MyDataEntry(name: "Mats Sundin", number: 13) 9 | ]) 10 | 11 | struct ExportCommands: Commands { 12 | 13 | @State private var isShowingExportDialog = false 14 | 15 | var body: some Commands { 16 | CommandGroup(replacing: .importExport) { 17 | Section { 18 | Button("Export…") { 19 | isShowingExportDialog = true 20 | } 21 | .fileExporter( 22 | isPresented: $isShowingExportDialog, 23 | document: myExportDocument, 24 | contentType: .tabSeparatedText, 25 | defaultFilename: "MyDocument.tsv") { result in 26 | } 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /SidebarApp/Export/MyExportDocument.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | import UniformTypeIdentifiers 3 | 4 | struct MyDataEntry { 5 | let name: String 6 | let number: Int 7 | } 8 | 9 | struct MyExportDocument: FileDocument { 10 | 11 | static var readableContentTypes = [UTType.tabSeparatedText] 12 | 13 | let entries: [MyDataEntry] 14 | 15 | init(entries: [MyDataEntry]) { 16 | self.entries = entries 17 | } 18 | 19 | init(configuration: ReadConfiguration) throws { 20 | self.init(entries: []) 21 | // TODO: Implement import 22 | } 23 | 24 | func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper { 25 | 26 | var data = Data() 27 | 28 | data.append("Name") 29 | data.append(delimiter) 30 | data.append("Number") 31 | data.append(delimiter) 32 | 33 | for entry in entries { 34 | data.append(entry.name) 35 | data.append(delimiter) 36 | data.append(entry.number) 37 | data.append(newline) 38 | } 39 | 40 | return FileWrapper(regularFileWithContents: data) 41 | } 42 | } 43 | 44 | private let newline = "\n".data(using: .utf8)! 45 | private let delimiter = "\t".data(using: .utf8)! 46 | 47 | private extension Data { 48 | 49 | mutating func append(_ string: S) { 50 | guard let data = string.data(using: .utf8) else { 51 | return 52 | } 53 | append(data) 54 | } 55 | 56 | mutating func append(_ number: Int) { 57 | append("\(number)") 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /SidebarApp/MainScene.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct MainScene: Scene { 4 | 5 | var body: some Scene { 6 | WindowGroup { 7 | MainView() 8 | .frame(minWidth: 400, minHeight: 300) 9 | .background(AlwaysOnTop()) 10 | } 11 | .commands { 12 | AboutCommand() 13 | SidebarCommands() 14 | ExportCommands() 15 | AlwaysOnTopCommand() 16 | 17 | /// Add a menu with custom commands 18 | MyCommands() 19 | 20 | // Remove the "New Window" option from the File menu. 21 | CommandGroup(replacing: .newItem, addition: { }) 22 | } 23 | Settings { 24 | SettingsWindow() 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /SidebarApp/MainView.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct MainView: View { 4 | 5 | var body: some View { 6 | NavigationView { 7 | Sidebar() 8 | EmptyPane() 9 | } 10 | } 11 | } 12 | 13 | struct MainView_Previews: PreviewProvider { 14 | static var previews: some View { 15 | MainView() 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /SidebarApp/Menu Bar Button/MenuBarButton.swift: -------------------------------------------------------------------------------- 1 | import AppKit 2 | import SwiftUI 3 | 4 | class MenuBarButton { 5 | 6 | let statusItem: NSStatusItem 7 | 8 | init() { 9 | statusItem = NSStatusBar.system 10 | .statusItem(withLength: CGFloat(NSStatusItem.squareLength)) 11 | 12 | guard let button = statusItem.button else { 13 | return 14 | } 15 | 16 | button.image = NSImage(systemSymbolName: "hammer", accessibilityDescription: nil) 17 | button.imagePosition = NSControl.ImagePosition.imageOnly 18 | button.target = self 19 | button.action = #selector(showMenu(_:)) 20 | button.sendAction(on: [.leftMouseUp, .rightMouseUp]) 21 | } 22 | 23 | // MARK: - Show Menu 24 | 25 | @objc 26 | func showMenu(_ sender: AnyObject?) { 27 | switch NSApp.currentEvent?.type { 28 | case .leftMouseUp: 29 | showPrimaryMenu() 30 | case .rightMouseUp: 31 | showSecondaryMenu() 32 | default: 33 | break 34 | } 35 | } 36 | 37 | func showPrimaryMenu() { 38 | let hostingView = NSHostingView(rootView: MenuBarPopup()) 39 | hostingView.frame.size = hostingView.fittingSize 40 | 41 | let menu = NSMenu() 42 | let item = NSMenuItem() 43 | item.view = hostingView 44 | menu.addItem(item) 45 | showStatusItemMenu(menu) 46 | } 47 | 48 | func showSecondaryMenu() { 49 | let menu = NSMenu() 50 | addItem("About...", action: #selector(showAbout), key: "c", to: menu) 51 | addItem("Do Stuff", action: #selector(doStuff), key: "d", to: menu) 52 | menu.addItem(NSMenuItem.separator()) 53 | addItem("Quit", action: #selector(quit), key: "q", to: menu) 54 | showStatusItemMenu(menu) 55 | } 56 | 57 | private func showStatusItemMenu(_ menu: NSMenu) { 58 | statusItem.menu = menu 59 | statusItem.button?.performClick(nil) 60 | statusItem.menu = nil 61 | } 62 | 63 | private func addItem(_ title: String, action: Selector?, key: String, to menu: NSMenu) { 64 | let item = NSMenuItem() 65 | item.title = title 66 | item.target = self 67 | item.action = action 68 | item.keyEquivalent = key 69 | menu.addItem(item) 70 | } 71 | 72 | // MARK: - Actions 73 | 74 | @objc 75 | func showAbout() { 76 | AboutWindow.show() 77 | } 78 | 79 | @objc 80 | func quit() { 81 | NSApp.terminate(self) 82 | } 83 | 84 | @objc 85 | func doStuff() { 86 | print("Do stuff") 87 | } 88 | } 89 | 90 | -------------------------------------------------------------------------------- /SidebarApp/Menu Bar Button/MenuBarPopup.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct MenuBarPopup: View { 4 | 5 | var body: some View { 6 | VStack(spacing: 20) { 7 | Text("Hello, World!") 8 | Button { 9 | AboutWindow.show() 10 | } label: { 11 | Text("About...") 12 | } 13 | } 14 | .frame(width: 200, height: 200) 15 | } 16 | } 17 | 18 | struct MenuBarPopup_Previews: PreviewProvider { 19 | static var previews: some View { 20 | MenuBarPopup() 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /SidebarApp/My Menu Commands/MyCommands.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct MyCommands: Commands { 4 | 5 | var body: some Commands { 6 | CommandMenu(Text("My Commands", comment: "My custom actions")) { 7 | Button { 8 | print("Build!") 9 | } label: { 10 | Text("Build", comment: "Build something or whatever.") 11 | } 12 | .keyboardShortcut("B", modifiers: [.command]) 13 | 14 | Divider() 15 | 16 | Button { 17 | print("Do Stuff!") 18 | } label: { 19 | Text("Do Stuff", comment: "Do various types of stuff.") 20 | } 21 | .keyboardShortcut("D", modifiers: [.command]) 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /SidebarApp/Panes/EmptyPane.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct EmptyPane: View { 4 | var body: some View { 5 | Pane { 6 | VStack { 7 | Text("No entry selected") 8 | } 9 | } 10 | } 11 | } 12 | 13 | struct EmptyPane_Previews: PreviewProvider { 14 | static var previews: some View { 15 | EmptyPane() 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /SidebarApp/Panes/HelloWorldPane.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct HelloWorldPane: View { 4 | 5 | var body: some View { 6 | Pane { 7 | VStack(spacing: 20) { 8 | Text("Hello, World!") 9 | AlwaysOnTopCheckbox() 10 | } 11 | } 12 | .navigationSubtitle("Hello, World!") 13 | } 14 | } 15 | 16 | struct HelloWorldPane_Previews: PreviewProvider { 17 | static var previews: some View { 18 | HelloWorldPane() 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /SidebarApp/Panes/MoreStuffPane.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | import UniformTypeIdentifiers 3 | 4 | private let dropTypes = [UTType.fileURL] 5 | 6 | // MARK: - More Stuff Pane 7 | 8 | struct MoreStuffPane: View { 9 | var body: some View { 10 | Pane { 11 | DropTarget(delegate: self, types: dropTypes) 12 | .frame(width: 220, height: 140) 13 | .overlay(Text("Drop files here")) 14 | } 15 | } 16 | } 17 | 18 | // MARK: - Drop Delegate 19 | 20 | extension MoreStuffPane: DropDelegate { 21 | 22 | /// Called when a drop, which has items conforming to any of the types 23 | /// passed to the onDrop() modifier, enters an onDrop target. 24 | /// This function is optional. 25 | func validateDrop(info: DropInfo) -> Bool { 26 | return true 27 | } 28 | 29 | /// Tells the delegate it can request the item provider data from the 30 | /// DropInfo incorporating it into the application's data model as 31 | /// appropriate. This function is required. 32 | /// 33 | /// Return `true` if the drop was successful, `false` otherwise. 34 | func performDrop(info: DropInfo) -> Bool { 35 | 36 | let itemProviders = info.itemProviders(for: [UTType.fileURL]) 37 | 38 | guard itemProviders.count == 1 else { 39 | // We want exactly one template root folder to be dropped. 40 | return false 41 | } 42 | 43 | for itemProvider in itemProviders { 44 | 45 | itemProvider.loadItem(forTypeIdentifier: UTType.fileURL.identifier, options: nil) { item, error in 46 | 47 | guard let data = item as? Data else { 48 | dump(error) 49 | return 50 | } 51 | 52 | guard let url = URL(dataRepresentation: data, relativeTo: nil) else { 53 | print("Error: Not a valid URL.") 54 | return 55 | } 56 | 57 | print("File was dropped: \(url.path)") 58 | } 59 | } 60 | 61 | return true 62 | } 63 | 64 | /// Tells the delegate a validated drop has entered the modified view. 65 | /// This function is optional. 66 | func dropEntered(info: DropInfo) { 67 | print("Drop entered!") 68 | } 69 | 70 | /// Called as a validated drop moves inside the onDrop modified view. 71 | /// 72 | /// Return a drop proposal that contains the operation the delegate intends 73 | /// to perform at the DropInfo.location, The default implementation returns 74 | /// nil, which tells the drop to use that last valid returned value or else 75 | /// .copy. 76 | /// 77 | /// This function is optional. 78 | func dropUpdated(info: DropInfo) -> DropProposal? { 79 | return nil 80 | } 81 | 82 | /// Tells the delegate a validated drop operation has exited the onDrop 83 | /// modified view. The default behavior does nothing. 84 | /// This function is optional. 85 | func dropExited(info: DropInfo) { 86 | print("Drop exited!") 87 | } 88 | } 89 | 90 | // MARK: - Preview 91 | 92 | struct MoreStuffPane_Previews: PreviewProvider { 93 | static var previews: some View { 94 | MoreStuffPane() 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /SidebarApp/Panes/Pane Helper/Pane.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct Pane: View { 4 | 5 | let content: () -> Content 6 | 7 | init(@ViewBuilder content: @escaping () -> Content) { 8 | self.content = content 9 | } 10 | 11 | var body: some View { 12 | content() 13 | .frame(maxWidth: .infinity, maxHeight: .infinity) 14 | .background(PaneBackground()) 15 | } 16 | } 17 | 18 | struct Pane_Previews: PreviewProvider { 19 | static var previews: some View { 20 | Pane { 21 | Text("Pane") 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /SidebarApp/Panes/Pane Helper/PaneBackground.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct PaneBackground: View { 4 | 5 | @Environment(\.colorScheme) var colorScheme 6 | 7 | var body: some View { 8 | if colorScheme == .dark { 9 | Color(.clear) 10 | .edgesIgnoringSafeArea(.all) 11 | } else { 12 | Color.white 13 | .edgesIgnoringSafeArea(.all) 14 | } 15 | } 16 | } 17 | 18 | struct PaneBackground_Previews: PreviewProvider { 19 | static var previews: some View { 20 | PaneBackground() 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /SidebarApp/Panes/WhatsUpPane.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct WhatsUpPane: View { 4 | 5 | let names = ["Holly", "Josh", "Rhonda", "Ted"] 6 | 7 | @State private var searchText = "" 8 | 9 | var body: some View { 10 | Pane { 11 | VStack(spacing: 10) { 12 | ForEach(searchResults, id: \.self) { name in 13 | Text("What's up, \(name)?") 14 | } 15 | } 16 | } 17 | .searchable( 18 | text: $searchText, 19 | placement: .toolbar, 20 | prompt: "Search", 21 | suggestions: { 22 | ForEach(searchResults, id: \.self) { result in 23 | Text(result) 24 | .searchCompletion(result) 25 | } 26 | }) 27 | .toolbar { 28 | ToolbarItem(placement: .automatic) { 29 | Button { 30 | print("Action!") 31 | } label: { 32 | Image(systemName: "square.and.arrow.up") 33 | } 34 | } 35 | ToolbarItem(placement: .automatic) { 36 | Button { 37 | print("Action!") 38 | } label: { 39 | Image(systemName: "trash") 40 | } 41 | } 42 | ToolbarItem(placement: .automatic) { 43 | Button { 44 | print("Action!") 45 | } label: { 46 | Image(systemName: "hammer") 47 | } 48 | } 49 | } 50 | .navigationSubtitle("What's Up?") 51 | } 52 | 53 | var searchResults: [String] { 54 | if searchText.isEmpty { 55 | return names 56 | } else { 57 | return names.filter { $0.contains(searchText) } 58 | } 59 | } 60 | } 61 | 62 | struct WhatsUpPane_Previews: PreviewProvider { 63 | static var previews: some View { 64 | WhatsUpPane() 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /SidebarApp/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /SidebarApp/Settings/GeneralSettingsView.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct GeneralSettingsTab: View { 4 | 5 | @AppStorage("settings.general.name") private var name: String = "" 6 | 7 | var body: some View { 8 | Form { 9 | Section { 10 | TextField("Name:", text: $name, onCommit: { 11 | print("On commit!") 12 | }) 13 | Text("This should describe the text field.") 14 | .font(.caption) 15 | Button("Apply", action: { 16 | print("Apply!") 17 | }) 18 | } 19 | } 20 | .padding(20) 21 | }} 22 | 23 | struct GeneralSettingsView_Previews: PreviewProvider { 24 | static var previews: some View { 25 | GeneralSettingsTab() 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /SidebarApp/Settings/SettingsWindow.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct SettingsWindow: View { 4 | 5 | private enum Tabs: Hashable { 6 | case general 7 | } 8 | 9 | var body: some View { 10 | TabView { 11 | GeneralSettingsTab() 12 | .tabItem { 13 | Label("General", systemImage: "gear") 14 | } 15 | .tag(Tabs.general) 16 | } 17 | .padding(20) 18 | .frame(width: 375, height: 150) 19 | } 20 | 21 | /// Show settings programmatically 22 | static func show() { 23 | NSApp.sendAction(Selector(("showPreferencesWindow:")), to: nil, from: nil) 24 | } 25 | } 26 | 27 | struct SettingsWindow_Previews: PreviewProvider { 28 | static var previews: some View { 29 | SettingsWindow() 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /SidebarApp/Sidebar/Sections/GeneralSidebarSection.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct GeneralSidebarSection: View { 4 | 5 | @Binding var selection: SidebarPane? 6 | 7 | var body: some View { 8 | 9 | Section(header: Text("General")) { 10 | 11 | NavigationLink { 12 | HelloWorldPane() 13 | } label: { 14 | Label("Hello, World!", systemImage: "text.bubble") 15 | } 16 | 17 | NavigationLink { 18 | WhatsUpPane() 19 | } label: { 20 | Label("What's Up?", systemImage: "questionmark.app.dashed") 21 | } 22 | } 23 | } 24 | } 25 | 26 | struct GeneralSidebarSection_Previews: PreviewProvider { 27 | static var previews: some View { 28 | GeneralSidebarSection(selection: .constant(.helloWorld)) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /SidebarApp/Sidebar/Sections/MoreSidebarSection.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct MoreSidebarSection: View { 4 | 5 | @Binding var selection: SidebarPane? 6 | 7 | var body: some View { 8 | 9 | Section(header: Text("More")) { 10 | 11 | NavigationLink { 12 | MoreStuffPane() 13 | } label: { 14 | Label("More Stuff", systemImage: "ellipsis.circle") 15 | } 16 | 17 | } 18 | } 19 | } 20 | 21 | struct MoreSidebarSection_Previews: PreviewProvider { 22 | static var previews: some View { 23 | MoreSidebarSection(selection: .constant(.moreStuff)) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /SidebarApp/Sidebar/Sidebar.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | // You may see the error message: 4 | // 5 | // `onChange(of: Bool) action tried to update multiple times per frame.` 6 | // 7 | // It seems to be a SwiftUI bug, as it can be reproduced with a minimal list. 8 | 9 | struct Sidebar: View { 10 | 11 | @State var selection: SidebarPane? = nil 12 | 13 | @State var searchText: String = "" 14 | 15 | var body: some View { 16 | List { 17 | GeneralSidebarSection(selection: $selection) 18 | MoreSidebarSection(selection: $selection) 19 | } 20 | .listStyle(SidebarListStyle()) 21 | .frame(minWidth: 180, idealWidth: 180, maxWidth: 300) 22 | .safeAreaInset(edge: .bottom, spacing: 0) { 23 | SidebarFooter() 24 | } 25 | .searchable(text: $searchText, placement: .sidebar) 26 | .toolbar { 27 | ToolbarItem { 28 | Button(action: toggleSidebar, label: { 29 | Image(systemName: "sidebar.left") 30 | }) 31 | } 32 | } 33 | } 34 | } 35 | 36 | private func toggleSidebar() { 37 | NSApp.keyWindow? 38 | .firstResponder? 39 | .tryToPerform(#selector(NSSplitViewController.toggleSidebar(_:)), with: nil) 40 | } 41 | 42 | struct Sidebar_Previews: PreviewProvider { 43 | static var previews: some View { 44 | Sidebar() 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /SidebarApp/Sidebar/SidebarFooter.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct SidebarFooter: View { 4 | var body: some View { 5 | VStack(spacing: 6) { 6 | Text("Sidebar Footer") 7 | .fontWeight(.medium) 8 | .foregroundColor(Color.white.opacity(0.6)) 9 | Text("You could put stuff here.") 10 | .font(.footnote) 11 | .foregroundColor(Color.black.opacity(0.4)) 12 | } 13 | .frame(height: 60) 14 | .frame(maxWidth: .infinity) 15 | .background(Color.gray.opacity(0.2)) 16 | .cornerRadius(8) 17 | .padding() 18 | } 19 | } 20 | 21 | struct SidebarFooter_Previews: PreviewProvider { 22 | static var previews: some View { 23 | SidebarFooter() 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /SidebarApp/Sidebar/SidebarPane.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | enum SidebarPane { 4 | 5 | // MARK: General Section 6 | 7 | case helloWorld 8 | case whatsUp 9 | 10 | // MARK: More Section 11 | 12 | case moreStuff 13 | } 14 | 15 | // MARK: - Protocol Conformances 16 | 17 | extension SidebarPane: Equatable, Identifiable { 18 | var id: Self { self } 19 | } 20 | -------------------------------------------------------------------------------- /SidebarApp/SidebarApp.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.files.user-selected.read-write 8 | 9 | com.apple.security.network.client 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /SidebarApp/SidebarApp.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | @main 4 | struct SidebarApp: App { 5 | 6 | /// Legacy app delegate. 7 | @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate 8 | 9 | var body: some Scene { 10 | MainScene() 11 | } 12 | } 13 | 14 | // MARK: - App Delegate 15 | 16 | class AppDelegate: NSObject, NSApplicationDelegate { 17 | 18 | var menuBarButton: MenuBarButton? 19 | 20 | func applicationDidFinishLaunching(_ notification: Notification) { 21 | menuBarButton = MenuBarButton() 22 | } 23 | 24 | func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { 25 | return true 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /SidebarApp/Utilities/AppKit Extensions/NSWindow+AlwaysOnTop.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import AppKit 3 | 4 | public extension NSWindow { 5 | 6 | var alwaysOnTop: Bool { 7 | get { 8 | return level.rawValue >= Int(CGWindowLevelForKey(CGWindowLevelKey.statusWindow)) 9 | } 10 | set { 11 | if newValue { 12 | makeKeyAndOrderFront(nil) 13 | level = NSWindow.Level(rawValue: Int(CGWindowLevelForKey(CGWindowLevelKey.statusWindow))) 14 | } else { 15 | level = NSWindow.Level(rawValue: Int(CGWindowLevelForKey(CGWindowLevelKey.normalWindow))) 16 | } 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /SidebarApp/Utilities/Bundle Extensions/Bundle+Copyright.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension Bundle { 4 | 5 | var copyright: String { 6 | func string(for key: String) -> String? { 7 | object(forInfoDictionaryKey: key) as? String 8 | } 9 | return string(for: "NSHumanReadableCopyright") ?? "N/A" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /SidebarApp/Utilities/Bundle Extensions/Bundle+Name.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension Bundle { 4 | 5 | var name: String { 6 | func string(for key: String) -> String? { 7 | object(forInfoDictionaryKey: key) as? String 8 | } 9 | return string(for: "CFBundleDisplayName") 10 | ?? string(for: "CFBundleName") 11 | ?? "N/A" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /SidebarApp/Utilities/Bundle Extensions/Bundle+Version.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension Bundle { 4 | 5 | var version: String { 6 | infoDictionary?["CFBundleShortVersionString"] as? String ?? "N/A" 7 | } 8 | 9 | var buildVersion: String { 10 | infoDictionary?["CFBundleVersion"] as? String ?? "N/A" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /SidebarApp/Utilities/WindowReflection.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | import AppKit 3 | 4 | /// The WindowReflection is a view that peeks behind the curtain and finds the underlying NSWindow. 5 | /// 6 | /// ``` 7 | /// .background(WindowReflection(window: $window)) 8 | /// ``` 9 | public struct WindowReflection: NSViewRepresentable { 10 | 11 | @Binding var window: NSWindow? 12 | 13 | public func makeNSView(context: Context) -> NSView { 14 | let view = NSView() 15 | DispatchQueue.main.async { 16 | self.window = view.window 17 | } 18 | return view 19 | } 20 | 21 | public func updateNSView(_ nsView: NSView, context: Context) {} 22 | } 23 | --------------------------------------------------------------------------------