├── .gitignore ├── README.md ├── SGWindowLayout.xcodeproj ├── project.pbxproj └── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ └── IDEWorkspaceChecks.plist └── SGWindowLayout ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets ├── AppIcon.appiconset │ ├── Contents.json │ ├── icon_128x128.png │ ├── icon_128x128@2x.png │ ├── icon_16x16.png │ ├── icon_16x16@2x.png │ ├── icon_256x256.png │ ├── icon_256x256@2x.png │ ├── icon_32x32.png │ ├── icon_32x32@2x.png │ ├── icon_512x512.png │ └── icon_512x512@2x.png ├── Contents.json └── logo.imageset │ ├── Contents.json │ ├── icon_16x16.png │ └── logo@2x.png ├── Base.lproj └── MainMenu.xib ├── Core ├── SGWLCursor.h ├── SGWLCursor.m ├── SGWLHotKey.h ├── SGWLHotKey.m ├── SGWLLayout.h ├── SGWLLayout.m ├── SGWLLogin.h ├── SGWLLogin.m ├── SGWLPoint.h ├── SGWLPoint.m ├── SGWLScreen.h └── SGWLScreen.m ├── Info.plist ├── Vendors └── PTHotKey │ ├── PTHotKey.h │ ├── PTHotKey.m │ ├── PTHotKeyCenter.h │ ├── PTHotKeyCenter.m │ ├── PTKeyCombo.h │ └── PTKeyCombo.m └── main.m /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | *.pbxuser 3 | !default.pbxuser 4 | *.mode1v3 5 | !default.mode1v3 6 | *.mode2v3 7 | !default.mode2v3 8 | *.perspectivev3 9 | !default.perspectivev3 10 | xcuserdata 11 | *.xccheckout 12 | *.moved-aside 13 | DerivedData 14 | *.hmap 15 | *.ipa 16 | *.xcuserstate 17 | .DS_Store 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Logo](https://github.com/libobjc/resource/blob/master/SGWindowLayout/SGWindowLayout-logo.png?raw=true) 2 | 3 | # SGWindowLayout 4 | 5 | SGWindowLayout 是一款 macOS 窗口布局小应用。 6 | 7 | ## 功能特点 8 | 9 | - 通过快捷键,快速布局焦点窗口的位置及大小。 10 | - 可自定义快捷键及布局方式。 11 | - 可设置开机自动启动。 12 | 13 | ## 使用示例 14 | 15 | 默认绑定了如下快捷键 16 | 17 | - Control + A -> 铺满屏幕左半部分 18 | - Control + S -> 全屏 19 | - Control + D -> 铺满屏幕左右部分 20 | - Control + W -> 铺满屏幕上半部分 21 | - Control + X -> 铺满屏幕下半部分 22 | 23 | 如需更改,可在 AppDelegate 的 registerHotKey 方法中更改。例如: 24 | 25 | ```obj-c 26 | // 使用 control + a 将当前焦点窗口铺满屏幕左半部分。 27 | [SGWLHotKey registerLayoutAttribute:SGWLLayoutAttributeLeft keyCode:SGWLKeyCodeA modifiers:SGWLModifiersKeyControl]; 28 | ``` 29 | 30 | ## 效果演示 31 | 32 | ![Example](https://github.com/libobjc/resource/blob/master/SGWindowLayout/SGWindowLayout-example.gif?raw=true) 33 | 34 | 35 | ## 注意事项 36 | 37 | - 如果出现在 Xcode 中直接 run 无响应的情况。可以先 build,再将 Products 下的 App 拷贝到 Applications 下进行使用。 38 | -------------------------------------------------------------------------------- /SGWindowLayout.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 9C27B1622101D86C00998E95 /* SGWLScreen.m in Sources */ = {isa = PBXBuildFile; fileRef = 9C27B1612101D86C00998E95 /* SGWLScreen.m */; }; 11 | 9C27B1652101DAA900998E95 /* SGWLPoint.m in Sources */ = {isa = PBXBuildFile; fileRef = 9C27B1642101DAA900998E95 /* SGWLPoint.m */; }; 12 | 9C336D7D1DE2D4D300193A8F /* SGWLLogin.m in Sources */ = {isa = PBXBuildFile; fileRef = 9C336D7C1DE2D4D300193A8F /* SGWLLogin.m */; }; 13 | 9C64AFDB1DE295CC008E0AC4 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 9C64AFDA1DE295CC008E0AC4 /* AppDelegate.m */; }; 14 | 9C64AFDE1DE295CC008E0AC4 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 9C64AFDD1DE295CC008E0AC4 /* main.m */; }; 15 | 9C64AFE31DE295CC008E0AC4 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9C64AFE11DE295CC008E0AC4 /* MainMenu.xib */; }; 16 | 9C64AFED1DE29624008E0AC4 /* SGWLLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 9C64AFEC1DE29624008E0AC4 /* SGWLLayout.m */; }; 17 | 9C64AFFC1DE29AE6008E0AC4 /* SGWLHotKey.m in Sources */ = {isa = PBXBuildFile; fileRef = 9C64AFFB1DE29AE6008E0AC4 /* SGWLHotKey.m */; }; 18 | 9CB78BB42101BF0000DCE0AC /* SGWLCursor.m in Sources */ = {isa = PBXBuildFile; fileRef = 9CB78BB32101BF0000DCE0AC /* SGWLCursor.m */; }; 19 | 9CBD4C381EAAED5500FDD330 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9C64AFDF1DE295CC008E0AC4 /* Assets.xcassets */; }; 20 | 9CBD4C411EAAEE6900FDD330 /* PTHotKey.m in Sources */ = {isa = PBXBuildFile; fileRef = 9CBD4C3C1EAAEE6900FDD330 /* PTHotKey.m */; }; 21 | 9CBD4C421EAAEE6900FDD330 /* PTHotKeyCenter.m in Sources */ = {isa = PBXBuildFile; fileRef = 9CBD4C3E1EAAEE6900FDD330 /* PTHotKeyCenter.m */; }; 22 | 9CBD4C431EAAEE6900FDD330 /* PTKeyCombo.m in Sources */ = {isa = PBXBuildFile; fileRef = 9CBD4C401EAAEE6900FDD330 /* PTKeyCombo.m */; }; 23 | /* End PBXBuildFile section */ 24 | 25 | /* Begin PBXFileReference section */ 26 | 9C27B1602101D86C00998E95 /* SGWLScreen.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SGWLScreen.h; sourceTree = ""; }; 27 | 9C27B1612101D86C00998E95 /* SGWLScreen.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SGWLScreen.m; sourceTree = ""; }; 28 | 9C27B1632101DAA900998E95 /* SGWLPoint.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SGWLPoint.h; sourceTree = ""; }; 29 | 9C27B1642101DAA900998E95 /* SGWLPoint.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SGWLPoint.m; sourceTree = ""; }; 30 | 9C336D7B1DE2D4D300193A8F /* SGWLLogin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SGWLLogin.h; sourceTree = ""; }; 31 | 9C336D7C1DE2D4D300193A8F /* SGWLLogin.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SGWLLogin.m; sourceTree = ""; }; 32 | 9C64AFD61DE295CC008E0AC4 /* SGWindowLayout.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SGWindowLayout.app; sourceTree = BUILT_PRODUCTS_DIR; }; 33 | 9C64AFD91DE295CC008E0AC4 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 34 | 9C64AFDA1DE295CC008E0AC4 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 35 | 9C64AFDD1DE295CC008E0AC4 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 36 | 9C64AFDF1DE295CC008E0AC4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 37 | 9C64AFE21DE295CC008E0AC4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; 38 | 9C64AFE41DE295CC008E0AC4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 39 | 9C64AFEB1DE29624008E0AC4 /* SGWLLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SGWLLayout.h; sourceTree = ""; }; 40 | 9C64AFEC1DE29624008E0AC4 /* SGWLLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SGWLLayout.m; sourceTree = ""; }; 41 | 9C64AFFA1DE29AE6008E0AC4 /* SGWLHotKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SGWLHotKey.h; sourceTree = ""; }; 42 | 9C64AFFB1DE29AE6008E0AC4 /* SGWLHotKey.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SGWLHotKey.m; sourceTree = ""; }; 43 | 9CB78BB22101BF0000DCE0AC /* SGWLCursor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SGWLCursor.h; sourceTree = ""; }; 44 | 9CB78BB32101BF0000DCE0AC /* SGWLCursor.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SGWLCursor.m; sourceTree = ""; }; 45 | 9CBD4C3B1EAAEE6900FDD330 /* PTHotKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PTHotKey.h; sourceTree = ""; }; 46 | 9CBD4C3C1EAAEE6900FDD330 /* PTHotKey.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PTHotKey.m; sourceTree = ""; }; 47 | 9CBD4C3D1EAAEE6900FDD330 /* PTHotKeyCenter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PTHotKeyCenter.h; sourceTree = ""; }; 48 | 9CBD4C3E1EAAEE6900FDD330 /* PTHotKeyCenter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PTHotKeyCenter.m; sourceTree = ""; }; 49 | 9CBD4C3F1EAAEE6900FDD330 /* PTKeyCombo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PTKeyCombo.h; sourceTree = ""; }; 50 | 9CBD4C401EAAEE6900FDD330 /* PTKeyCombo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PTKeyCombo.m; sourceTree = ""; }; 51 | /* End PBXFileReference section */ 52 | 53 | /* Begin PBXFrameworksBuildPhase section */ 54 | 9C64AFD31DE295CC008E0AC4 /* Frameworks */ = { 55 | isa = PBXFrameworksBuildPhase; 56 | buildActionMask = 2147483647; 57 | files = ( 58 | ); 59 | runOnlyForDeploymentPostprocessing = 0; 60 | }; 61 | /* End PBXFrameworksBuildPhase section */ 62 | 63 | /* Begin PBXGroup section */ 64 | 9C64AFCD1DE295CC008E0AC4 = { 65 | isa = PBXGroup; 66 | children = ( 67 | 9C64AFD81DE295CC008E0AC4 /* SGWindowLayout */, 68 | 9C64AFD71DE295CC008E0AC4 /* Products */, 69 | ); 70 | sourceTree = ""; 71 | }; 72 | 9C64AFD71DE295CC008E0AC4 /* Products */ = { 73 | isa = PBXGroup; 74 | children = ( 75 | 9C64AFD61DE295CC008E0AC4 /* SGWindowLayout.app */, 76 | ); 77 | name = Products; 78 | sourceTree = ""; 79 | }; 80 | 9C64AFD81DE295CC008E0AC4 /* SGWindowLayout */ = { 81 | isa = PBXGroup; 82 | children = ( 83 | 9CBD4C391EAAEE6900FDD330 /* Vendors */, 84 | 9C64AFEA1DE29614008E0AC4 /* Core */, 85 | 9C64AFD91DE295CC008E0AC4 /* AppDelegate.h */, 86 | 9C64AFDA1DE295CC008E0AC4 /* AppDelegate.m */, 87 | 9C64AFE11DE295CC008E0AC4 /* MainMenu.xib */, 88 | 9C64AFDC1DE295CC008E0AC4 /* Supporting Files */, 89 | ); 90 | path = SGWindowLayout; 91 | sourceTree = ""; 92 | }; 93 | 9C64AFDC1DE295CC008E0AC4 /* Supporting Files */ = { 94 | isa = PBXGroup; 95 | children = ( 96 | 9C64AFDF1DE295CC008E0AC4 /* Assets.xcassets */, 97 | 9C64AFE41DE295CC008E0AC4 /* Info.plist */, 98 | 9C64AFDD1DE295CC008E0AC4 /* main.m */, 99 | ); 100 | name = "Supporting Files"; 101 | sourceTree = ""; 102 | }; 103 | 9C64AFEA1DE29614008E0AC4 /* Core */ = { 104 | isa = PBXGroup; 105 | children = ( 106 | 9C64AFEB1DE29624008E0AC4 /* SGWLLayout.h */, 107 | 9C64AFEC1DE29624008E0AC4 /* SGWLLayout.m */, 108 | 9C64AFFA1DE29AE6008E0AC4 /* SGWLHotKey.h */, 109 | 9C64AFFB1DE29AE6008E0AC4 /* SGWLHotKey.m */, 110 | 9C336D7B1DE2D4D300193A8F /* SGWLLogin.h */, 111 | 9C336D7C1DE2D4D300193A8F /* SGWLLogin.m */, 112 | 9CB78BB22101BF0000DCE0AC /* SGWLCursor.h */, 113 | 9CB78BB32101BF0000DCE0AC /* SGWLCursor.m */, 114 | 9C27B1602101D86C00998E95 /* SGWLScreen.h */, 115 | 9C27B1612101D86C00998E95 /* SGWLScreen.m */, 116 | 9C27B1632101DAA900998E95 /* SGWLPoint.h */, 117 | 9C27B1642101DAA900998E95 /* SGWLPoint.m */, 118 | ); 119 | path = Core; 120 | sourceTree = ""; 121 | }; 122 | 9CBD4C391EAAEE6900FDD330 /* Vendors */ = { 123 | isa = PBXGroup; 124 | children = ( 125 | 9CBD4C3A1EAAEE6900FDD330 /* PTHotKey */, 126 | ); 127 | path = Vendors; 128 | sourceTree = ""; 129 | }; 130 | 9CBD4C3A1EAAEE6900FDD330 /* PTHotKey */ = { 131 | isa = PBXGroup; 132 | children = ( 133 | 9CBD4C3B1EAAEE6900FDD330 /* PTHotKey.h */, 134 | 9CBD4C3C1EAAEE6900FDD330 /* PTHotKey.m */, 135 | 9CBD4C3D1EAAEE6900FDD330 /* PTHotKeyCenter.h */, 136 | 9CBD4C3E1EAAEE6900FDD330 /* PTHotKeyCenter.m */, 137 | 9CBD4C3F1EAAEE6900FDD330 /* PTKeyCombo.h */, 138 | 9CBD4C401EAAEE6900FDD330 /* PTKeyCombo.m */, 139 | ); 140 | path = PTHotKey; 141 | sourceTree = ""; 142 | }; 143 | /* End PBXGroup section */ 144 | 145 | /* Begin PBXNativeTarget section */ 146 | 9C64AFD51DE295CC008E0AC4 /* SGWindowLayout */ = { 147 | isa = PBXNativeTarget; 148 | buildConfigurationList = 9C64AFE71DE295CC008E0AC4 /* Build configuration list for PBXNativeTarget "SGWindowLayout" */; 149 | buildPhases = ( 150 | 9C64AFD21DE295CC008E0AC4 /* Sources */, 151 | 9C64AFD31DE295CC008E0AC4 /* Frameworks */, 152 | 9C64AFD41DE295CC008E0AC4 /* Resources */, 153 | ); 154 | buildRules = ( 155 | ); 156 | dependencies = ( 157 | ); 158 | name = SGWindowLayout; 159 | productName = WindowLayout; 160 | productReference = 9C64AFD61DE295CC008E0AC4 /* SGWindowLayout.app */; 161 | productType = "com.apple.product-type.application"; 162 | }; 163 | /* End PBXNativeTarget section */ 164 | 165 | /* Begin PBXProject section */ 166 | 9C64AFCE1DE295CC008E0AC4 /* Project object */ = { 167 | isa = PBXProject; 168 | attributes = { 169 | LastUpgradeCheck = 0940; 170 | ORGANIZATIONNAME = single; 171 | TargetAttributes = { 172 | 9C64AFD51DE295CC008E0AC4 = { 173 | CreatedOnToolsVersion = 8.0; 174 | DevelopmentTeam = 79WDBYQ4TC; 175 | ProvisioningStyle = Automatic; 176 | }; 177 | }; 178 | }; 179 | buildConfigurationList = 9C64AFD11DE295CC008E0AC4 /* Build configuration list for PBXProject "SGWindowLayout" */; 180 | compatibilityVersion = "Xcode 3.2"; 181 | developmentRegion = English; 182 | hasScannedForEncodings = 0; 183 | knownRegions = ( 184 | en, 185 | Base, 186 | ); 187 | mainGroup = 9C64AFCD1DE295CC008E0AC4; 188 | productRefGroup = 9C64AFD71DE295CC008E0AC4 /* Products */; 189 | projectDirPath = ""; 190 | projectRoot = ""; 191 | targets = ( 192 | 9C64AFD51DE295CC008E0AC4 /* SGWindowLayout */, 193 | ); 194 | }; 195 | /* End PBXProject section */ 196 | 197 | /* Begin PBXResourcesBuildPhase section */ 198 | 9C64AFD41DE295CC008E0AC4 /* Resources */ = { 199 | isa = PBXResourcesBuildPhase; 200 | buildActionMask = 2147483647; 201 | files = ( 202 | 9CBD4C381EAAED5500FDD330 /* Assets.xcassets in Resources */, 203 | 9C64AFE31DE295CC008E0AC4 /* MainMenu.xib in Resources */, 204 | ); 205 | runOnlyForDeploymentPostprocessing = 0; 206 | }; 207 | /* End PBXResourcesBuildPhase section */ 208 | 209 | /* Begin PBXSourcesBuildPhase section */ 210 | 9C64AFD21DE295CC008E0AC4 /* Sources */ = { 211 | isa = PBXSourcesBuildPhase; 212 | buildActionMask = 2147483647; 213 | files = ( 214 | 9C64AFED1DE29624008E0AC4 /* SGWLLayout.m in Sources */, 215 | 9CBD4C411EAAEE6900FDD330 /* PTHotKey.m in Sources */, 216 | 9C64AFFC1DE29AE6008E0AC4 /* SGWLHotKey.m in Sources */, 217 | 9CBD4C421EAAEE6900FDD330 /* PTHotKeyCenter.m in Sources */, 218 | 9C27B1652101DAA900998E95 /* SGWLPoint.m in Sources */, 219 | 9CBD4C431EAAEE6900FDD330 /* PTKeyCombo.m in Sources */, 220 | 9CB78BB42101BF0000DCE0AC /* SGWLCursor.m in Sources */, 221 | 9C336D7D1DE2D4D300193A8F /* SGWLLogin.m in Sources */, 222 | 9C64AFDE1DE295CC008E0AC4 /* main.m in Sources */, 223 | 9C27B1622101D86C00998E95 /* SGWLScreen.m in Sources */, 224 | 9C64AFDB1DE295CC008E0AC4 /* AppDelegate.m in Sources */, 225 | ); 226 | runOnlyForDeploymentPostprocessing = 0; 227 | }; 228 | /* End PBXSourcesBuildPhase section */ 229 | 230 | /* Begin PBXVariantGroup section */ 231 | 9C64AFE11DE295CC008E0AC4 /* MainMenu.xib */ = { 232 | isa = PBXVariantGroup; 233 | children = ( 234 | 9C64AFE21DE295CC008E0AC4 /* Base */, 235 | ); 236 | name = MainMenu.xib; 237 | sourceTree = ""; 238 | }; 239 | /* End PBXVariantGroup section */ 240 | 241 | /* Begin XCBuildConfiguration section */ 242 | 9C64AFE51DE295CC008E0AC4 /* Debug */ = { 243 | isa = XCBuildConfiguration; 244 | buildSettings = { 245 | ALWAYS_SEARCH_USER_PATHS = NO; 246 | CLANG_ANALYZER_NONNULL = YES; 247 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 248 | CLANG_CXX_LIBRARY = "libc++"; 249 | CLANG_ENABLE_MODULES = YES; 250 | CLANG_ENABLE_OBJC_ARC = YES; 251 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 252 | CLANG_WARN_BOOL_CONVERSION = YES; 253 | CLANG_WARN_COMMA = YES; 254 | CLANG_WARN_CONSTANT_CONVERSION = YES; 255 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 256 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 257 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 258 | CLANG_WARN_EMPTY_BODY = YES; 259 | CLANG_WARN_ENUM_CONVERSION = YES; 260 | CLANG_WARN_INFINITE_RECURSION = YES; 261 | CLANG_WARN_INT_CONVERSION = YES; 262 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 263 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 264 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 265 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 266 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 267 | CLANG_WARN_STRICT_PROTOTYPES = YES; 268 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 269 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 270 | CLANG_WARN_UNREACHABLE_CODE = YES; 271 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 272 | CODE_SIGN_IDENTITY = "-"; 273 | COPY_PHASE_STRIP = NO; 274 | DEBUG_INFORMATION_FORMAT = dwarf; 275 | ENABLE_STRICT_OBJC_MSGSEND = YES; 276 | ENABLE_TESTABILITY = YES; 277 | GCC_C_LANGUAGE_STANDARD = gnu99; 278 | GCC_DYNAMIC_NO_PIC = NO; 279 | GCC_NO_COMMON_BLOCKS = YES; 280 | GCC_OPTIMIZATION_LEVEL = 0; 281 | GCC_PREPROCESSOR_DEFINITIONS = ( 282 | "DEBUG=1", 283 | "$(inherited)", 284 | ); 285 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 286 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 287 | GCC_WARN_UNDECLARED_SELECTOR = YES; 288 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 289 | GCC_WARN_UNUSED_FUNCTION = YES; 290 | GCC_WARN_UNUSED_VARIABLE = YES; 291 | MACOSX_DEPLOYMENT_TARGET = 10.12; 292 | MTL_ENABLE_DEBUG_INFO = YES; 293 | ONLY_ACTIVE_ARCH = YES; 294 | SDKROOT = macosx; 295 | }; 296 | name = Debug; 297 | }; 298 | 9C64AFE61DE295CC008E0AC4 /* Release */ = { 299 | isa = XCBuildConfiguration; 300 | buildSettings = { 301 | ALWAYS_SEARCH_USER_PATHS = NO; 302 | CLANG_ANALYZER_NONNULL = YES; 303 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 304 | CLANG_CXX_LIBRARY = "libc++"; 305 | CLANG_ENABLE_MODULES = YES; 306 | CLANG_ENABLE_OBJC_ARC = YES; 307 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 308 | CLANG_WARN_BOOL_CONVERSION = YES; 309 | CLANG_WARN_COMMA = YES; 310 | CLANG_WARN_CONSTANT_CONVERSION = YES; 311 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 312 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 313 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 314 | CLANG_WARN_EMPTY_BODY = YES; 315 | CLANG_WARN_ENUM_CONVERSION = YES; 316 | CLANG_WARN_INFINITE_RECURSION = YES; 317 | CLANG_WARN_INT_CONVERSION = YES; 318 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 319 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 320 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 321 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 322 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 323 | CLANG_WARN_STRICT_PROTOTYPES = YES; 324 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 325 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 326 | CLANG_WARN_UNREACHABLE_CODE = YES; 327 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 328 | CODE_SIGN_IDENTITY = "-"; 329 | COPY_PHASE_STRIP = NO; 330 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 331 | ENABLE_NS_ASSERTIONS = NO; 332 | ENABLE_STRICT_OBJC_MSGSEND = YES; 333 | GCC_C_LANGUAGE_STANDARD = gnu99; 334 | GCC_NO_COMMON_BLOCKS = YES; 335 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 336 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 337 | GCC_WARN_UNDECLARED_SELECTOR = YES; 338 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 339 | GCC_WARN_UNUSED_FUNCTION = YES; 340 | GCC_WARN_UNUSED_VARIABLE = YES; 341 | MACOSX_DEPLOYMENT_TARGET = 10.12; 342 | MTL_ENABLE_DEBUG_INFO = NO; 343 | SDKROOT = macosx; 344 | }; 345 | name = Release; 346 | }; 347 | 9C64AFE81DE295CC008E0AC4 /* Debug */ = { 348 | isa = XCBuildConfiguration; 349 | buildSettings = { 350 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 351 | COMBINE_HIDPI_IMAGES = YES; 352 | DEVELOPMENT_TEAM = 79WDBYQ4TC; 353 | INFOPLIST_FILE = "$(SRCROOT)/SGWindowLayout/Info.plist"; 354 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 355 | PRODUCT_BUNDLE_IDENTIFIER = com.libobjc.SGWindowLayout; 356 | PRODUCT_NAME = "$(TARGET_NAME)"; 357 | }; 358 | name = Debug; 359 | }; 360 | 9C64AFE91DE295CC008E0AC4 /* Release */ = { 361 | isa = XCBuildConfiguration; 362 | buildSettings = { 363 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 364 | COMBINE_HIDPI_IMAGES = YES; 365 | DEVELOPMENT_TEAM = 79WDBYQ4TC; 366 | INFOPLIST_FILE = "$(SRCROOT)/SGWindowLayout/Info.plist"; 367 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 368 | PRODUCT_BUNDLE_IDENTIFIER = com.libobjc.SGWindowLayout; 369 | PRODUCT_NAME = "$(TARGET_NAME)"; 370 | }; 371 | name = Release; 372 | }; 373 | /* End XCBuildConfiguration section */ 374 | 375 | /* Begin XCConfigurationList section */ 376 | 9C64AFD11DE295CC008E0AC4 /* Build configuration list for PBXProject "SGWindowLayout" */ = { 377 | isa = XCConfigurationList; 378 | buildConfigurations = ( 379 | 9C64AFE51DE295CC008E0AC4 /* Debug */, 380 | 9C64AFE61DE295CC008E0AC4 /* Release */, 381 | ); 382 | defaultConfigurationIsVisible = 0; 383 | defaultConfigurationName = Release; 384 | }; 385 | 9C64AFE71DE295CC008E0AC4 /* Build configuration list for PBXNativeTarget "SGWindowLayout" */ = { 386 | isa = XCConfigurationList; 387 | buildConfigurations = ( 388 | 9C64AFE81DE295CC008E0AC4 /* Debug */, 389 | 9C64AFE91DE295CC008E0AC4 /* Release */, 390 | ); 391 | defaultConfigurationIsVisible = 0; 392 | defaultConfigurationName = Release; 393 | }; 394 | /* End XCConfigurationList section */ 395 | }; 396 | rootObject = 9C64AFCE1DE295CC008E0AC4 /* Project object */; 397 | } 398 | -------------------------------------------------------------------------------- /SGWindowLayout.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SGWindowLayout.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /SGWindowLayout/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // WindowLayout 4 | // 5 | // Created by Single on 2016/11/21. 6 | // Copyright © 2016年 single. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : NSObject 12 | 13 | @end 14 | 15 | -------------------------------------------------------------------------------- /SGWindowLayout/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // WindowLayout 4 | // 5 | // Created by Single on 2016/11/21. 6 | // Copyright © 2016年 single. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | #import "SGWLHotKey.h" 11 | #import "SGWLLayout.h" 12 | #import "SGWLCursor.h" 13 | #import "SGWLLogin.h" 14 | 15 | @interface AppDelegate () 16 | 17 | @property (nonatomic, strong) NSStatusItem * statusItem; 18 | @property (weak) IBOutlet NSMenu *statusMenu; 19 | 20 | @end 21 | 22 | @implementation AppDelegate 23 | 24 | - (void)applicationDidFinishLaunching:(NSNotification *)aNotification 25 | { 26 | self.statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:22]; 27 | self.statusItem.image = [NSImage imageNamed:@"logo"]; 28 | self.statusItem.menu = self.statusMenu; 29 | NSMenuItem * item = [self.statusMenu itemAtIndex:0]; 30 | item.state = [SGWLLogin login].startAtLogin; 31 | 32 | [self registerHotKey]; 33 | } 34 | 35 | - (void)registerHotKey 36 | { 37 | [SGWLHotKey registerKeyCode:SGWLKeyCodeQ modifiers:SGWLModifiersKeyControl handler:^{ 38 | [SGWLLayout layoutCurrentFocusedWindowWithLayoutAttribute:SGWLLayoutAttributeLeft]; 39 | }]; 40 | [SGWLHotKey registerKeyCode:SGWLKeyCodeW modifiers:SGWLModifiersKeyControl handler:^{ 41 | [SGWLLayout layoutCurrentFocusedWindowWithLayoutAttribute:SGWLLayoutAttributeFull]; 42 | }]; 43 | [SGWLHotKey registerKeyCode:SGWLKeyCodeE modifiers:SGWLModifiersKeyControl handler:^{ 44 | [SGWLLayout layoutCurrentFocusedWindowWithLayoutAttribute:SGWLLayoutAttributeRight]; 45 | }]; 46 | [SGWLHotKey registerKeyCode:SGWLKeyCodeS modifiers:SGWLModifiersKeyControl handler:^{ 47 | [SGWLLayout layoutCurrentFocusedWindowWithLayoutAttribute:SGWLLayoutAttributeTwoThirdsFourThirdsCenter]; 48 | }]; 49 | [SGWLHotKey registerKeyCode:SGWLKeyCodeX modifiers:SGWLModifiersKeyControl handler:^{ 50 | NSScreen * screen = [SGWLLayout swapScreenIfNeeded]; 51 | [SGWLCursor swapCursorToScreenIfNeeded:screen]; 52 | }]; 53 | [SGWLHotKey registerKeyCode:SGWLKeyCodeZ modifiers:SGWLModifiersKeyControl handler:^{ 54 | [SGWLCursor swapCursorIfNeeded]; 55 | }]; 56 | } 57 | 58 | - (void)applicationWillTerminate:(NSNotification *)aNotification 59 | { 60 | [[NSStatusBar systemStatusBar] removeStatusItem:self.statusItem]; 61 | } 62 | 63 | - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender 64 | { 65 | return YES; 66 | } 67 | 68 | - (IBAction)quitAction:(id)sender 69 | { 70 | [[NSApplication sharedApplication] terminate:self]; 71 | } 72 | 73 | - (IBAction)startAtLogin:(NSMenuItem *)menuItem 74 | { 75 | menuItem.state = !menuItem.state; 76 | [SGWLLogin login].startAtLogin = menuItem.state; 77 | } 78 | 79 | @end 80 | -------------------------------------------------------------------------------- /SGWindowLayout/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "16x16", 5 | "idiom" : "mac", 6 | "filename" : "icon_16x16.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "16x16", 11 | "idiom" : "mac", 12 | "filename" : "icon_16x16@2x.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "32x32", 17 | "idiom" : "mac", 18 | "filename" : "icon_32x32.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "32x32", 23 | "idiom" : "mac", 24 | "filename" : "icon_32x32@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "128x128", 29 | "idiom" : "mac", 30 | "filename" : "icon_128x128.png", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "size" : "128x128", 35 | "idiom" : "mac", 36 | "filename" : "icon_128x128@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "256x256", 41 | "idiom" : "mac", 42 | "filename" : "icon_256x256.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "256x256", 47 | "idiom" : "mac", 48 | "filename" : "icon_256x256@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "512x512", 53 | "idiom" : "mac", 54 | "filename" : "icon_512x512.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "512x512", 59 | "idiom" : "mac", 60 | "filename" : "icon_512x512@2x.png", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /SGWindowLayout/Assets.xcassets/AppIcon.appiconset/icon_128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/libobjc/SGWindowLayout/0516b77f9e835d2776df321ee20f430eeb26197f/SGWindowLayout/Assets.xcassets/AppIcon.appiconset/icon_128x128.png -------------------------------------------------------------------------------- /SGWindowLayout/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/libobjc/SGWindowLayout/0516b77f9e835d2776df321ee20f430eeb26197f/SGWindowLayout/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png -------------------------------------------------------------------------------- /SGWindowLayout/Assets.xcassets/AppIcon.appiconset/icon_16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/libobjc/SGWindowLayout/0516b77f9e835d2776df321ee20f430eeb26197f/SGWindowLayout/Assets.xcassets/AppIcon.appiconset/icon_16x16.png -------------------------------------------------------------------------------- /SGWindowLayout/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/libobjc/SGWindowLayout/0516b77f9e835d2776df321ee20f430eeb26197f/SGWindowLayout/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png -------------------------------------------------------------------------------- /SGWindowLayout/Assets.xcassets/AppIcon.appiconset/icon_256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/libobjc/SGWindowLayout/0516b77f9e835d2776df321ee20f430eeb26197f/SGWindowLayout/Assets.xcassets/AppIcon.appiconset/icon_256x256.png -------------------------------------------------------------------------------- /SGWindowLayout/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/libobjc/SGWindowLayout/0516b77f9e835d2776df321ee20f430eeb26197f/SGWindowLayout/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png -------------------------------------------------------------------------------- /SGWindowLayout/Assets.xcassets/AppIcon.appiconset/icon_32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/libobjc/SGWindowLayout/0516b77f9e835d2776df321ee20f430eeb26197f/SGWindowLayout/Assets.xcassets/AppIcon.appiconset/icon_32x32.png -------------------------------------------------------------------------------- /SGWindowLayout/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/libobjc/SGWindowLayout/0516b77f9e835d2776df321ee20f430eeb26197f/SGWindowLayout/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png -------------------------------------------------------------------------------- /SGWindowLayout/Assets.xcassets/AppIcon.appiconset/icon_512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/libobjc/SGWindowLayout/0516b77f9e835d2776df321ee20f430eeb26197f/SGWindowLayout/Assets.xcassets/AppIcon.appiconset/icon_512x512.png -------------------------------------------------------------------------------- /SGWindowLayout/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/libobjc/SGWindowLayout/0516b77f9e835d2776df321ee20f430eeb26197f/SGWindowLayout/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png -------------------------------------------------------------------------------- /SGWindowLayout/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /SGWindowLayout/Assets.xcassets/logo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icon_16x16.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "logo@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /SGWindowLayout/Assets.xcassets/logo.imageset/icon_16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/libobjc/SGWindowLayout/0516b77f9e835d2776df321ee20f430eeb26197f/SGWindowLayout/Assets.xcassets/logo.imageset/icon_16x16.png -------------------------------------------------------------------------------- /SGWindowLayout/Assets.xcassets/logo.imageset/logo@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/libobjc/SGWindowLayout/0516b77f9e835d2776df321ee20f430eeb26197f/SGWindowLayout/Assets.xcassets/logo.imageset/logo@2x.png -------------------------------------------------------------------------------- /SGWindowLayout/Base.lproj/MainMenu.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /SGWindowLayout/Core/SGWLCursor.h: -------------------------------------------------------------------------------- 1 | // 2 | // SGWLCursor.h 3 | // SGWindowLayout 4 | // 5 | // Created by Single on 2018/7/20. 6 | // Copyright © 2018年 single. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SGWLCursor : NSObject 12 | 13 | + (NSScreen *)swapCursorIfNeeded; 14 | + (BOOL)swapCursorToScreenIfNeeded:(NSScreen *)screen; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /SGWindowLayout/Core/SGWLCursor.m: -------------------------------------------------------------------------------- 1 | // 2 | // SGWLCursor.m 3 | // SGWindowLayout 4 | // 5 | // Created by Single on 2018/7/20. 6 | // Copyright © 2018年 single. All rights reserved. 7 | // 8 | 9 | #import "SGWLCursor.h" 10 | #import "SGWLScreen.h" 11 | #import "SGWLPoint.h" 12 | 13 | @interface SGWLCursor () 14 | 15 | @property (nonatomic, assign) NSInteger index; 16 | @property (nonatomic, strong) NSScreen * mainScreen; 17 | 18 | @end 19 | 20 | @implementation SGWLCursor 21 | 22 | + (instancetype)sharedInstance 23 | { 24 | static SGWLCursor * obj = nil; 25 | static dispatch_once_t onceToken; 26 | dispatch_once(&onceToken, ^{ 27 | obj = [[SGWLCursor alloc] init]; 28 | }); 29 | return obj; 30 | } 31 | 32 | + (NSScreen *)swapCursorIfNeeded 33 | { 34 | return [[self sharedInstance] swapCursorIfNeeded]; 35 | } 36 | 37 | + (BOOL)swapCursorToScreenIfNeeded:(NSScreen *)screen 38 | { 39 | return [[self sharedInstance] swapCursorToScreenIfNeeded:screen]; 40 | } 41 | 42 | - (NSScreen *)swapCursorIfNeeded 43 | { 44 | if ([NSScreen screens].count <= 1) 45 | { 46 | return nil; 47 | } 48 | CGPoint point = [SGWLPoint mouseLocation]; 49 | NSScreen * screen = [SGWLScreen nextScreenWithPoint:point]; 50 | [self moveToScreen:screen]; 51 | return screen; 52 | } 53 | 54 | - (BOOL)swapCursorToScreenIfNeeded:(NSScreen *)screen 55 | { 56 | if (!screen) 57 | { 58 | return NO; 59 | } 60 | CGPoint mousePoint = [SGWLPoint mouseLocation]; 61 | NSScreen * mouseScreen = [SGWLScreen screenWithPoint:mousePoint]; 62 | if (screen != mouseScreen) 63 | { 64 | return [self moveToScreen:screen]; 65 | } 66 | return NO; 67 | } 68 | 69 | - (BOOL)moveToScreen:(NSScreen *)screen 70 | { 71 | if (!screen) 72 | { 73 | return NO; 74 | } 75 | uint32_t displayID = [[screen.deviceDescription objectForKey:@"NSScreenNumber"] intValue]; 76 | CGPoint point = CGPointMake(CGRectGetWidth(screen.frame) / 2.0, CGRectGetHeight(screen.frame) / 2.0); 77 | CGDisplayMoveCursorToPoint(displayID, point); 78 | return YES; 79 | } 80 | 81 | @end 82 | -------------------------------------------------------------------------------- /SGWindowLayout/Core/SGWLHotKey.h: -------------------------------------------------------------------------------- 1 | // 2 | // HotKey.h 3 | // WindowLayout 4 | // 5 | // Created by Single on 2016/11/21. 6 | // Copyright © 2016年 single. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef NS_ENUM(NSInteger, SGWLKeyCode) 12 | { 13 | SGWLKeyCodeA = 0, 14 | SGWLKeyCodeS = 1, 15 | SGWLKeyCodeD = 2, 16 | SGWLKeyCodeW = 13, 17 | SGWLKeyCodeX = 7, 18 | SGWLKeyCodeQ = 12, 19 | SGWLKeyCodeE = 14, 20 | SGWLKeyCodeZ = 6, 21 | SGWLKeyCodeC = 8, 22 | }; 23 | 24 | typedef NS_ENUM(NSInteger, SGWLModifiersKey) 25 | { 26 | SGWLModifiersKeyControl, 27 | SGWLModifiersKeyOption, 28 | SGWLModifiersKeyCommand, 29 | SGWLModifiersKeyShift, 30 | }; 31 | 32 | @interface SGWLHotKey : NSObject 33 | 34 | + (void)registerKeyCode:(SGWLKeyCode)keyCode modifiers:(SGWLModifiersKey)modifiers handler:(void (^)(void))handler; 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /SGWindowLayout/Core/SGWLHotKey.m: -------------------------------------------------------------------------------- 1 | // 2 | // HotKey.m 3 | // WindowLayout 4 | // 5 | // Created by Single on 2016/11/21. 6 | // Copyright © 2016年 single. All rights reserved. 7 | // 8 | 9 | #import "SGWLHotKey.h" 10 | #import "PTHotKey.h" 11 | #import "PTHotKeyCenter.h" 12 | 13 | @interface SGWLHotKey () 14 | 15 | @property (nonatomic, strong) NSMutableDictionary * handlers; 16 | 17 | @end 18 | 19 | @implementation SGWLHotKey 20 | 21 | + (instancetype)sharedInstance 22 | { 23 | static SGWLHotKey * obj = nil; 24 | static dispatch_once_t onceToken; 25 | dispatch_once(&onceToken, ^{ 26 | obj = [[SGWLHotKey alloc] init]; 27 | }); 28 | return obj; 29 | } 30 | 31 | + (void)registerKeyCode:(SGWLKeyCode)keyCode modifiers:(SGWLModifiersKey)modifiers handler:(void (^)(void))handler 32 | { 33 | [[SGWLHotKey sharedInstance] registerKeyCode:keyCode modifiers:modifiers handler:handler]; 34 | } 35 | 36 | - (instancetype)init 37 | { 38 | if (self = [super init]) 39 | { 40 | self.handlers = [NSMutableDictionary dictionary]; 41 | } 42 | return self; 43 | } 44 | 45 | - (void)registerKeyCode:(SGWLKeyCode)keyCode modifiers:(SGWLModifiersKey)modifiers handler:(void (^)(void))handler 46 | { 47 | int intModifiers = controlKey; 48 | switch (modifiers) 49 | { 50 | case SGWLModifiersKeyControl: 51 | intModifiers = controlKey; 52 | break; 53 | case SGWLModifiersKeyOption: 54 | intModifiers = optionKey; 55 | break; 56 | case SGWLModifiersKeyCommand: 57 | intModifiers = cmdKey; 58 | break; 59 | case SGWLModifiersKeyShift: 60 | intModifiers = shiftKey; 61 | break; 62 | } 63 | [self registerFreeKeyCode:keyCode freeModifiers:intModifiers handler:handler]; 64 | } 65 | 66 | - (void)registerFreeKeyCode:(int)keyCode freeModifiers:(int)modifiers handler:(void (^)(void))handler 67 | { 68 | static int index = 0; 69 | NSString * name = [NSString stringWithFormat:@"%d", index++]; 70 | 71 | PTKeyCombo * keyCombo = [PTKeyCombo keyComboWithKeyCode:keyCode modifiers:modifiers]; 72 | 73 | PTHotKey * ptHotKey = [[PTHotKey alloc] init]; 74 | [ptHotKey setName:name]; 75 | [ptHotKey setKeyCombo:keyCombo]; 76 | [ptHotKey setIsExclusive:YES]; 77 | [ptHotKey setTarget:self]; 78 | [ptHotKey setAction:@selector(globalHotKeyDidPress:)]; 79 | 80 | [self.handlers setObject:handler forKey:name]; 81 | 82 | [[PTHotKeyCenter sharedCenter] registerHotKey:ptHotKey]; 83 | } 84 | 85 | - (void)globalHotKeyDidPress:(PTHotKey *)hotKey 86 | { 87 | void (^handler)(void) = [self.handlers objectForKey:hotKey.name]; 88 | if (handler) 89 | { 90 | handler(); 91 | } 92 | } 93 | 94 | @end 95 | -------------------------------------------------------------------------------- /SGWindowLayout/Core/SGWLLayout.h: -------------------------------------------------------------------------------- 1 | // 2 | // Layout.h 3 | // WindowLayout 4 | // 5 | // Created by Single on 2016/11/21. 6 | // Copyright © 2016年 single. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef NS_ENUM(NSInteger, SGWLLayoutAttribute) 12 | { 13 | SGWLLayoutAttributeLeft, 14 | SGWLLayoutAttributeFull, 15 | SGWLLayoutAttributeRight, 16 | SGWLLayoutAttributeTop, 17 | SGWLLayoutAttributeBottom, 18 | SGWLLayoutAttributeLeftTop, 19 | SGWLLayoutAttributeRightTop, 20 | SGWLLayoutAttributeLeftBottom, 21 | SGWLLayoutAttributeRightBottom, 22 | SGWLLayoutAttributeSmaller, 23 | SGWLLayoutAttributeCenter, 24 | SGWLLayoutAttributeHalfCenter, 25 | SGWLLayoutAttributeTwoThirdsFourThirdsCenter, 26 | }; 27 | 28 | @interface SGWLLayout : NSObject 29 | 30 | + (NSScreen *)swapScreenIfNeeded; 31 | + (void)layoutCurrentFocusedWindowWithLayoutAttribute:(SGWLLayoutAttribute)layoutAttribute; 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /SGWindowLayout/Core/SGWLLayout.m: -------------------------------------------------------------------------------- 1 | // 2 | // Layout.m 3 | // WindowLayout 4 | // 5 | // Created by Single on 2016/11/21. 6 | // Copyright © 2016年 single. All rights reserved. 7 | // 8 | 9 | #import "SGWLLayout.h" 10 | #import "SGWLScreen.h" 11 | #import "SGWLPoint.h" 12 | 13 | @implementation SGWLLayout 14 | 15 | + (instancetype)sharedInstance 16 | { 17 | static SGWLLayout * obj = nil; 18 | static dispatch_once_t onceToken; 19 | dispatch_once(&onceToken, ^{ 20 | obj = [[SGWLLayout alloc] init]; 21 | }); 22 | return obj; 23 | } 24 | 25 | + (NSScreen *)swapScreenIfNeeded 26 | { 27 | return [[SGWLLayout sharedInstance] swapScreenIfNeeded]; 28 | } 29 | 30 | + (void)layoutCurrentFocusedWindowWithLayoutAttribute:(SGWLLayoutAttribute)layoutAttribute 31 | { 32 | CGPoint point = [SGWLPoint mouseLocation]; 33 | NSScreen * screen = [SGWLScreen screenWithPoint:point]; 34 | [[SGWLLayout sharedInstance] layoutCurrentFocusedWindowWithLayoutAttribute:layoutAttribute screen:screen]; 35 | } 36 | 37 | - (instancetype)init 38 | { 39 | if (self = [super init]) 40 | { 41 | if (!AXIsProcessTrusted()) 42 | { 43 | [self processTrusted]; 44 | } 45 | } 46 | return self; 47 | } 48 | 49 | - (NSScreen *)swapScreenIfNeeded 50 | { 51 | if ([NSScreen screens].count <= 1) 52 | { 53 | return nil; 54 | } 55 | CGPoint point = [SGWLPoint focusedWindowLocation]; 56 | NSScreen * screen = [SGWLScreen nextScreenWithPoint:point]; 57 | [self layoutCurrentFocusedWindowWithLayoutAttribute:SGWLLayoutAttributeFull screen:screen]; 58 | return screen; 59 | } 60 | 61 | - (void)layoutCurrentFocusedWindowWithLayoutAttribute:(SGWLLayoutAttribute)layoutAttribute screen:(NSScreen *)screen 62 | { 63 | if (!AXIsProcessTrusted()) 64 | { 65 | [self processTrusted]; 66 | return; 67 | } 68 | 69 | pid_t pid = [NSWorkspace sharedWorkspace].frontmostApplication.processIdentifier; 70 | 71 | AXUIElementRef application = AXUIElementCreateApplication(pid); 72 | 73 | AXUIElementRef focusedWindow; 74 | AXError error = AXUIElementCopyAttributeValue(application, kAXFocusedWindowAttribute, (CFTypeRef *)&focusedWindow); 75 | 76 | if (error != kAXErrorSuccess) { 77 | NSLog(@"error : get focused window error"); 78 | return; 79 | } 80 | 81 | AXValueRef currentPositionValue; 82 | error = AXUIElementCopyAttributeValue(focusedWindow, kAXPositionAttribute, (CFTypeRef *)¤tPositionValue); 83 | if (error != kAXErrorSuccess) { 84 | NSLog(@"error : get focused window position error"); 85 | } 86 | 87 | CGPoint currentPoint; 88 | AXValueGetValue(currentPositionValue, kAXValueCGPointType, ¤tPoint); 89 | 90 | AXValueRef currentSizeValue; 91 | error = AXUIElementCopyAttributeValue(focusedWindow, kAXSizeAttribute, (CFTypeRef *)¤tSizeValue); 92 | if (error != kAXErrorSuccess) { 93 | NSLog(@"error : get focused window size error"); 94 | return; 95 | } 96 | 97 | CGSize currentSize; 98 | AXValueGetValue(currentSizeValue, kAXValueTypeCGSize, ¤tSize); 99 | 100 | NSRect currentFrame = NSMakeRect(currentPoint.x, currentPoint.y, currentSize.width, currentSize.height); 101 | CGRect resultFrame = [self realFrameCurrentFrame:currentFrame layoutAttribute:layoutAttribute screen:screen]; 102 | CGPoint position = resultFrame.origin; 103 | CGSize size = resultFrame.size; 104 | 105 | AXValueRef positionValue = AXValueCreate(kAXValueCGPointType, &position); 106 | AXValueRef sizeValue = AXValueCreate(kAXValueCGSizeType, &size); 107 | 108 | error = AXUIElementSetAttributeValue(focusedWindow, kAXPositionAttribute, positionValue); 109 | if (error != kAXErrorSuccess) { 110 | NSLog(@"error : set position error"); 111 | return; 112 | } 113 | error = AXUIElementSetAttributeValue(focusedWindow, kAXSizeAttribute, sizeValue); 114 | if (error != kAXErrorSuccess) { 115 | NSLog(@"error : set size error"); 116 | return; 117 | } 118 | 119 | CFRelease(positionValue); 120 | CFRelease(sizeValue); 121 | } 122 | 123 | - (void)processTrusted 124 | { 125 | CFMutableDictionaryRef options = CFDictionaryCreateMutable(NULL, 0, NULL, NULL); 126 | CFDictionaryAddValue(options, kAXTrustedCheckOptionPrompt, kCFBooleanTrue); 127 | AXIsProcessTrustedWithOptions(options); 128 | CFRelease(options); 129 | } 130 | 131 | - (NSRect)realFrameCurrentFrame:(NSRect)currentFrame layoutAttribute:(SGWLLayoutAttribute)layoutAttribute screen:(NSScreen *)screen 132 | { 133 | NSRect realFrame; 134 | NSRect screenFrame = [self frameForScreen:screen]; 135 | 136 | switch (layoutAttribute) { 137 | case SGWLLayoutAttributeLeft: 138 | { 139 | realFrame.origin = screenFrame.origin; 140 | realFrame.size = NSMakeSize(screenFrame.size.width/2, screenFrame.size.height); 141 | } 142 | break; 143 | case SGWLLayoutAttributeFull: 144 | { 145 | realFrame = screenFrame; 146 | } 147 | break; 148 | case SGWLLayoutAttributeRight: 149 | { 150 | realFrame.origin = CGPointMake(screenFrame.origin.x + screenFrame.size.width/2, screenFrame.origin.y); 151 | realFrame.size = NSMakeSize(screenFrame.size.width/2, screenFrame.size.height); 152 | } 153 | break; 154 | case SGWLLayoutAttributeTop: 155 | { 156 | realFrame.origin = screenFrame.origin; 157 | realFrame.size = NSMakeSize(screenFrame.size.width, screenFrame.size.height/2); 158 | } 159 | break; 160 | case SGWLLayoutAttributeBottom: 161 | { 162 | realFrame.origin = NSMakePoint(screenFrame.origin.x, screenFrame.origin.y + screenFrame.size.height/2); 163 | realFrame.size = NSMakeSize(screenFrame.size.width, screenFrame.size.height/2); 164 | } 165 | break; 166 | case SGWLLayoutAttributeLeftTop: 167 | { 168 | realFrame.origin = screenFrame.origin; 169 | realFrame.size = NSMakeSize(screenFrame.size.width/2, screenFrame.size.height/2); 170 | } 171 | break; 172 | case SGWLLayoutAttributeRightTop: 173 | { 174 | realFrame.origin = CGPointMake(screenFrame.origin.x + screenFrame.size.width/2, screenFrame.origin.y); 175 | realFrame.size = NSMakeSize(screenFrame.size.width/2, screenFrame.size.height/2); 176 | } 177 | break; 178 | case SGWLLayoutAttributeLeftBottom: 179 | { 180 | realFrame.origin = NSMakePoint(screenFrame.origin.x, screenFrame.origin.y + screenFrame.size.height/2); 181 | realFrame.size = NSMakeSize(screenFrame.size.width/2, screenFrame.size.height/2); 182 | } 183 | break; 184 | case SGWLLayoutAttributeRightBottom: 185 | { 186 | realFrame.origin = CGPointMake(screenFrame.origin.x + screenFrame.size.width/2, screenFrame.origin.y + screenFrame.size.height/2); 187 | realFrame.size = NSMakeSize(screenFrame.size.width/2, screenFrame.size.height/2); 188 | } 189 | break; 190 | case SGWLLayoutAttributeSmaller: 191 | { 192 | realFrame.origin = currentFrame.origin; 193 | realFrame.size = NSMakeSize(currentFrame.size.width/2, currentFrame.size.height/2); 194 | } 195 | break; 196 | case SGWLLayoutAttributeCenter: 197 | { 198 | realFrame.origin = NSMakePoint((screenFrame.size.width - currentFrame.size.width) / 2 + screenFrame.origin.x, (screenFrame.size.height - currentFrame.size.height) / 2 + screenFrame.origin.y); 199 | realFrame.size = currentFrame.size; 200 | } 201 | break; 202 | case SGWLLayoutAttributeHalfCenter: 203 | { 204 | NSSize size = NSMakeSize(screenFrame.size.width * 1 / 2, screenFrame.size.height * 1 / 2); 205 | realFrame.origin = NSMakePoint((screenFrame.size.width - size.width) / 2 + screenFrame.origin.x, (screenFrame.size.height - size.height) / 2 + screenFrame.origin.y); 206 | realFrame.size = NSMakeSize(screenFrame.size.width / 2, screenFrame.size.height / 2); 207 | } 208 | break; 209 | case SGWLLayoutAttributeTwoThirdsFourThirdsCenter: 210 | { 211 | NSSize size = NSMakeSize(screenFrame.size.width * 2 / 3, screenFrame.size.height * 3 / 4); 212 | realFrame.origin = NSMakePoint((screenFrame.size.width - size.width) / 2 + screenFrame.origin.x, (screenFrame.size.height - size.height) / 2 + screenFrame.origin.y); 213 | realFrame.size = size; 214 | } 215 | break; 216 | } 217 | 218 | if (realFrame.origin.y > 0) { 219 | realFrame.origin.y += 23; 220 | } 221 | return realFrame; 222 | } 223 | 224 | - (NSRect)frameForScreen:(NSScreen *)screen 225 | { 226 | NSScreen * baseScreen = [NSScreen screens].firstObject; 227 | NSRect baseFrame = baseScreen.frame; 228 | 229 | NSRect mainFrame = screen.frame; 230 | NSRect mainVisibleFrame = screen.visibleFrame; 231 | 232 | NSRect frame = NSMakeRect(mainVisibleFrame.origin.x, 233 | baseFrame.size.height - mainFrame.size.height - mainFrame.origin.y, 234 | mainVisibleFrame.size.width, 235 | mainVisibleFrame.size.height); 236 | 237 | return frame; 238 | } 239 | 240 | @end 241 | -------------------------------------------------------------------------------- /SGWindowLayout/Core/SGWLLogin.h: -------------------------------------------------------------------------------- 1 | // 2 | // Login.h 3 | // WindowLayout 4 | // 5 | // Created by Single on 2016/11/21. 6 | // Copyright © 2016年 single. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SGWLLogin : NSObject 12 | 13 | + (instancetype)login; 14 | 15 | @property (nonatomic, assign) BOOL startAtLogin; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /SGWindowLayout/Core/SGWLLogin.m: -------------------------------------------------------------------------------- 1 | // 2 | // Login.m 3 | // WindowLayout 4 | // 5 | // Created by Single on 2016/11/21. 6 | // Copyright © 2016年 single. All rights reserved. 7 | // 8 | 9 | #import "SGWLLogin.h" 10 | 11 | @interface SGWLLogin () 12 | 13 | @property (nonatomic, assign) LSSharedFileListRef sharedFileList; 14 | @property (nonatomic, assign) LSSharedFileListItemRef shareedFileListItem; 15 | 16 | @end 17 | 18 | @implementation SGWLLogin 19 | 20 | + (instancetype)login 21 | { 22 | static SGWLLogin * login = nil; 23 | static dispatch_once_t onceToken; 24 | dispatch_once(&onceToken, ^{ 25 | login = [[self alloc] init]; 26 | }); 27 | return login; 28 | } 29 | 30 | #pragma clang diagnostic push 31 | #pragma clang diagnostic ignored"-Wdeprecated-declarations" 32 | 33 | - (instancetype)init 34 | { 35 | if (self = [super init]) { 36 | self.sharedFileList = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL); 37 | } 38 | return self; 39 | } 40 | 41 | - (void)setStartAtLogin:(BOOL)startAtLogin 42 | { 43 | NSURL * bundleURL = [[NSBundle mainBundle] bundleURL]; 44 | if (startAtLogin) { 45 | self.shareedFileListItem = LSSharedFileListInsertItemURL(self.sharedFileList, kLSSharedFileListItemLast, NULL, NULL, (__bridge CFURLRef)bundleURL, NULL, NULL); 46 | if (self.shareedFileListItem == NULL) { 47 | NSLog(@"register start at login failure!"); 48 | } 49 | } else { 50 | LSSharedFileListItemRemove(self.sharedFileList, self.shareedFileListItem); 51 | } 52 | } 53 | 54 | - (BOOL)startAtLogin 55 | { 56 | UInt32 seed = 0; 57 | CFArrayRef loginItemsSnapshot = LSSharedFileListCopySnapshot(self.sharedFileList, &seed); 58 | if (loginItemsSnapshot == NULL) return NO; 59 | 60 | CFIndex totalCount = CFArrayGetCount(loginItemsSnapshot); 61 | for (CFIndex index = 0; index < totalCount; index++) 62 | { 63 | LSSharedFileListItemRef handle = (LSSharedFileListItemRef)CFArrayGetValueAtIndex(loginItemsSnapshot, index); 64 | CFRetain(handle); 65 | 66 | CFURLRef urlRef = LSSharedFileListItemCopyResolvedURL(handle, kLSSharedFileListNoUserInteraction, NULL); 67 | NSURL * url = CFBridgingRelease(urlRef); 68 | 69 | if ([url isEqualTo:[[NSBundle mainBundle] bundleURL]]) { 70 | self.shareedFileListItem = handle; 71 | CFRelease(loginItemsSnapshot); 72 | return YES; 73 | } 74 | } 75 | CFRelease(loginItemsSnapshot); 76 | return NO; 77 | } 78 | 79 | #pragma clang diagnostic pop 80 | 81 | @end 82 | -------------------------------------------------------------------------------- /SGWindowLayout/Core/SGWLPoint.h: -------------------------------------------------------------------------------- 1 | // 2 | // SGWLPoint.h 3 | // SGWindowLayout 4 | // 5 | // Created by Single on 2018/7/20. 6 | // Copyright © 2018年 single. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SGWLPoint : NSObject 12 | 13 | + (CGPoint)mouseLocation; 14 | + (CGPoint)focusedWindowLocation; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /SGWindowLayout/Core/SGWLPoint.m: -------------------------------------------------------------------------------- 1 | // 2 | // SGWLPoint.m 3 | // SGWindowLayout 4 | // 5 | // Created by Single on 2018/7/20. 6 | // Copyright © 2018年 single. All rights reserved. 7 | // 8 | 9 | #import "SGWLPoint.h" 10 | 11 | @implementation SGWLPoint 12 | 13 | + (CGPoint)mouseLocation 14 | { 15 | return [NSEvent mouseLocation]; 16 | } 17 | 18 | + (CGPoint)focusedWindowLocation 19 | { 20 | pid_t pid = [NSWorkspace sharedWorkspace].frontmostApplication.processIdentifier; 21 | 22 | AXUIElementRef application = AXUIElementCreateApplication(pid); 23 | 24 | AXUIElementRef focusedWindow; 25 | AXError error = AXUIElementCopyAttributeValue(application, kAXFocusedWindowAttribute, (CFTypeRef *)&focusedWindow); 26 | 27 | if (error != kAXErrorSuccess) { 28 | NSLog(@"error : get focused window error"); 29 | return CGPointZero; 30 | } 31 | 32 | AXValueRef currentPositionValue; 33 | error = AXUIElementCopyAttributeValue(focusedWindow, kAXPositionAttribute, (CFTypeRef *)¤tPositionValue); 34 | if (error != kAXErrorSuccess) { 35 | NSLog(@"error : get focused window position error"); 36 | return CGPointZero; 37 | } 38 | 39 | CGPoint currentPoint; 40 | AXValueGetValue(currentPositionValue, kAXValueCGPointType, ¤tPoint); 41 | 42 | return currentPoint; 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /SGWindowLayout/Core/SGWLScreen.h: -------------------------------------------------------------------------------- 1 | // 2 | // SGWLScreen.h 3 | // SGWindowLayout 4 | // 5 | // Created by Single on 2018/7/20. 6 | // Copyright © 2018年 single. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SGWLScreen : NSObject 12 | 13 | + (NSScreen *)screenWithPoint:(CGPoint)point; 14 | + (NSScreen *)nextScreenWithPoint:(CGPoint)point; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /SGWindowLayout/Core/SGWLScreen.m: -------------------------------------------------------------------------------- 1 | // 2 | // SGWLScreen.m 3 | // SGWindowLayout 4 | // 5 | // Created by Single on 2018/7/20. 6 | // Copyright © 2018年 single. All rights reserved. 7 | // 8 | 9 | #import "SGWLScreen.h" 10 | 11 | @implementation SGWLScreen 12 | 13 | + (NSScreen *)screenWithPoint:(CGPoint)point 14 | { 15 | for (NSScreen * obj in [NSScreen screens]) 16 | { 17 | if (CGRectContainsPoint(obj.frame, point)) 18 | { 19 | return obj; 20 | } 21 | } 22 | return nil; 23 | } 24 | 25 | + (NSScreen *)nextScreenWithPoint:(CGPoint)point 26 | { 27 | int index = 0; 28 | for (int i = 0; i < [NSScreen screens].count; i++) 29 | { 30 | NSScreen * screen = [[NSScreen screens] objectAtIndex:i]; 31 | if (CGRectContainsPoint(screen.frame, point)) 32 | { 33 | index = i + 1; 34 | } 35 | } 36 | if (index == [NSScreen screens].count) 37 | { 38 | index = 0; 39 | } 40 | if (index < [NSScreen screens].count) 41 | { 42 | return [[NSScreen screens] objectAtIndex:index]; 43 | } 44 | return nil; 45 | } 46 | 47 | @end 48 | -------------------------------------------------------------------------------- /SGWindowLayout/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0.0 21 | CFBundleVersion 22 | 1 23 | LSApplicationCategoryType 24 | public.app-category.utilities 25 | LSMinimumSystemVersion 26 | $(MACOSX_DEPLOYMENT_TARGET) 27 | LSUIElement 28 | 29 | NSHumanReadableCopyright 30 | Copyright © 2016年 single. All rights reserved. 31 | NSMainNibFile 32 | MainMenu 33 | NSPrincipalClass 34 | NSApplication 35 | 36 | 37 | -------------------------------------------------------------------------------- /SGWindowLayout/Vendors/PTHotKey/PTHotKey.h: -------------------------------------------------------------------------------- 1 | // 2 | // PTHotKey.h 3 | // Protein 4 | // 5 | // Created by Quentin Carnicelli on Sat Aug 02 2003. 6 | // Copyright (c) 2003 Quentin D. Carnicelli. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "PTKeyCombo.h" 12 | 13 | @interface PTHotKey : NSObject { 14 | NSString* mName; 15 | PTKeyCombo* mKeyCombo; 16 | id mTarget; 17 | SEL mAction; 18 | EventHotKeyRef carbonHotKey; 19 | BOOL mIsExclusive; 20 | } 21 | 22 | - (id)init; 23 | 24 | - (void)setName: (NSString*)name; 25 | - (NSString*)name; 26 | 27 | - (EventHotKeyRef)carbonHotKey; 28 | - (void)setCarbonHotKey:(EventHotKeyRef)hotKey; 29 | 30 | - (void)setKeyCombo: (PTKeyCombo*)combo; 31 | - (PTKeyCombo*)keyCombo; 32 | 33 | - (void)setIsExclusive:(BOOL)isExclusive; 34 | - (BOOL)isExclusive; 35 | 36 | - (void)setTarget: (id)target; 37 | - (id)target; 38 | - (void)setAction: (SEL)action; 39 | - (SEL)action; 40 | 41 | - (void)invoke; 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /SGWindowLayout/Vendors/PTHotKey/PTHotKey.m: -------------------------------------------------------------------------------- 1 | // 2 | // PTHotKey.m 3 | // Protein 4 | // 5 | // Created by Quentin Carnicelli on Sat Aug 02 2003. 6 | // Copyright (c) 2003 Quentin D. Carnicelli. All rights reserved. 7 | // 8 | 9 | #import "PTHotKey.h" 10 | 11 | #import "PTHotKeyCenter.h" 12 | #import "PTKeyCombo.h" 13 | 14 | @implementation PTHotKey 15 | 16 | - (id)init 17 | { 18 | self = [super init]; 19 | 20 | if( self ) 21 | { 22 | [self setKeyCombo: [PTKeyCombo clearKeyCombo]]; 23 | [self setIsExclusive:NO]; 24 | } 25 | 26 | return self; 27 | } 28 | 29 | 30 | - (NSString*)description 31 | { 32 | return [NSString stringWithFormat: @"<%@: %@>", NSStringFromClass( [self class] ), [self keyCombo]]; 33 | } 34 | 35 | #pragma mark - 36 | 37 | - (EventHotKeyRef)carbonHotKey { 38 | return carbonHotKey; 39 | } 40 | 41 | - (void)setCarbonHotKey:(EventHotKeyRef)hotKey { 42 | carbonHotKey = hotKey; 43 | } 44 | 45 | - (void)setKeyCombo: (PTKeyCombo*)combo 46 | { 47 | mKeyCombo = combo; 48 | } 49 | 50 | - (PTKeyCombo*)keyCombo 51 | { 52 | return mKeyCombo; 53 | } 54 | 55 | - (void)setName: (NSString*)name 56 | { 57 | mName = name; 58 | } 59 | 60 | - (NSString*)name 61 | { 62 | return mName; 63 | } 64 | 65 | - (void)setIsExclusive:(BOOL)isExclusive 66 | { 67 | mIsExclusive = isExclusive; 68 | } 69 | 70 | - (BOOL)isExclusive 71 | { 72 | return mIsExclusive; 73 | } 74 | 75 | - (void)setTarget: (id)target 76 | { 77 | mTarget = target; 78 | } 79 | 80 | - (id)target 81 | { 82 | return mTarget; 83 | } 84 | 85 | - (void)setAction: (SEL)action 86 | { 87 | mAction = action; 88 | } 89 | 90 | - (SEL)action 91 | { 92 | return mAction; 93 | } 94 | 95 | - (void)invoke 96 | { 97 | #pragma clang diagnostic push 98 | #pragma clang diagnostic ignored "-Warc-performSelector-leaks" 99 | [mTarget performSelector: mAction withObject: self]; 100 | #pragma clang diagnostic pop 101 | } 102 | 103 | @end 104 | -------------------------------------------------------------------------------- /SGWindowLayout/Vendors/PTHotKey/PTHotKeyCenter.h: -------------------------------------------------------------------------------- 1 | // 2 | // PTHotKeyCenter.h 3 | // Protein 4 | // 5 | // Created by Quentin Carnicelli on Sat Aug 02 2003. 6 | // Copyright (c) 2003 Quentin D. Carnicelli. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class PTHotKey; 12 | 13 | @interface PTHotKeyCenter : NSObject 14 | { 15 | NSMutableDictionary* mHotKeys; //Keys are NSValue of EventHotKeyRef 16 | NSMutableDictionary* mHotKeyMap; 17 | u_int32_t mNextKeyID; 18 | BOOL mEventHandlerInstalled; 19 | } 20 | 21 | + (id)sharedCenter; 22 | 23 | //- (void) enterHotKeyWithName:(NSString *)name enable:(BOOL)ena; 24 | - (BOOL)registerHotKey: (PTHotKey*)hotKey; 25 | - (void)unregisterHotKey: (PTHotKey*)hotKey; 26 | - (void) unregisterHotKeyForName:(NSString *)name; 27 | - (void) unregisterAllHotKeys; 28 | - (void) setHotKeyRegistrationForName:(NSString *)name enable:(BOOL)ena; 29 | - (PTHotKey *) hotKeyForName:(NSString *)name; 30 | - (void) updateHotKey:(PTHotKey *)hk; 31 | 32 | - (NSArray*)allHotKeys; 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /SGWindowLayout/Vendors/PTHotKey/PTHotKeyCenter.m: -------------------------------------------------------------------------------- 1 | // 2 | // PTHotKeyCenter.m 3 | // Protein 4 | // 5 | // Created by Quentin Carnicelli on Sat Aug 02 2003. 6 | // Copyright (c) 2003 Quentin D. Carnicelli. All rights reserved. 7 | // 8 | 9 | #import "PTHotKeyCenter.h" 10 | #import "PTHotKey.h" 11 | #import "PTKeyCombo.h" 12 | #import 13 | 14 | @interface PTHotKeyCenter (Private) 15 | - (void)_updateEventHandler; 16 | - (void)_hotKeyDown: (PTHotKey*)hotKey; 17 | - (void)_hotKeyUp: (PTHotKey*)hotKey; 18 | static OSStatus hotKeyEventHandler(EventHandlerCallRef inHandlerRef, EventRef inEvent, void* refCon ); 19 | @end 20 | 21 | @implementation PTHotKeyCenter 22 | 23 | static id _sharedHotKeyCenter = nil; 24 | 25 | + (id)sharedCenter 26 | { 27 | if( _sharedHotKeyCenter == nil ) 28 | { 29 | _sharedHotKeyCenter = [[self alloc] init]; 30 | } 31 | 32 | return _sharedHotKeyCenter; 33 | } 34 | 35 | - (id)init 36 | { 37 | self = [super init]; 38 | 39 | if( self ) 40 | { 41 | mHotKeys = [[NSMutableDictionary alloc] init]; 42 | mHotKeyMap = [[NSMutableDictionary alloc] init]; 43 | mNextKeyID = 1; 44 | } 45 | 46 | return self; 47 | } 48 | 49 | 50 | #pragma mark - 51 | 52 | - (BOOL)registerHotKey: (PTHotKey*)hotKey 53 | { 54 | //NSLog(@"registerHotKey: %@", hotKey); 55 | OSStatus err; 56 | EventHotKeyID hotKeyID; 57 | EventHotKeyRef carbonHotKey; 58 | /* 59 | if([mHotKeys objectForKey:[hotKey name]]) 60 | [self unregisterHotKey: hotKey]; 61 | */ 62 | if( [[hotKey keyCombo] isValidHotKeyCombo] == NO ) 63 | { 64 | //NSLog(@"not valid keycombo:"); 65 | return YES; 66 | } 67 | 68 | 69 | hotKeyID.signature = UTGetOSTypeFromString(CFSTR("PTHk")); 70 | hotKeyID.id = mNextKeyID; 71 | 72 | //NSLog(@"registering..."); 73 | err = RegisterEventHotKey( [[hotKey keyCombo] keyCode], 74 | [[hotKey keyCombo] modifiers], 75 | hotKeyID, 76 | GetEventDispatcherTarget(), 77 | [hotKey isExclusive] ? kEventHotKeyExclusive : 0, 78 | &carbonHotKey ); 79 | 80 | if( err ) 81 | { 82 | //NSLog(@"error --"); 83 | return NO; 84 | } 85 | 86 | NSNumber *kid = [NSNumber numberWithUnsignedInt:mNextKeyID]; 87 | [mHotKeyMap setObject:hotKey forKey:kid]; 88 | mNextKeyID += 1; 89 | 90 | 91 | [hotKey setCarbonHotKey:carbonHotKey]; 92 | [mHotKeys setObject: hotKey forKey: [hotKey name]]; 93 | 94 | [self _updateEventHandler]; 95 | //NSLog(@"Eo registerHotKey:"); 96 | return YES; 97 | } 98 | 99 | - (void)unregisterHotKey: (PTHotKey*)hotKey 100 | { 101 | //NSLog(@"unregisterHotKey: %@", hotKey); 102 | EventHotKeyRef carbonHotKey; 103 | 104 | if(![mHotKeys objectForKey:[hotKey name]]) 105 | return; 106 | 107 | carbonHotKey = [hotKey carbonHotKey]; 108 | NSAssert( carbonHotKey != nil, @"" ); 109 | 110 | (void)UnregisterEventHotKey( carbonHotKey ); 111 | //Watch as we ignore 'err': 112 | 113 | [mHotKeys removeObjectForKey: [hotKey name]]; 114 | NSArray *remKeys = [mHotKeyMap allKeysForObject:hotKey]; 115 | if (remKeys && [remKeys count] > 0) 116 | [mHotKeyMap removeObjectsForKeys:remKeys]; 117 | 118 | [self _updateEventHandler]; 119 | //NSLog(@"Eo unregisterHotKey:"); 120 | //See that? Completely ignored 121 | } 122 | 123 | - (void) unregisterHotKeyForName:(NSString *)name 124 | { 125 | [self unregisterHotKey:[mHotKeys objectForKey:name]]; 126 | } 127 | 128 | - (void) unregisterAllHotKeys; 129 | { 130 | NSEnumerator *enumerator = [mHotKeys objectEnumerator]; 131 | id thing; 132 | while ((thing = [enumerator nextObject])) 133 | { 134 | [self unregisterHotKey:thing]; 135 | } 136 | } 137 | 138 | - (void) setHotKeyRegistrationForName:(NSString *)name enable:(BOOL)ena 139 | { 140 | if (ena) 141 | { 142 | [self registerHotKey:[mHotKeys objectForKey:name]]; 143 | } else 144 | { 145 | [self unregisterHotKey:[mHotKeys objectForKey:name]]; 146 | } 147 | } 148 | 149 | - (void) updateHotKey:(PTHotKey *)hk 150 | { 151 | //NSLog(@"updateHotKey: %@", hk); 152 | [self unregisterHotKey:[mHotKeys objectForKey:[hk name]]]; 153 | //NSLog(@"unreg'd: %@", hk); 154 | [self registerHotKey:hk]; 155 | //NSLog(@"Eo updateHotKey:"); 156 | } 157 | 158 | - (PTHotKey *) hotKeyForName:(NSString *)name 159 | { 160 | return [mHotKeys objectForKey:name]; 161 | } 162 | 163 | - (NSArray*)allHotKeys 164 | { 165 | return [mHotKeys allValues]; 166 | } 167 | 168 | #pragma mark - 169 | - (void)_updateEventHandler 170 | { 171 | if( [mHotKeys count] && mEventHandlerInstalled == NO ) 172 | { 173 | EventTypeSpec eventSpec[2] = { 174 | { kEventClassKeyboard, kEventHotKeyPressed }, 175 | { kEventClassKeyboard, kEventHotKeyReleased } 176 | }; 177 | 178 | InstallEventHandler( GetEventDispatcherTarget(), 179 | (EventHandlerProcPtr)hotKeyEventHandler, 180 | 2, eventSpec, nil, nil); 181 | 182 | mEventHandlerInstalled = YES; 183 | } 184 | } 185 | 186 | - (void)_hotKeyDown: (PTHotKey*)hotKey 187 | { 188 | [hotKey invoke]; 189 | } 190 | 191 | - (void)_hotKeyUp: (PTHotKey*)hotKey 192 | { 193 | //[hotKey uninvoke]; 194 | } 195 | 196 | - (OSStatus)sendCarbonEvent: (EventRef)event 197 | { 198 | OSStatus err; 199 | EventHotKeyID hotKeyID; 200 | PTHotKey* hotKey; 201 | 202 | NSAssert( GetEventClass( event ) == kEventClassKeyboard, @"Unknown event class" ); 203 | 204 | err = GetEventParameter( event, 205 | kEventParamDirectObject, 206 | typeEventHotKeyID, 207 | nil, 208 | sizeof(EventHotKeyID), 209 | nil, 210 | &hotKeyID ); 211 | if( err ) 212 | return err; 213 | 214 | 215 | NSAssert( hotKeyID.signature == UTGetOSTypeFromString(CFSTR("PTHk")), @"Invalid hot key id" ); 216 | 217 | NSNumber *kid = [NSNumber numberWithUnsignedInt:hotKeyID.id]; 218 | hotKey = [mHotKeyMap objectForKey:kid]; 219 | 220 | NSAssert( hotKey != nil, @"Invalid hot key id" ); 221 | 222 | switch( GetEventKind( event ) ) 223 | { 224 | case kEventHotKeyPressed: 225 | [self _hotKeyDown: hotKey]; 226 | break; 227 | 228 | case kEventHotKeyReleased: 229 | [self _hotKeyUp: hotKey]; 230 | break; 231 | 232 | default: 233 | NSAssert( 0, @"Unknown event kind" ); 234 | } 235 | 236 | return noErr; 237 | } 238 | 239 | static OSStatus hotKeyEventHandler(EventHandlerCallRef inHandlerRef, EventRef inEvent, void* refCon ) 240 | { 241 | return [[PTHotKeyCenter sharedCenter] sendCarbonEvent: inEvent]; 242 | } 243 | 244 | @end 245 | -------------------------------------------------------------------------------- /SGWindowLayout/Vendors/PTHotKey/PTKeyCombo.h: -------------------------------------------------------------------------------- 1 | // 2 | // PTKeyCombo.h 3 | // Protein 4 | // 5 | // Created by Quentin Carnicelli on Sat Aug 02 2003. 6 | // Copyright (c) 2003 Quentin D. Carnicelli. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | @interface PTKeyCombo : NSObject 13 | { 14 | int mKeyCode; 15 | int mModifiers; 16 | } 17 | 18 | + (id)clearKeyCombo; 19 | + (id)keyComboWithKeyCode: (int)keyCode modifiers: (int)modifiers; 20 | - (id)initWithKeyCode: (int)keyCode modifiers: (int)modifiers; 21 | 22 | - (id)initWithPlistRepresentation: (id)plist; 23 | - (id)plistRepresentation; 24 | 25 | - (BOOL)isEqual: (PTKeyCombo*)combo; 26 | 27 | - (int)keyCode; 28 | - (int)modifiers; 29 | 30 | - (BOOL)isClearCombo; 31 | - (BOOL)isValidHotKeyCombo; 32 | 33 | @end 34 | 35 | @interface PTKeyCombo (UserDisplayAdditions) 36 | 37 | - (NSString*)description; 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /SGWindowLayout/Vendors/PTHotKey/PTKeyCombo.m: -------------------------------------------------------------------------------- 1 | // 2 | // PTKeyCombo.m 3 | // Protein 4 | // 5 | // Created by Quentin Carnicelli on Sat Aug 02 2003. 6 | // Copyright (c) 2003 Quentin D. Carnicelli. All rights reserved. 7 | // 8 | 9 | #import "PTKeyCombo.h" 10 | 11 | #import 12 | 13 | @implementation PTKeyCombo 14 | 15 | + (id)clearKeyCombo 16 | { 17 | return [self keyComboWithKeyCode: -1 modifiers: -1]; 18 | } 19 | 20 | + (id)keyComboWithKeyCode: (int)keyCode modifiers: (int)modifiers 21 | { 22 | return [[self alloc] initWithKeyCode: keyCode modifiers: modifiers]; 23 | } 24 | 25 | - (id)initWithKeyCode: (int)keyCode modifiers: (int)modifiers 26 | { 27 | self = [super init]; 28 | 29 | if( self ) 30 | { 31 | mKeyCode = keyCode; 32 | mModifiers = modifiers; 33 | } 34 | 35 | return self; 36 | } 37 | 38 | - (id)initWithPlistRepresentation: (id)plist 39 | { 40 | int keyCode, modifiers; 41 | 42 | keyCode = [[plist objectForKey: @"keyCode"] intValue]; 43 | if( keyCode <= 0 ) keyCode = -1; 44 | 45 | modifiers = [[plist objectForKey: @"modifiers"] intValue]; 46 | if( modifiers <= 0 ) modifiers = -1; 47 | 48 | return [self initWithKeyCode: keyCode modifiers: modifiers]; 49 | } 50 | 51 | - (id)plistRepresentation 52 | { 53 | return [NSDictionary dictionaryWithObjectsAndKeys: 54 | [NSNumber numberWithInt: [self keyCode]], @"keyCode", 55 | [NSNumber numberWithInt: [self modifiers]], @"modifiers", 56 | nil]; 57 | } 58 | 59 | - (id)copyWithZone:(NSZone*)zone; 60 | { 61 | return self; 62 | } 63 | 64 | - (BOOL)isEqual: (PTKeyCombo*)combo 65 | { 66 | return [self keyCode] == [combo keyCode] && 67 | [self modifiers] == [combo modifiers]; 68 | } 69 | 70 | #pragma mark - 71 | 72 | - (int)keyCode 73 | { 74 | return mKeyCode; 75 | } 76 | 77 | - (int)modifiers 78 | { 79 | return mModifiers; 80 | } 81 | 82 | - (BOOL)isValidHotKeyCombo 83 | { 84 | return mKeyCode >= 0 && mModifiers > 0; 85 | } 86 | 87 | - (BOOL)isClearCombo 88 | { 89 | return mKeyCode == -1 && mModifiers == -1; 90 | } 91 | 92 | @end 93 | 94 | #pragma mark - 95 | 96 | @implementation PTKeyCombo (UserDisplayAdditions) 97 | 98 | + (NSDictionary*)_keyCodesDictionary 99 | { 100 | static NSDictionary* keyCodes = nil; 101 | if( keyCodes == nil ) { 102 | keyCodes = [NSDictionary dictionaryWithContentsOfFile:[[NSBundle bundleForClass: self] pathForResource: @"PTKeyCodes" ofType: @"plist"]]; 103 | } 104 | 105 | return keyCodes; 106 | } 107 | 108 | + (NSString*)_stringForModifiers: (long)modifiers 109 | { 110 | static long modToChar[4][2] = 111 | { 112 | #ifdef __BIG_ENDIAN__ 113 | { cmdKey, 0x23180000 }, 114 | { optionKey, 0x23250000 }, 115 | { controlKey, 0x005E0000 }, 116 | { shiftKey, 0x21e70000 } 117 | #else 118 | { cmdKey, 0x00002318 }, 119 | { optionKey, 0x00002325 }, 120 | { controlKey, 0x0000005E }, 121 | { shiftKey, 0x000021e7 } 122 | #endif 123 | }; 124 | 125 | NSString* str = nil; 126 | NSString* charStr; 127 | long i; 128 | 129 | str = [NSString string]; 130 | 131 | for( i = 0; i < 4; i++ ) 132 | { 133 | if( modifiers & modToChar[i][0] ) 134 | { 135 | charStr = [NSString stringWithCharacters: (const unichar*)&modToChar[i][1] length: 1]; 136 | str = [str stringByAppendingString: charStr]; 137 | } 138 | } 139 | 140 | if( !str ) 141 | str = @""; 142 | 143 | return str; 144 | } 145 | 146 | + (NSString*)_stringForKeyCode: (short)keyCode 147 | { 148 | NSDictionary* dict; 149 | id key; 150 | NSString* str; 151 | 152 | dict = [self _keyCodesDictionary]; 153 | key = [NSString stringWithFormat: @"%d", keyCode]; 154 | str = [dict objectForKey: key]; 155 | 156 | if( !str ) 157 | str = [NSString stringWithFormat: @"%X", keyCode]; 158 | 159 | return str; 160 | } 161 | 162 | - (NSString*)description 163 | { 164 | NSString* desc; 165 | 166 | if( [self isValidHotKeyCombo] ) //This might have to change 167 | { 168 | desc = [NSString stringWithFormat: @"%@%@", 169 | [[self class] _stringForModifiers: [self modifiers]], 170 | [[self class] _stringForKeyCode: [self keyCode]]]; 171 | } 172 | else 173 | desc = NSLocalizedString(@"(None)", @"description for no key combination"); 174 | 175 | return desc; 176 | } 177 | 178 | @end 179 | -------------------------------------------------------------------------------- /SGWindowLayout/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // WindowLayout 4 | // 5 | // Created by Single on 2016/11/21. 6 | // Copyright © 2016年 single. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | int main(int argc, const char * argv[]) { 12 | return NSApplicationMain(argc, argv); 13 | } 14 | --------------------------------------------------------------------------------