├── logo.ico
├── logo.png
├── banner.bmp
├── dialog.bmp
├── github.png
├── .editorconfig
├── polygon.ini
├── .github
├── ISSUE_TEMPLATE
│ ├── feature_request.md
│ └── bug_report.md
└── workflows
│ └── release.yml
├── .gitignore
├── LICENSE
├── polygon.rtf
├── CHANGELOG.md
├── polygon.wxs
├── polygon.ps1
├── README.md
└── polygon.ahk
/logo.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thesobercoder/polygon/HEAD/logo.ico
--------------------------------------------------------------------------------
/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thesobercoder/polygon/HEAD/logo.png
--------------------------------------------------------------------------------
/banner.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thesobercoder/polygon/HEAD/banner.bmp
--------------------------------------------------------------------------------
/dialog.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thesobercoder/polygon/HEAD/dialog.bmp
--------------------------------------------------------------------------------
/github.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thesobercoder/polygon/HEAD/github.png
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | insert_final_newline = true
6 | end_of_line = lf
7 | indent_style = space
8 | indent_size = 2
9 | max_line_length = 80
10 |
--------------------------------------------------------------------------------
/polygon.ini:
--------------------------------------------------------------------------------
1 | [Shortcut]
2 | Center="^#c"
3 | CenterHD="^#/"
4 | CenterHalf="^#w"
5 | CenterTwoThird="^#r"
6 | FirstTwoThird="^#a"
7 | LastTwoThird="^#h"
8 | FirstThird="^#d"
9 | CenterThird="^#f"
10 | LastThird="^#g"
11 | TopLeftSixth="^#z"
12 | BottomLeftSixth="^#x"
13 | TopRightSixth="^#v"
14 | BottomRightSixth="^#b"
15 | TopCenterSixth="^#n"
16 | BottomCenterSixth="^#m"
17 | LeftHalf="^#["
18 | RightHalf="^#]"
19 | TopLeft="^#u"
20 | TopRight="^#i"
21 | BottomLeft="^#j"
22 | BottomRight="^#k"
23 | TopHalf="^#-"
24 | BottomHalf="^#="
25 | FirstFourth="^#;"
26 | SecondFourth="^#'"
27 | ThirdFourth="^#,"
28 | LastFourth="^#."
29 |
30 | [Toast]
31 | Show=1
32 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # General
2 | .DS_Store
3 | .AppleDouble
4 | .LSOverride
5 |
6 | # Icon must end with two \r
7 | Icon
8 |
9 |
10 | # Thumbnails
11 | ._*
12 |
13 | # Files that might appear in the root of a volume
14 | .DocumentRevisions-V100
15 | .fseventsd
16 | .Spotlight-V100
17 | .TemporaryItems
18 | .Trashes
19 | .VolumeIcon.icns
20 | .com.apple.timemachine.donotpresent
21 |
22 | # Directories potentially created on remote AFP share
23 | .AppleDB
24 | .AppleDesktop
25 | Network Trash Folder
26 | Temporary Items
27 | .apdisk
28 |
29 | # Homebrew
30 | Brewfile.lock.json
31 |
32 | # dependencies
33 | /node_modules
34 | /.pnp
35 | .pnp.js
36 |
37 | # debug
38 | npm-debug.log*
39 | yarn-debug.log*
40 | yarn-error.log*
41 | .pnpm-debug.log*
42 |
43 | # AHK
44 | /build
45 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Operating System**
27 | Provide the output from `systeminfo`.
28 |
29 | For example:
30 | ```
31 | OS Name: Microsoft Windows 11 Pro
32 | OS Version: 10.0.22000 N/A Build 22000
33 | ```
34 |
35 | **Additional context**
36 | Add any other context about the problem here.
37 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Build and Release Polygon
2 |
3 | permissions:
4 | contents: write
5 |
6 | on:
7 | release:
8 | types: [published]
9 |
10 | jobs:
11 | BuildAndRelease:
12 | name: Build and Release
13 | runs-on: windows-latest
14 | steps:
15 | - name: Checkout
16 | uses: actions/checkout@v4
17 | if: startsWith(github.ref, 'refs/tags/v')
18 | with:
19 | fetch-depth: 0
20 | - name: Build Polygon
21 | shell: pwsh
22 | if: startsWith(github.ref, 'refs/tags/v')
23 | run: .\polygon.ps1;
24 | - name: Upload to GitHub release
25 | uses: softprops/action-gh-release@v1
26 | if: startsWith(github.ref, 'refs/tags/v')
27 | with:
28 | files: |
29 | ./build/polygon-x*.zip
30 | ./build/polygon-x*.msi
31 | ./build/polygon-checksum.txt
32 | env:
33 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
34 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Soham Dasgupta
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 |
--------------------------------------------------------------------------------
/polygon.rtf:
--------------------------------------------------------------------------------
1 | {\rtf1\ansi\deff0\nouicompat{\fonttbl{\f0\fnil\fcharset0 Arial;}{\f1\fnil\fcharset0 Courier New;}}
2 | {\*\generator Riched20 10.0.15063}\viewkind4\uc1
3 | \pard\sa180\fs24\lang9 Copyright (c) 2023 Soham Dasgupta\par
4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\par
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\par
6 | \f1 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\f0\par
7 | }
8 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## [v0.7.0](https://github.com/thesobercoder/polygon/compare/v0.6.0...v0.7.0) (2024-08-01)
2 |
3 | ### Features
4 |
5 | - Adds ability to turn off shortcuts by removing the config from the ini file
6 |
7 | ## [v0.6.0](https://github.com/thesobercoder/polygon/compare/v0.5.0...v0.6.0) (2024-05-06)
8 |
9 | ### Features
10 |
11 | - Add First Two Third
12 | - Add last Two Third
13 |
14 | ## [v0.5.0](https://github.com/thesobercoder/polygon/compare/v0.4.0...v0.5.0) (2023-11-21)
15 |
16 | ### Bugs
17 |
18 | - Improve toast display on quickly switching layout
19 | - Restore the window first before positioning
20 |
21 | ## [v0.4.0](https://github.com/thesobercoder/polygon/compare/v0.3.0...v0.4.0) (2023-11-20)
22 |
23 | ### Features
24 |
25 | - Add First Fourth
26 | - Add Second Fourth
27 | - Add Third Fourth
28 | - Add Last Fourth
29 |
30 | ## [v0.3.0](https://github.com/thesobercoder/polygon/compare/v0.2.0...v0.3.0) (2023-10-06)
31 |
32 | ### Features
33 |
34 | - Add Top Half
35 | - Add Bottom Half
36 |
37 | ### Shortcuts
38 |
39 | - Changed Center HD shortcut from CTRL+WIN+q to CTRL+WIN+/
40 | - Changed Center HD shortcut from CTRL+WIN+q to CTRL+WIN+/
41 |
42 | ## [v0.2.0](https://github.com/thesobercoder/polygon/compare/v0.1.0...v0.2.0) (2023-09-27)
43 |
44 | ### Features
45 |
46 | - Centralize window dimension and monitor detection functions
47 |
48 | ## v0.1.0 (2023-09-25)
49 |
50 | ### Features
51 |
52 | - Add Center
53 | - Add Center HD
54 | - Add Center Half
55 | - Add Left Half
56 | - Add Right Half
57 | - Add Center Two Third
58 | - Add First Third
59 | - Add Center Third
60 | - Add Last Third
61 | - Add Top Left Sixth
62 | - Add Bottom Left Sixth
63 | - Add Top Right Sixth
64 | - Add Bottom Right Sixth
65 | - Add Top Center Sixth
66 | - Add Bottom Center Sixth
67 | - Add Top Left
68 | - Add Top Right
69 | - Add Bottom Left
70 | - Add Bottom Right
71 | - Add automated version check at startup
72 | - Add tray menu for manual version checking
73 |
--------------------------------------------------------------------------------
/polygon.wxs:
--------------------------------------------------------------------------------
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 |
41 |
42 |
43 |
44 |
45 |
46 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
--------------------------------------------------------------------------------
/polygon.ps1:
--------------------------------------------------------------------------------
1 | $cwd = (Get-Location).Path;
2 |
3 | # Declare variable to check if running on GitHub action
4 | $isGitHubActions = if ($env:GITHUB_ACTIONS -eq "true") {
5 | $true;
6 | } else {
7 | $false;
8 | }
9 |
10 | # Assign tag based on environment
11 | $latestTag = if ($isGitHubActions) {
12 | $env:GITHUB_REF;
13 | } else {
14 | (git describe --tags --abbrev=0);
15 | }
16 |
17 | # Get the latest version from tag
18 | $versionNumber = (Select-String -Pattern "\d+\.\d+\.\d+" -InputObject $latestTag).Matches[0].Value;
19 |
20 | Write-Host "Building Polygon version $versionNumber" -ForegroundColor Green;
21 |
22 | # Clean build folder
23 | Remove-Item -Path "$cwd\build" -Recurse -Force -ErrorAction SilentlyContinue;
24 | New-Item -Path "$cwd\build" -ItemType Directory -Force | Out-Null;
25 |
26 | # Download AutoHotkey
27 | $ahkFileNamePattern = "AutoHotkey_.*\.zip";
28 | $ahkLatestRelease = Invoke-RestMethod -Uri "https://api.github.com/repos/AutoHotkey/AutoHotkey/releases/latest";
29 | $ahkDownloadURL = ($ahkLatestRelease.assets | Where-Object { $_.name -match "$ahkFileNamePattern" }) | Select-Object -ExpandProperty browser_download_url;
30 | Invoke-WebRequest -Uri $ahkDownloadURL -OutFile "$cwd\build\autohotkey.zip";
31 | Expand-Archive -Path "$cwd\build\autohotkey.zip" -DestinationPath "$cwd\build\ahk";
32 |
33 | Write-Host "Downloaded the latest release from AutoHotkey into `"$cwd\build\ahk\`"" -ForegroundColor Green;
34 |
35 | # Download Ahk2Exe
36 | $ahk2ExeFileNamePattern = "Ahk2Exe.*\.zip";
37 | $ahk2ExeLatestRelease = Invoke-RestMethod -Uri "https://api.github.com/repos/AutoHotkey/Ahk2Exe/releases/latest";
38 | $ahk2ExeDownloadURL = ($ahk2ExeLatestRelease.assets | Where-Object { $_.name -match "$ahk2ExeFileNamePattern" }) | Select-Object -ExpandProperty browser_download_url;
39 | Invoke-WebRequest -Uri $ahk2ExeDownloadURL -OutFile "$cwd\build\Ahk2exe.zip";
40 | Expand-Archive -Path "$cwd\build\Ahk2exe.zip" -DestinationPath "$cwd\build\ahk";
41 |
42 | Write-Host "Downloaded the latest release from Ahk2Exe into `"$cwd\build\ahk\`"" -ForegroundColor Green;
43 |
44 | # Download upx
45 | $upxFileNamePattern = "upx-.*\-win64.zip";
46 | $upxLatestRelease = Invoke-RestMethod -Uri "https://api.github.com/repos/upx/upx/releases/latest";
47 | $upxDownloadURL = ($upxLatestRelease.assets | Where-Object { $_.name -match "$upxFileNamePattern" }) | Select-Object -ExpandProperty browser_download_url;
48 | Invoke-WebRequest -Uri $upxDownloadURL -OutFile "$cwd\build\upx.zip";
49 | Expand-Archive -Path "$cwd\build\upx.zip" -DestinationPath "$cwd\build\upx" -Force;
50 | $sourceFolders = Get-ChildItem -Path "$cwd\build\upx\" -Directory -Filter "upx-*";
51 | foreach ($folder in $sourceFolders) {
52 | $exeFiles = Get-ChildItem -Path $folder.FullName -Filter "*.exe";
53 | foreach ($file in $exeFiles) {
54 | Move-Item -Path $file.FullName -Destination "$cwd\build\ahk\" -Force;
55 | }
56 | }
57 |
58 | Write-Host "Downloaded the latest release from upx into `"$cwd\build\upx\`"" -ForegroundColor Green;
59 |
60 | # Download Wix3
61 | $wixFileNamePattern = "wix314-binaries.zip";
62 | $wixLatestRelease = Invoke-RestMethod -Uri "https://api.github.com/repos/wixtoolset/wix3/releases/latest";
63 | $wixDownloadURL = ($wixLatestRelease.assets | Where-Object { $_.name -match "$wixFileNamePattern" }) | Select-Object -ExpandProperty browser_download_url;
64 | Invoke-WebRequest -Uri $wixDownloadURL -OutFile "$cwd\build\$wixFileNamePattern";
65 | Expand-Archive -Path "$cwd\build\$wixFileNamePattern" -DestinationPath "$cwd\build\wix\";
66 |
67 | Write-Host "Downloaded the latest release from Wix3 into `"$cwd\build\wix\`"" -ForegroundColor Green;
68 |
69 | # Build polygon executables
70 | Start-Process -FilePath "$cwd\build\ahk\Ahk2Exe.exe" -NoNewWindow -Wait -ArgumentList "/in polygon.ahk /out ""$cwd\build\polygon-x86.exe"" /compress 2 /icon logo.ico /base ""$cwd\build\ahk\AutoHotkey32.exe""";
71 | Start-Process -FilePath "$cwd\build\ahk\Ahk2Exe.exe" -NoNewWindow -Wait -ArgumentList "/in polygon.ahk /out ""$cwd\build\polygon-x64.exe"" /compress 2 /icon logo.ico /base ""$cwd\build\ahk\AutoHotkey64.exe""";
72 |
73 | Write-Host "Polygon binaries built successfully into `"$cwd\build\`"" -ForegroundColor Green;
74 |
75 | # Generate checksums
76 | $checksumFile = "$cwd\build\polygon-checksum.txt";
77 | $hash_x86 = Get-FileHash -Path "$cwd\build\polygon-x86.exe" -Algorithm SHA256;
78 | $hash_x64 = Get-FileHash -Path "$cwd\build\polygon-x64.exe" -Algorithm SHA256;
79 | "polygon-x86.exe: $($hash_x86.Hash)".ToLower() | Out-File -Append -FilePath $checksumFile;
80 | "polygon-x64.exe: $($hash_x64.Hash)".ToLower() | Out-File -Append -FilePath $checksumFile;
81 |
82 | Write-Host "Polygon binary checksums calculated successfully into `"$cwd\build\`"" -ForegroundColor Green;
83 |
84 | # Create Polygon archives
85 | Compress-Archive -Path "$cwd\polygon.ini","$cwd\build\polygon-x86.exe" -DestinationPath "$cwd\build\polygon-x86.zip";
86 | Compress-Archive -Path "$cwd\polygon.ini","$cwd\build\polygon-x64.exe" -DestinationPath "$cwd\build\polygon-x64.zip";
87 |
88 | Write-Host "Polygon archives successfully generated into `"$cwd\build\`"" -ForegroundColor Green;
89 |
90 | # Build polygon installer object file
91 | Start-Process -FilePath "$cwd\build\wix\candle.exe" -NoNewWindow -Wait -ArgumentList ".\polygon.wxs -out ""$cwd\build\polygon-x86.wixobj"" -dVersion=""$versionNumber"" -arch x86";
92 | Start-Process -FilePath "$cwd\build\wix\candle.exe" -NoNewWindow -Wait -ArgumentList ".\polygon.wxs -out ""$cwd\build\polygon-x64.wixobj"" -dVersion=""$versionNumber"" -arch x64";
93 |
94 | # Build polygon installer MSI
95 | Start-Process -FilePath "$cwd\build\wix\light.exe" -NoNewWindow -Wait -ArgumentList """$cwd\build\polygon-x86.wixobj"" -ext WixUIExtension -out ""$cwd\build\polygon-x86.msi""";
96 | Start-Process -FilePath "$cwd\build\wix\light.exe" -NoNewWindow -Wait -ArgumentList """$cwd\build\polygon-x64.wixobj"" -ext WixUIExtension -out ""$cwd\build\polygon-x64.msi""";
97 |
98 | Write-Host "Polygon installers built successfully into `"$cwd\build\`"" -ForegroundColor Green;
99 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Polygon
2 |
3 | A [Mac Rectangle](https://github.com/rxhanson/Rectangle) inspired window manager for Windows 10/11 powered by AutoHotkey.
4 |
5 | 
6 |
7 | > [!NOTE]
8 | > Polygon is still a work in progress and aims to have 100% feature compatibility with Rectangle.
9 |
10 | ## System Requirements
11 |
12 | Polygon supports Windows 10/11.
13 |
14 | ## Installation
15 |
16 | ### Installer
17 |
18 | You can grab the latest Polygon installer from the latest [release](https://github.com/thesobercoder/polygon/releases/latest). The installer will place a shortcut to your user startup folder to ensure that Polygon start at Windows logon.
19 |
20 | ### Manual
21 |
22 | If you rather want to install Polygon manually, please follow the steps outlined below.
23 |
24 | - Download the compiled executable zip file based on your OS architecture from the latest [release](https://github.com/thesobercoder/polygon/releases/latest).
25 | - Extract the `polygon-x[64|86].zip` into a location of your choice.
26 | - Right click the executable and unblock it if Windows complain about security.
27 | - Open the user startup folder by running the `shell:startup` command in the run (WIN+R) window.
28 | - Copy the `polygon-x[64|86].exe` and the `polygon.ini` that you downloaded from the previous step and copy them to the `shell:startup` directory.
29 |
30 | If you followed everything correctly, Polygon should start at Windows logon.
31 |
32 | ## Usage
33 |
34 | Here are the shortcuts available in Polygon.
35 |
36 | > [!NOTE]
37 | > Some of the shortcuts Polygon uses conflicts with Window in-built shortcuts and has been mentioned below. If you want to avoid these conflicts, please use the `polygon.ini` file to configure a different shortcut of your choosing by referring to the AutoHotkey v2 [List of Keys](https://www.autohotkey.com/docs/v2/KeyList.htm) documentation.
38 |
39 | | Status | Shortcut | Layout | Conflicts |
40 | | ------------------ | ------------------------------------------- | ------------------- | --------------- |
41 | | :white_check_mark: | CTRL+WIN+C | Center |
42 | | :white_check_mark: | CTRL+WIN+/ | Center HD | |
43 | | :white_check_mark: | CTRL+WIN+W | Center Half | |
44 | | :white_check_mark: | CTRL+WIN+[ | Left Half | |
45 | | :white_check_mark: | CTRL+WIN+] | Right Half | |
46 | | :white_check_mark: | CTRL+WIN+R | Center Two Third | |
47 | | :white_check_mark: | CTRL+WIN+D | First Third | Virtual Desktop |
48 | | :white_check_mark: | CTRL+WIN+F | Center Third | Feedback Hub |
49 | | :white_check_mark: | CTRL+WIN+G | Last Third | |
50 | | :white_check_mark: | CTRL+WIN+Z | Top Left Sixth | |
51 | | :white_check_mark: | CTRL+WIN+X | Bottom Left Sixth | |
52 | | :white_check_mark: | CTRL+WIN+V | Top Right Sixth | |
53 | | :white_check_mark: | CTRL+WIN+B | Bottom Right Sixth | |
54 | | :white_check_mark: | CTRL+WIN+N | Top Center Sixth | Narrator |
55 | | :white_check_mark: | CTRL+WIN+M | Bottom Center Sixth | Magnifier |
56 | | :white_check_mark: | CTRL+WIN+- | Top Half | |
57 | | :white_check_mark: | CTRL+WIN+= | Bottom Half | |
58 | | :white_check_mark: | CTRL+WIN+A | First Two Thirds | |
59 | | :white_check_mark: | CTRL+WIN+H | Last Two Thirds | |
60 | | :white_check_mark: | CTRL+WIN+U | Top Left | |
61 | | :white_check_mark: | CTRL+WIN+I | Top Right | |
62 | | :white_check_mark: | CTRL+WIN+J | Bottom Left | |
63 | | :white_check_mark: | CTRL+WIN+K | Bottom Right | |
64 | | :white_check_mark: | CTRL+WIN+; | First Fourth | |
65 | | :white_check_mark: | CTRL+WIN+' | Second Fourth | |
66 | | :white_check_mark: | CTRL+WIN+, | Third Fourth | |
67 | | :white_check_mark: | CTRL+WIN+. | Last Fourth | |
68 | | :construction: | | Next Display | |
69 | | :construction: | | Previous Display | |
70 |
71 | ## Bugs and feature requests
72 |
73 | Have a bug or a feature request, please open a new issue.
74 |
75 | ## Contributing
76 |
77 | Contributions are most welcome to fix bugs or to add new features.
78 |
79 | ### Environment
80 |
81 | - You need to have [AutoHotkey v2](https://github.com/AutoHotkey/AutoHotkey) installed.
82 | - I recommend using Visual Studio Code or other VS Code based editors like Cursor.
83 | - You need to have the `AutoHotkey v2 Language Support` extension installed by searching with the `thqby.vscode-autohotkey2-lsp` extension ID.
84 | - If AutoHotkey is located in a different location other than `C:\Program Files\AutoHotkey\v2\AutoHotkey.exe`, then set the `AutoHotkey2.InterpreterPath` setting to point to the correct location.
85 | - You need to have the `EditorConfig for VS Code` extension installed by searching with the `EditorConfig.EditorConfig` extension ID.
86 | - You need to have the `Prettier - Code formatter` extension installed by searching with the `esbenp.prettier-vscode` extension ID.
87 | - Add the following lines to your VS Code settings.json file to enable AutoHotkey v2 language support.
88 | ```json
89 | {
90 | "AutoHotkey2.InterpreterPath": "",
91 | "AutoHotkey2.Warn.LocalSameAsGlobal": true,
92 | "AutoHotkey2.Warn.CallWithoutParentheses": "On"
93 | }
94 | ```
95 |
96 | ### Structure
97 |
98 | The repository is structured pretty flat to keep things simple and find all necessary files easily. Polygon is developed as a single AHK file named `polygon.ahk` for now. I may in the future refactor it into multiple files if the push comes to shove.
99 |
100 | ### Commit Convention
101 |
102 | Before you create a Pull Request, please check whether your commits comply with the commit conventions used in this repository.
103 |
104 | When you create a commit I kindly ask you to follow the convention `category(scope): message` in your commit message while using one of the following categories:
105 |
106 | - `feat`: all changes that introduce completely new code or new features
107 | - `fix`: changes that fix a bug (ideally you will additionally reference an issue if present)
108 | - `refactor`: any code related change that is not a fix nor a feature
109 | - `docs`: changing existing or creating new documentation (i.e. README, docs for usage of a lib or cli usage)
110 | - `build`: all changes regarding the build of the software, changes to dependencies or the addition of new dependencies
111 | - `ci`: all changes regarding the configuration of continuous integration (i.e. github actions, ci system)
112 | - `chore`: all changes to the repository that do not fit into any of the above categories
113 |
114 | ### Release
115 |
116 | Please follow the following release checklist and convention.
117 |
118 | - :exclamation: The changes for the new version have been added to the `CHANGELOG.md` file.
119 | - :exclamation: The new version has been updated in the `polygon.ahk` file.
120 | - :exclamation: When cutting a release, please use tags with semver like `v*.*.*` format.
121 |
122 | Use the following release note body for each release.
123 |
124 | ```md
125 | Please refer to [CHANGELOG.md](https://github.com/thesobercoder/polygon/blob/main/CHANGELOG.md) for details.
126 | ```
127 |
128 | ## License
129 |
130 | Licensed under the [MIT license](https://github.com/thesobercoder/polygon/blob/main/LICENSE).
131 |
--------------------------------------------------------------------------------
/polygon.ahk:
--------------------------------------------------------------------------------
1 | #Requires AutoHotkey v2.0
2 | #SingleInstance
3 | #DllLoad Dwmapi.dll
4 | #DllLoad User32.dll
5 |
6 | ;-- Ahk2Exe properties
7 | ;@Ahk2Exe-SetName Polygon
8 | ;@Ahk2Exe-SetVersion 0.7.0
9 | ;@Ahk2Exe-SetCompanyName Soham Dasgupta
10 | ;@Ahk2Exe-SetDescription A window manager for Windows 10/11 powered by AutoHotkey
11 |
12 | ;-- Globals
13 | global APP_VERSION := "0.7.0"
14 | global APP_VERSION_NAME := "v" . APP_VERSION
15 | global APP_NAME := "Polygon"
16 | global APP_REPO_OWNER := "thesobercoder"
17 | global APP_REPO_NAME := "polygon"
18 | global APP_URL := "https://github.com/" . APP_REPO_OWNER . "/" . APP_REPO_NAME
19 | global APP_FEEDBACK_URL := APP_URL . "/issues/new"
20 | global APP_UPDATE_URL := APP_URL . "/releases/latest"
21 | global APP_INI_FILE := "polygon.ini"
22 | global APP_INI_SECTION_SHORTCUT := "Shortcut"
23 | global APP_INI_SECTION_TOAST := "Toast"
24 | global APP_SETTING_ISTOASTENABLED := IniRead(APP_INI_FILE, APP_INI_SECTION_TOAST, "Show", "0") == "1" ? true : false
25 | global APP_SHORTCUT_CENTER := IniRead(APP_INI_FILE, APP_INI_SECTION_SHORTCUT, "Center", "")
26 | global APP_SHORTCUT_CENTERHD := IniRead(APP_INI_FILE, APP_INI_SECTION_SHORTCUT, "CenterHD", "")
27 | global APP_SHORTCUT_CENTERHALF := IniRead(APP_INI_FILE, APP_INI_SECTION_SHORTCUT, "CenterHalf", "")
28 | global APP_SHORTCUT_CENTERTWOTHIRD := IniRead(APP_INI_FILE, APP_INI_SECTION_SHORTCUT, "CenterTwoThird", "")
29 | global APP_SHORTCUT_FIRSTTWOTHIRD := IniRead(APP_INI_FILE, APP_INI_SECTION_SHORTCUT, "FirstTwoThird", "")
30 | global APP_SHORTCUT_LASTTWOTHIRD := IniRead(APP_INI_FILE, APP_INI_SECTION_SHORTCUT, "LastTwoThird", "")
31 | global APP_SHORTCUT_FIRSTTHIRD := IniRead(APP_INI_FILE, APP_INI_SECTION_SHORTCUT, "FirstThird", "")
32 | global APP_SHORTCUT_CENTERTHIRD := IniRead(APP_INI_FILE, APP_INI_SECTION_SHORTCUT, "CenterThird", "")
33 | global APP_SHORTCUT_LASTTHIRD := IniRead(APP_INI_FILE, APP_INI_SECTION_SHORTCUT, "LastThird", "")
34 | global APP_SHORTCUT_TOPLEFTSIXTH := IniRead(APP_INI_FILE, APP_INI_SECTION_SHORTCUT, "TopLeftSixth", "")
35 | global APP_SHORTCUT_BOTTOMLEFTSIXTH := IniRead(APP_INI_FILE, APP_INI_SECTION_SHORTCUT, "BottomLeftSixth", "")
36 | global APP_SHORTCUT_TOPRIGHTSIXTH := IniRead(APP_INI_FILE, APP_INI_SECTION_SHORTCUT, "TopRightSixth", "")
37 | global APP_SHORTCUT_BOTTOMRIGHTSIXTH := IniRead(APP_INI_FILE, APP_INI_SECTION_SHORTCUT, "BottomRightSixth", "")
38 | global APP_SHORTCUT_TOPCENTERSIXTH := IniRead(APP_INI_FILE, APP_INI_SECTION_SHORTCUT, "TopCenterSixth", "")
39 | global APP_SHORTCUT_BOTTOMCENTERSIXTH := IniRead(APP_INI_FILE, APP_INI_SECTION_SHORTCUT, "BottomCenterSixth", "")
40 | global APP_SHORTCUT_LEFTHALF := IniRead(APP_INI_FILE, APP_INI_SECTION_SHORTCUT, "LeftHalf", "")
41 | global APP_SHORTCUT_RIGHTHALF := IniRead(APP_INI_FILE, APP_INI_SECTION_SHORTCUT, "RightHalf", "")
42 | global APP_SHORTCUT_TOPLEFT := IniRead(APP_INI_FILE, APP_INI_SECTION_SHORTCUT, "TopLeft", "")
43 | global APP_SHORTCUT_TOPRIGHT := IniRead(APP_INI_FILE, APP_INI_SECTION_SHORTCUT, "TopRight", "")
44 | global APP_SHORTCUT_BOTTOMLEFT := IniRead(APP_INI_FILE, APP_INI_SECTION_SHORTCUT, "BottomLeft", "")
45 | global APP_SHORTCUT_BOTTOMRIGHT := IniRead(APP_INI_FILE, APP_INI_SECTION_SHORTCUT, "BottomRight", "")
46 | global APP_SHORTCUT_TOPHALF := IniRead(APP_INI_FILE, APP_INI_SECTION_SHORTCUT, "TopHalf", "")
47 | global APP_SHORTCUT_BOTTOMHALF := IniRead(APP_INI_FILE, APP_INI_SECTION_SHORTCUT, "BottomHalf", "")
48 | global APP_SHORTCUT_FIRSTFOURTH := IniRead(APP_INI_FILE, APP_INI_SECTION_SHORTCUT, "FirstFourth", "")
49 | global APP_SHORTCUT_SECONDFOURTH := IniRead(APP_INI_FILE, APP_INI_SECTION_SHORTCUT, "SecondFourth", "")
50 | global APP_SHORTCUT_THIRDFOURTH := IniRead(APP_INI_FILE, APP_INI_SECTION_SHORTCUT, "ThirdFourth", "")
51 | global APP_SHORTCUT_LASTFOURTH := IniRead(APP_INI_FILE, APP_INI_SECTION_SHORTCUT, "LastFourth", "")
52 | ;--Tooltip
53 | A_IconTip := APP_NAME
54 | ;-- Register global error logging
55 | OnError(LogError)
56 | ;-- On startup check for version update
57 | CheckForUpdate()
58 | ;-- Context Menu
59 | tray := A_TrayMenu
60 | tray.Delete()
61 | tray.Add("Help", ShowHelp)
62 | tray.Add("Version", ShowVersion)
63 | tray.Add("Check for Updates", CheckForUpdate)
64 | tray.Add("Feedback", SubmitFeedback)
65 | tray.Add("Restart", Restart)
66 | tray.Add("Exit", Terminate)
67 | tray.Default := "Version"
68 | LogError(exception, mode) {
69 | ; Get the user's application data folder
70 | PolygonDataFolder := A_AppData . "\Polygon"
71 | PolygonLogPath := PolygonDataFolder . "\errorlog.txt"
72 | ; Create the folder if it doesn't exist
73 | if (!DirExist(PolygonDataFolder))
74 | {
75 | DirCreate(PolygonDataFolder)
76 | }
77 | ; Append the error message to the log file
78 | FileAppend("Error on line " . exception.Line . ": " . exception.Message . "`n", PolygonLogPath)
79 | ; Display a message to the user
80 | result := MsgBox('Polygon encountered an error. The error has been logged in the "errorlog.txt" file. Open folder location?', APP_NAME, "YesNo Iconx")
81 | if (result == "Yes")
82 | {
83 | Run(PolygonDataFolder)
84 | }
85 | return true
86 | }
87 | Toast(Message, r, l, t, b) {
88 | ;-- Return if toast is not enabled
89 | if (!APP_SETTING_ISTOASTENABLED)
90 | return
91 | ;-- Calculate the center of the current monitor
92 | centerX := Ceil((l + r) / 2)
93 | centerY := Ceil((b + t) / 2)
94 | title := APP_NAME . " 08ab0337-daeb-4b9c-b01d-11fbc97e1dcb"
95 | hWnd := WinExist(title)
96 | if (hWnd > 0)
97 | WinClose(hWnd)
98 | toastGui := Gui()
99 | toastGui.Opt("+ToolWindow -Caption +AlwaysOnTop +Disabled +E0x20")
100 | toastGui.BackColor := "000000"
101 | toastGui.SetFont("cFFFFFF S18", "Verdana")
102 | toastGui.add("Text", "Center X0 Y90 W278 H210", Message)
103 | toastGui.Title := title
104 | toastGui.Show("X" (centerX - 139) " Y" (centerY - 55) " H210 W278 NA")
105 | WinSetRegion("0-0 H210 W278 R30-30", title)
106 | WinSetExStyle(32, title)
107 | Loop 60
108 | {
109 | hWnd := WinExist(title)
110 | if (hWnd < 1)
111 | break
112 |
113 | if (A_Index = 1)
114 | {
115 | WinSetTransparent(120, hWnd)
116 | Sleep(100)
117 | }
118 | else if (A_Index = 60)
119 | {
120 | toastGui.Destroy()
121 | break
122 | }
123 | else
124 | {
125 | TransFade := 120 - A_Index * 2
126 | WinSetTransparent(TransFade, hWnd)
127 | Sleep(1)
128 | }
129 | }
130 | }
131 | ParseVersionString(version) {
132 | regex := "v(\d+)\.(\d+)\.(\d+)"
133 | if (RegExMatch(version, regex, &parsedVersion))
134 | {
135 | if (parsedVersion.Count > 0)
136 | {
137 | ;-- Extract the version here
138 | major := parsedVersion[1]
139 | minor := parsedVersion[2]
140 | patch := parsedVersion[3]
141 | return Number(major) * 10000 + Number(minor) * 100 + Number(patch)
142 | }
143 | }
144 | return 0
145 | }
146 | CheckForUpdate(args*) {
147 | version := GetLatestGitHubRelease(APP_REPO_OWNER, APP_REPO_NAME)
148 | ; Check if the request was successful
149 | if (version)
150 | {
151 | newVersion := ParseVersionString(version)
152 | currentVersion := ParseVersionString(APP_VERSION_NAME)
153 | ;-- Check if the latest version is greater
154 | if (newVersion > currentVersion)
155 | {
156 | result := MsgBox("A new version " . newVersion . " is available. Do you want to update?", APP_NAME, "YesNo Iconi")
157 | if (result == "Yes")
158 | {
159 | Run(APP_UPDATE_URL)
160 | return
161 | }
162 | }
163 | }
164 | if (args && args.Length > 0 && args[1] == "Check for Updates")
165 | {
166 | MsgBox("You already have the latest version.", APP_NAME, "Iconi")
167 | }
168 | }
169 | GetLatestGitHubRelease(owner, repo) {
170 | req := ComObject("Msxml2.XMLHTTP")
171 | req.open("GET", "https://api.github.com/repos/" . owner . "/" . repo . "/releases/latest", false)
172 | req.send()
173 | if req.status != 200
174 | {
175 | MsgBox("Error checking for update. Please try after some time.", APP_NAME, "Iconx")
176 | return
177 | }
178 | res := JSONParse(req.responseText)
179 | return res.tag_name
180 | JSONParse(str)
181 | {
182 | htmlfile := ComObject("HTMLFile")
183 | htmlfile.write('')
184 | return htmlfile.parentWindow.JSON.parse(str)
185 | }
186 | }
187 | SubmitFeedback(*) {
188 | Run(APP_FEEDBACK_URL)
189 | }
190 | Restart(*) {
191 | Reload()
192 | }
193 | ShowHelp(*) {
194 | Run(APP_URL)
195 | }
196 | Terminate(*) {
197 | ExitApp(0)
198 | }
199 | ShowVersion(*) {
200 | MsgBox("Version " . APP_VERSION, APP_NAME, "Iconi")
201 | }
202 | SetConditionalHotkey(shortcut, func) {
203 | if (shortcut && shortcut != "") {
204 | Hotkey(shortcut, func)
205 | }
206 | }
207 | ;-- Map Hotkeys using SetConditionalHotkey function
208 | SetConditionalHotkey(APP_SHORTCUT_CENTER, Center)
209 | SetConditionalHotkey(APP_SHORTCUT_CENTERHD, CenterHD)
210 | SetConditionalHotkey(APP_SHORTCUT_CENTERHALF, CenterHalf)
211 | SetConditionalHotkey(APP_SHORTCUT_CENTERTWOTHIRD, CenterTwoThird)
212 | SetConditionalHotkey(APP_SHORTCUT_FIRSTTHIRD, FirstThird)
213 | SetConditionalHotkey(APP_SHORTCUT_CENTERTHIRD, CenterThird)
214 | SetConditionalHotkey(APP_SHORTCUT_FIRSTTWOTHIRD, FirstTwoThird)
215 | SetConditionalHotkey(APP_SHORTCUT_LASTTWOTHIRD, LastTwoThird)
216 | SetConditionalHotkey(APP_SHORTCUT_LASTTHIRD, LastThird)
217 | SetConditionalHotkey(APP_SHORTCUT_TOPLEFTSIXTH, TopLeftSixth)
218 | SetConditionalHotkey(APP_SHORTCUT_BOTTOMLEFTSIXTH, BottomLeftSixth)
219 | SetConditionalHotkey(APP_SHORTCUT_TOPRIGHTSIXTH, TopRightSixth)
220 | SetConditionalHotkey(APP_SHORTCUT_BOTTOMRIGHTSIXTH, BottomRightSixth)
221 | SetConditionalHotkey(APP_SHORTCUT_TOPCENTERSIXTH, TopCenterSixth)
222 | SetConditionalHotkey(APP_SHORTCUT_BOTTOMCENTERSIXTH, BottomCenterSixth)
223 | SetConditionalHotkey(APP_SHORTCUT_LEFTHALF, LeftHalf)
224 | SetConditionalHotkey(APP_SHORTCUT_RIGHTHALF, RightHalf)
225 | SetConditionalHotkey(APP_SHORTCUT_TOPLEFT, TopLeft)
226 | SetConditionalHotkey(APP_SHORTCUT_TOPRIGHT, TopRight)
227 | SetConditionalHotkey(APP_SHORTCUT_BOTTOMLEFT, BottomLeft)
228 | SetConditionalHotkey(APP_SHORTCUT_BOTTOMRIGHT, BottomRight)
229 | SetConditionalHotkey(APP_SHORTCUT_TOPHALF, TopHalf)
230 | SetConditionalHotkey(APP_SHORTCUT_BOTTOMHALF, BottomHalf)
231 | SetConditionalHotkey(APP_SHORTCUT_FIRSTFOURTH, FirstFourth)
232 | SetConditionalHotkey(APP_SHORTCUT_SECONDFOURTH, SecondFourth)
233 | SetConditionalHotkey(APP_SHORTCUT_THIRDFOURTH, ThirdFourth)
234 | SetConditionalHotkey(APP_SHORTCUT_LASTFOURTH, LastFourth)
235 | ;-- Layout Functions
236 | Center(*) {
237 | if (GetWindowRectEx(&hWnd, &x, &y, &w, &h, &ofl, &ofr, &oft, &ofb, &r, &l, &t, &b))
238 | {
239 | ;-- Calculate the center of the current monitor
240 | centerX := Ceil(((l + r) - (ofl + ofr)) / 2)
241 | centerY := Ceil(((t + b) - (ofb + oft)) / 2)
242 | ;-- Move the active window to the center of the current monitor
243 | WinMoveEx(centerX - w / 2, centerY - h / 2, w + ofl + ofr, h + oft + ofb, hWnd)
244 | ;-- Show layout toast
245 | Toast("Center", r, l, t, b)
246 | }
247 | }
248 | CenterHD(*) {
249 | if (GetWindowRectEx(&hWnd, &x, &y, &w, &h, &ofl, &ofr, &oft, &ofb, &r, &l, &t, &b))
250 | {
251 | ;-- Set desired window dimension
252 | rw := Min(1920, ((r + ofr) - (l + ofl)))
253 | rh := Min(1080, ((b + ofb) - (t + oft)))
254 | ;-- Calculate the center of the current monitor with desired size
255 | centerX := Ceil(((l + r) - (ofl + ofr)) / 2)
256 | centerY := Ceil(((t + b) - (ofb + oft)) / 2)
257 | ;-- Move the active window to the center of the current monitor with desired size
258 | WinMoveEx(Ceil(centerX - rw / 2), Ceil(centerY - rh / 2), rw + ofl + ofr, rh + oft + ofb, hWnd)
259 | ;-- Show layout toast
260 | Toast("Center HD", r, l, t, b)
261 | }
262 | }
263 | CenterHalf(*) {
264 | if (GetWindowRectEx(&hWnd, &x, &y, &w, &h, &ofl, &ofr, &oft, &ofb, &r, &l, &t, &b))
265 | {
266 | ;-- Calculate the width of half of the monitor
267 | HalfWidth := Ceil((r - l) / 2)
268 | ;-- Calculate the horizontal position for centering
269 | CenterX := (l - ofl) + Ceil(((r - ofr) - (l - ofl)) / 2) - Ceil(HalfWidth / 2)
270 | ;-- Set the window position to the center half of the monitor
271 | WinMoveEx(CenterX, t - oft, HalfWidth + ofl + ofr, (b - t) + oft + ofb, hWnd)
272 | ;-- Show layout toast
273 | Toast("Center Half", r, l, t, b)
274 | }
275 | }
276 | CenterTwoThird(*) {
277 | if (GetWindowRectEx(&hWnd, &x, &y, &w, &h, &ofl, &ofr, &oft, &ofb, &r, &l, &t, &b))
278 | {
279 | ;-- Calculate two third width of screen
280 | TwoThirdWidth := Ceil((r - l) * 2 / 3)
281 | ;-- Calculate horizontal position for centering
282 | CenterX := (l - ofl) + Ceil(((r - ofr) - (l - ofl)) / 2) - Ceil(TwoThirdWidth / 2)
283 | ;-- Set window position to center two third width
284 | WinMoveEx(CenterX, t - oft, TwoThirdWidth + ofl + ofr, (b - t) + oft + ofb, hWnd)
285 | ;-- Show layout toast
286 | Toast("Center Two Third", r, l, t, b)
287 | }
288 | }
289 | FirstTwoThird(*) {
290 | if (GetWindowRectEx(&hWnd, &x, &y, &w, &h, &ofl, &ofr, &oft, &ofb, &r, &l, &t, &b))
291 | {
292 | ;-- Calculate two third width of screen
293 | TwoThirdWidth := Ceil((r - l) * 2 / 3)
294 | ;-- Set the window position to the left first two third of the monitor
295 | WinMoveEx(l - ofl, t - oft, TwoThirdWidth + ofr + ofl, (b - t) + oft + ofb, hWnd)
296 | ;-- Show layout toast
297 | Toast("First Two Third", r, l, t, b)
298 | }
299 | }
300 | LastTwoThird(*) {
301 | if (GetWindowRectEx(&hWnd, &x, &y, &w, &h, &ofl, &ofr, &oft, &ofb, &r, &l, &t, &b))
302 | {
303 | ;-- Calculate two third width of screen
304 | TwoThirdWidth := Ceil((r - l) * 2 / 3)
305 | ;-- Calculate horizontal position for right aligning
306 | RightX := (r - ofr) - TwoThirdWidth
307 | ;-- Set window position to right one third of monitor
308 | WinMoveEx(RightX, t - oft, TwoThirdWidth + ofl + ofr, (b - t) + oft + ofb, hWnd)
309 | ;-- Show layout toast
310 | Toast("Last Two Third", r, l, t, b)
311 | }
312 | }
313 | FirstThird(*) {
314 | if (GetWindowRectEx(&hWnd, &x, &y, &w, &h, &ofl, &ofr, &oft, &ofb, &r, &l, &t, &b))
315 | {
316 | ;-- Calculate the width of one third of the monitor
317 | OneThirdWidth := Ceil((r - l) / 3)
318 | ;-- Set the window position to the left one third of the monitor
319 | WinMoveEx(l - ofl, t - oft, OneThirdWidth + ofr + ofl, (b - t) + oft + ofb, hWnd)
320 | ;-- Show layout toast
321 | Toast("First Third", r, l, t, b)
322 | }
323 | }
324 | CenterThird(*) {
325 | if (GetWindowRectEx(&hWnd, &x, &y, &w, &h, &ofl, &ofr, &oft, &ofb, &r, &l, &t, &b))
326 | {
327 | ;-- Calculate the width of one third of the monitor
328 | OneThirdWidth := Ceil((r - l) / 3)
329 | ;-- Calculate the horizontal position for centering
330 | CenterX := (l - ofl) + Ceil(((r - ofr) - (l - ofl)) / 2) - Ceil(OneThirdWidth / 2)
331 | ;-- Set the window position to the center one third of the monitor
332 | WinMoveEx(CenterX, t - oft, OneThirdWidth + ofl + ofr, (b - t) + oft + ofb, hWnd)
333 | ;-- Show layout toast
334 | Toast("Center Third", r, l, t, b)
335 | }
336 | }
337 | LastThird(*) {
338 | if (GetWindowRectEx(&hWnd, &x, &y, &w, &h, &ofl, &ofr, &oft, &ofb, &r, &l, &t, &b))
339 | {
340 | ;-- Calculate width of one third of monitor
341 | OneThirdWidth := Ceil((r - l) / 3)
342 | ;-- Calculate horizontal position for right aligning
343 | RightX := (r - ofr) - OneThirdWidth
344 | ;-- Set window position to right one third of monitor
345 | WinMoveEx(RightX, t - oft, OneThirdWidth + ofl + ofr, (b - t) + oft + ofb, hWnd)
346 | ;-- Show layout toast
347 | Toast("Last Third", r, l, t, b)
348 | }
349 | }
350 | TopLeftSixth(*) {
351 | if (GetWindowRectEx(&hWnd, &x, &y, &w, &h, &ofl, &ofr, &oft, &ofb, &r, &l, &t, &b))
352 | {
353 | ;-- Calculate the width of one third of the monitor
354 | OneThirdWidth := Ceil((r - l) / 3)
355 | ;-- Set the window position to the left one third of the monitor and top half of it
356 | WinMoveEx(l - ofl, t - oft, OneThirdWidth + ofl + ofb, Ceil((b - t) / 2) + oft + ofb, hWnd)
357 | ;-- Show layout toast
358 | Toast("Top Left Sixth", r, l, t, b)
359 | }
360 | }
361 | BottomLeftSixth(*) {
362 | if (GetWindowRectEx(&hWnd, &x, &y, &w, &h, &ofl, &ofr, &oft, &ofb, &r, &l, &t, &b))
363 | {
364 | ;-- Calculate the width of one third of the monitor
365 | OneThirdWidth := Ceil((r - l) / 3)
366 | ;-- Set the window position to left one third of monitor and bottom half of it
367 | WinMoveEx(l - ofl, Ceil(b - (b - t) / 2), OneThirdWidth + ofl + ofr, Ceil((b - t) / 2) + oft + ofb, hWnd)
368 | ;-- Show layout toast
369 | Toast("Bottom Left Sixth", r, l, t, b)
370 | }
371 | }
372 | TopRightSixth(*) {
373 | if (GetWindowRectEx(&hWnd, &x, &y, &w, &h, &ofl, &ofr, &oft, &ofb, &r, &l, &t, &b))
374 | {
375 | ;-- Calculate width of one third of monitor
376 | OneThirdWidth := Ceil((r - l) / 3)
377 | ;-- Calculate horizontal position for right aligning
378 | RightX := (r - ofr) - OneThirdWidth
379 | ;-- Set window position to right one third of monitor and top half of it.
380 | WinMoveEx(RightX, t - oft, OneThirdWidth + ofl + ofr, Ceil((b - t) / 2) + oft + ofb, hWnd)
381 | ;-- Show layout toast
382 | Toast("Top Right Sixth", r, l, t, b)
383 | }
384 | }
385 | BottomRightSixth(*) {
386 | if (GetWindowRectEx(&hWnd, &x, &y, &w, &h, &ofl, &ofr, &oft, &ofb, &r, &l, &t, &b))
387 | {
388 | ;-- Calculate width of one third of monitor
389 | OneThirdWidth := Ceil((r - l) / 3)
390 | ;-- Calculate horizontal position for right aligning
391 | RightX := (r - ofr) - OneThirdWidth
392 | ;-- Set window position to right one third of monitor and bottom half of it
393 | WinMoveEx(RightX, Ceil(b - (b - t) / 2), OneThirdWidth + ofl + ofr, Ceil((b - t) / 2) + oft + ofb, hWnd)
394 | ;-- Show layout toast
395 | Toast("Bottom Right Sixth", r, l, t, b)
396 | }
397 | }
398 | TopCenterSixth(*) {
399 | if (GetWindowRectEx(&hWnd, &x, &y, &w, &h, &ofl, &ofr, &oft, &ofb, &r, &l, &t, &b))
400 | {
401 | ;-- Calculate the width of one third of the monitor
402 | OneThirdWidth := Ceil((r - l) / 3)
403 | ;-- Calculate the horizontal position for centering
404 | CenterX := (l - ofl) + Ceil(((r - ofr) - (l - ofl)) / 2) - Ceil(OneThirdWidth / 2)
405 | ;-- Set the window position to top center one sixth of the monitor
406 | WinMoveEx(CenterX, t - oft, OneThirdWidth + ofl + ofr, Ceil((b - t) / 2) + oft + ofb, hWnd)
407 | ;-- Show layout toast
408 | Toast("Top Center Sixth", r, l, t, b)
409 | }
410 | }
411 | BottomCenterSixth(*) {
412 | if (GetWindowRectEx(&hWnd, &x, &y, &w, &h, &ofl, &ofr, &oft, &ofb, &r, &l, &t, &b))
413 | {
414 | ;-- Calculate the width of one third of the monitor
415 | OneThirdWidth := Ceil((r - l) / 3)
416 | ;-- Calculate the horizontal position for centering
417 | CenterX := (l - ofl) + Ceil(((r - ofr) - (l - ofl)) / 2) - Ceil(OneThirdWidth / 2)
418 | ;-- Set the window position to bottom center one sixth of the monitor
419 | WinMoveEx(CenterX, Ceil(b - (b - t) / 2), OneThirdWidth + ofl + ofr, Ceil((b - t) / 2) + oft + ofb, hWnd)
420 | ;-- Show layout toast
421 | Toast("Bottom Center Sixth", r, l, t, b)
422 | }
423 | }
424 | LeftHalf(*) {
425 | if (GetWindowRectEx(&hWnd, &x, &y, &w, &h, &ofl, &ofr, &oft, &ofb, &r, &l, &t, &b))
426 | {
427 | ;-- Calculate the width of half of the monitor
428 | HalfWidth := Ceil((r - l) / 2)
429 | ;-- Set the window position to the left half of the monitor
430 | WinMoveEx(l - ofl, t - oft, HalfWidth + ofl + ofr, (b - t) + oft + ofb, hWnd)
431 | ;-- Show layout toast
432 | Toast("Left Half", r, l, t, b)
433 | }
434 | }
435 | RightHalf(*) {
436 | if (GetWindowRectEx(&hWnd, &x, &y, &w, &h, &ofl, &ofr, &oft, &ofb, &r, &l, &t, &b))
437 | {
438 | ;-- Calculate the width of half of the monitor
439 | HalfWidth := Ceil((r - l) / 2)
440 | ;-- Calculate horizontal position for right aligning
441 | RightX := (r - ofr) - HalfWidth
442 | ;-- Set the window position to the right half of the monitor
443 | WinMoveEx(RightX, t - oft, HalfWidth + ofl + ofr, (b - t) + oft + ofb, hWnd)
444 | ;-- Show layout toast
445 | Toast("Right Half", r, l, t, b)
446 | }
447 | }
448 | TopLeft(*) {
449 | if (GetWindowRectEx(&hWnd, &x, &y, &w, &h, &ofl, &ofr, &oft, &ofb, &r, &l, &t, &b))
450 | {
451 | ;-- Calculate the width as half of the monitor
452 | HalfWidth := Ceil((r - l) / 2)
453 | ;-- Set the window position to the left half of the monitor and top half of it
454 | WinMoveEx(l - ofl, t - oft, HalfWidth + ofl + ofr, Ceil((b - t) / 2) + oft + ofb, hWnd)
455 | ;-- Show layout toast
456 | Toast("Top Left", r, l, t, b)
457 | }
458 | }
459 | TopRight(*) {
460 | if (GetWindowRectEx(&hWnd, &x, &y, &w, &h, &ofl, &ofr, &oft, &ofb, &r, &l, &t, &b))
461 | {
462 | ;-- Calculate the width as half of the monitor
463 | HalfWidth := Ceil((r - l) / 2)
464 | ;-- Calculate horizontal position for right aligning
465 | RightX := (r - ofr) - HalfWidth
466 | ;-- Set the window position to start from the middle of the monitor and extend to the very right edge
467 | WinMoveEx(RightX, t - oft, r - l - HalfWidth + ofl + ofr, Ceil((b - t) / 2) + oft + ofb, hWnd)
468 | ;-- Show layout toast
469 | Toast("Top Right", r, l, t, b)
470 | }
471 | }
472 | BottomLeft(*) {
473 | if (GetWindowRectEx(&hWnd, &x, &y, &w, &h, &ofl, &ofr, &oft, &ofb, &r, &l, &t, &b))
474 | {
475 | ;-- Calculate the width of half of the monitor
476 | HalfWidth := Ceil((r - l) / 2)
477 | ;-- Calculate the height of half of the monitor
478 | HalfHeight := Ceil((b - t) / 2)
479 | ;-- Set the window position to left one third of monitor and bottom half of it
480 | WinMoveEx(l - ofl, Ceil(b - (b - t) / 2), HalfWidth + ofl + ofr, HalfHeight + oft + ofb, hWnd)
481 | ;-- Show layout toast
482 | Toast("Bottom Left", r, l, t, b)
483 | }
484 | }
485 | BottomRight(*) {
486 | if (GetWindowRectEx(&hWnd, &x, &y, &w, &h, &ofl, &ofr, &oft, &ofb, &r, &l, &t, &b))
487 | {
488 | ;-- Calculate the width as half of the monitor
489 | HalfWidth := Ceil((r - l) / 2)
490 | ;-- Calculate horizontal position for right aligning
491 | RightX := (r - ofr) - HalfWidth
492 | ;-- Set the window position to the right half of the monitor and bottom half of it
493 | WinMoveEx(RightX, Ceil(b - (b - t) / 2), HalfWidth + ofl + ofr, Ceil((b - t) / 2) + oft + ofb, hWnd)
494 | ;-- Show layout toast
495 | Toast("Bottom Right", r, l, t, b)
496 | }
497 | }
498 | TopHalf(*) {
499 | if (GetWindowRectEx(&hWnd, &x, &y, &w, &h, &ofl, &ofr, &oft, &ofb, &r, &l, &t, &b))
500 | {
501 | ;-- Calculate the height of half of the monitor
502 | HalfHeight := Ceil((b - t) / 2)
503 | ;-- Set the window position to the left half of the monitor and top half of it
504 | WinMoveEx(l - ofl, t - oft, Ceil((r - l)) + ofl + ofr, HalfHeight + oft + ofb, hWnd)
505 | ;-- Show layout toast
506 | Toast("Top Half", r, l, t, b)
507 | }
508 | }
509 | BottomHalf(*) {
510 | if (GetWindowRectEx(&hWnd, &x, &y, &w, &h, &ofl, &ofr, &oft, &ofb, &r, &l, &t, &b))
511 | {
512 | ;-- Calculate the height of half of the monitor
513 | HalfHeight := Ceil((b - t) / 2)
514 | ;-- Set the window position to the left half of the monitor and top half of it
515 | WinMoveEx(l - ofl, Ceil(b - (b - t) / 2), Ceil((r - l)) + ofl + ofr, HalfHeight + oft + ofb, hWnd)
516 | ;-- Show layout toast
517 | Toast("Bottom Half", r, l, t, b)
518 | }
519 | }
520 | FirstFourth(*) {
521 | if (GetWindowRectEx(&hWnd, &x, &y, &w, &h, &ofl, &ofr, &oft, &ofb, &r, &l, &t, &b))
522 | {
523 | ;-- Calculate the width of one fourth of the monitor
524 | OneFourthWidth := Ceil((r - l) / 4)
525 | ;-- Set the window position to the left one fourth of the monitor
526 | WinMoveEx(l - ofl, t - oft, OneFourthWidth + ofr + ofl, (b - t) + oft + ofb, hWnd)
527 | ;-- Show layout toast
528 | Toast("First Fourth", r, l, t, b)
529 | }
530 | }
531 | SecondFourth(*) {
532 | if (GetWindowRectEx(&hWnd, &x, &y, &w, &h, &ofl, &ofr, &oft, &ofb, &r, &l, &t, &b))
533 | {
534 | ; Calculate the width of one-fourth of the monitor
535 | OneFourthWidth := Ceil((r - l) / 4)
536 | ; Set the window position to the left one-fourth of the monitor
537 | WinMoveEx(l - ofl + OneFourthWidth, t - oft, OneFourthWidth + ofr + ofl, (b - t) + oft + ofb, hWnd)
538 | ; Show layout toast
539 | Toast("Second Fourth", r, l, t, b)
540 | }
541 | }
542 | ThirdFourth(*) {
543 | if (GetWindowRectEx(&hWnd, &x, &y, &w, &h, &ofl, &ofr, &oft, &ofb, &r, &l, &t, &b))
544 | {
545 | ; Calculate the width of one-fourth of the monitor
546 | OneFourthWidth := Ceil((r - l) / 4)
547 | ; Set the window position to the right one-fourth of the monitor
548 | WinMoveEx(l - ofl + 2 * OneFourthWidth, t - oft, OneFourthWidth + ofr + ofl, (b - t) + oft + ofb, hWnd)
549 | ; Show layout toast
550 | Toast("Third Fourth", r, l, t, b)
551 | }
552 | }
553 | LastFourth(*) {
554 | if (GetWindowRectEx(&hWnd, &x, &y, &w, &h, &ofl, &ofr, &oft, &ofb, &r, &l, &t, &b))
555 | {
556 | ;-- Calculate the width of one fourth of the monitor
557 | OneFourthWidth := Ceil((r - l) / 4)
558 | ;-- Set the window position to the right one fourth of the monitor
559 | WinMoveEx(r - OneFourthWidth - ofr, t - oft, OneFourthWidth + ofr + ofl, (b - t) + oft + ofb, hWnd)
560 | ;-- Show layout toast
561 | Toast("Last Fourth", r, l, t, b)
562 | }
563 | }
564 | WinMoveEx(X := 0, Y := 0, Width := 0, Height := 0, hWnd := 0) {
565 | ;-- Restore the window before moving
566 | WinRestore(hWnd)
567 | ;-- Move the window to the desired position and dimension
568 | WinMove(X, Y, Width, Height, hWnd)
569 | }
570 | GetWindowRectEx(&hWindow := 0, &winX := 0, &winY := 0, &winW := 0, &winH := 0, &winOffsetLeft := 0, &winOffsetRight := 0, &winOffsetTop := 0, &winOffsetBottom := 0, &monRight := 0, &monLeft := 0, &monTop := 0, &monBottom := 0) {
571 | ;-- Get the handle of the active window
572 | hWindow := WinExist("A")
573 | if (hWindow > 0)
574 | {
575 | ;-- Get the number of monitors
576 | MonitorCount := MonitorGetCount()
577 | ;-- Get the dimensions of the current window
578 | WinGetPosEx(hWindow, &winX, &winY, &winW, &winH, &winOffsetLeft, &winOffsetRight, &winOffsetTop, &winOffsetBottom)
579 | ;-- Loop through each monitor to find which one contains the active window
580 | Loop MonitorCount
581 | {
582 | ;-- Get the dimensions of the current monitor
583 | MonitorGetWorkArea(A_Index, &monLeft, &monTop, &monRight, &monBottom)
584 | ;-- Check if the active window is within the current monitor
585 | if (CheckWindowWithinMonitor(winX, winY, winW, winH, winOffsetLeft, winOffsetRight, winOffsetTop, winOffsetBottom, monRight, monLeft, monTop, monBottom))
586 | {
587 | return true
588 | }
589 | }
590 | }
591 | return false
592 | }
593 | CheckWindowWithinMonitor(winX, winY, winW, winH, winOffsetLeft, winOffsetRight, winOffsetTop, winOffsetBottom, monRight, monLeft, monTop, monBottom) {
594 | ; Calculate the coordinates of the corners of the window
595 | winLeft := winX + winOffsetLeft
596 | winRight := winX + winW - winOffsetRight
597 | winTop := winY + winOffsetTop
598 | winBottom := winY + winH - winOffsetBottom
599 | ; Calculate the area of intersection between the window and the monitor
600 | intersectionArea := (min(winRight, monRight) - max(winLeft, monLeft)) * (min(winBottom, monBottom) - max(winTop, monTop))
601 | ; Calculate the total area of the window
602 | windowArea := winW * winH
603 | ; Check if more than 50% of the window is within the monitor
604 | if (Round(intersectionArea / windowArea, 1) > 0.5)
605 | {
606 | return true
607 | }
608 | return false
609 | }
610 | WinGetPosEx(hWindow, &winX := 0, &winY := 0, &winW := 0, &winH := 0, &winOffsetLeft := 0, &winOffsetRight := 0, &winOffsetTop := 0, &winOffsetBottom := 0) {
611 | Static RECTPlus, DWMWA_EXTENDED_FRAME_BOUNDS := 9
612 | ;-- Workaround for AutoHotkey Basic
613 | PtrType := (A_PtrSize = 8) ? "Ptr" : "UInt"
614 | ;-- Get the window's dimensions
615 | ;-- Note: Only the first 16 bytes of the RECTPlus structure are used by the
616 | ;-- DwmGetWindowAttribute and GetWindowRect functions.
617 | RECTPlus := Buffer(32, 0)
618 | DllCall("Dwmapi.dll\DwmGetWindowAttribute", PtrType, hWindow, "UInt", DWMWA_EXTENDED_FRAME_BOUNDS, PtrType, RECTPlus, "UInt", 16)
619 | ;-- Populate the output variables
620 | winX := Left := NumGet(RECTPlus, 0, "Int")
621 | winY := Top := NumGet(RECTPlus, 4, "Int")
622 | Right := NumGet(RECTPlus, 8, "Int")
623 | Bottom := NumGet(RECTPlus, 12, "Int")
624 | winW := Right - Left
625 | winH := Bottom - Top
626 | winOffsetLeft := 0
627 | winOffsetRight := 0
628 | winOffsetTop := 0
629 | winOffsetBottom := 0
630 | ;-- Collect dimensions via GetWindowRect
631 | RECT := Buffer(16, 0)
632 | DllCall("User32.dll\GetWindowRect", PtrType, hWindow, PtrType, RECT)
633 | GWR_Left := NumGet(RECT, 0, "Int")
634 | GWR_Top := NumGet(RECT, 4, "Int")
635 | GWR_Right := NumGet(RECT, 8, "Int")
636 | GWR_Bottom := NumGet(RECT, 12, "Int")
637 | ;-- Calculate offsets and update output variables
638 | NumPut("Int", winOffsetLeft := Left - GWR_Left, RECTPlus, 16)
639 | NumPut("Int", winOffsetTop := Top - GWR_Top, RECTPlus, 20)
640 | NumPut("Int", winOffsetRight := GWR_Right - Right, RECTPlus, 24)
641 | NumPut("Int", winOffsetBottom := GWR_Bottom - Bottom, RECTPlus, 28)
642 | Return &RECTPlus
643 | }
--------------------------------------------------------------------------------