Created
April 30, 2023 09:33
-
-
Save mridgers/8e88a7fb4b428d460e4efef57dd2839d to your computer and use it in GitHub Desktop.
Three column window placement using hot keys
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* building; | |
* cl.exe thirds.cpp /std:c++17 /EHsc /Zi /Ox /link /subsystem:windows | |
* | |
* using; | |
* Win-Ctrl-A : [-1/3-:-1/3-:-1/3-] | |
* Win-Ctrl-Z : [-1/3-:----2/3----] + fat middle | |
* Win-Ctrl-W : toggles between top/bottom halves of column from 'A' | |
*/ | |
#include <dwmapi.h> | |
#include <shellscalingapi.h> | |
#include <vector> | |
#include <Windows.h> | |
#pragma comment(lib, "user32") | |
#pragma comment(lib, "shcore") | |
#pragma comment(lib, "dwmapi") | |
//------------------------------------------------------------------------------ | |
struct Point | |
{ | |
Point operator - (const Point& r) const { return { x - r.x, y - r.y }; } | |
int length_sq() const { return (x * x) + (y * y); } | |
int x, y; | |
}; | |
//------------------------------------------------------------------------------ | |
struct Box | |
{ | |
operator bool () const { return get_dims().x == 0; } | |
operator RECT* () { return (RECT*)this; } | |
Point get_origin() const { return { left, top }; } | |
Point get_dims() const { return { right - left, bottom - top }; } | |
Point get_centre() const { return { (left + right) / 2, (top + bottom) / 2 }; } | |
void expand(int d) { left -= d; top -= d; right += d; bottom += d; } | |
int left, top, right, bottom; | |
}; | |
static_assert(sizeof(Box) == sizeof(RECT)); | |
//------------------------------------------------------------------------------ | |
static void three_columns(std::vector<Box>& boxes, const Box& monitor) | |
{ | |
auto [x, y] = monitor.get_origin(); | |
auto [w, h] = monitor.get_dims(); | |
int third_w = w / 3; | |
int xs[4] = { | |
x, | |
x + third_w, | |
x + (third_w * 2), | |
x + w, | |
}; | |
boxes.push_back({ xs[0], y, xs[1], y + h }); | |
boxes.push_back({ xs[1], y, xs[2], y + h }); | |
boxes.push_back({ xs[2], y, xs[3], y + h }); | |
for (auto& box : boxes) | |
box.expand(1); | |
} | |
//------------------------------------------------------------------------------ | |
static void two_thirds(std::vector<Box>& boxes, const Box& monitor) | |
{ | |
auto [x, y] = monitor.get_origin(); | |
auto [w, h] = monitor.get_dims(); | |
int third_w = w / 3; | |
int d; | |
if ((w * 10) / h < 16) | |
d = (third_w * 4) / 9; | |
else | |
d = (third_w * 2) / 10; | |
boxes.push_back({ x, y, x + third_w, y + h }); | |
boxes.push_back({ x + third_w - d, y, x + (third_w * 2) + d, y + h }); | |
boxes.push_back({ x + third_w, y, x + w, y + h }); | |
for (auto& box : boxes) | |
box.expand(1); | |
} | |
//------------------------------------------------------------------------------ | |
static int get_closest(const std::vector<Box>& boxes, const Box& box, int threshold=INT_MAX) | |
{ | |
Point point = box.get_centre(); | |
const Box* best = nullptr; | |
int dist = INT_MAX; | |
for (int i = 0, n = boxes.size(); i < n; ++i) | |
{ | |
const Box& box = boxes[i]; | |
int candidate = (box.get_centre() - point).length_sq(); | |
if (candidate < threshold && candidate < dist) | |
{ | |
best = &box; | |
dist = candidate; | |
} | |
} | |
if (best == nullptr) | |
return -1; | |
return int(uintptr_t(best - &(boxes[0]))); | |
} | |
//------------------------------------------------------------------------------ | |
static Box get_monitor_box(HWND handle) | |
{ | |
HMONITOR monitor = MonitorFromWindow(handle, MONITOR_DEFAULTTONEAREST); | |
if (monitor == nullptr) | |
return {}; | |
MONITORINFO info = { sizeof(info) }; | |
GetMonitorInfoW(monitor, &info); | |
const RECT& rect = info.rcWork; | |
return { rect.left, rect.top, rect.right, rect.bottom }; | |
} | |
//------------------------------------------------------------------------------ | |
static void on_hotkey(HWND handle, int key) | |
{ | |
Box monitor_box = get_monitor_box(handle); | |
if (!monitor_box) | |
return; | |
if (IsZoomed(handle)) | |
ShowWindow(handle, SW_RESTORE); | |
std::vector<Box> boxes; | |
switch (key) | |
{ | |
case 'A': | |
case 'W': three_columns(boxes, monitor_box); break; | |
case 'Z': two_thirds(boxes, monitor_box); break; | |
default: return; | |
} | |
Box window_box; | |
GetWindowRect(handle, window_box); | |
int box_index = -1; | |
if (key != 'W') | |
{ | |
box_index = get_closest(boxes, window_box, 100); | |
if (box_index != -1) | |
box_index = (box_index + 1) % boxes.size(); | |
} | |
if (box_index == -1) | |
box_index = get_closest(boxes, window_box); | |
if (unsigned(box_index) >= unsigned(boxes.size())) | |
{ | |
ShowWindowAsync(handle, SW_SHOWMAXIMIZED); | |
return; | |
} | |
Box box = boxes[box_index]; | |
if (key == 'W') | |
{ | |
int half_height = box.get_dims().y / 2; | |
if (window_box.top < half_height - 100) | |
{ | |
if (window_box.bottom < half_height + 100) | |
box.top += half_height; | |
else | |
box.bottom -= half_height; | |
} | |
} | |
Point tl = box.get_origin(); | |
Point wh = box.get_dims(); | |
// to get perfect alignment we need to adjust for whatever DWM is up to | |
Box dwm_box; | |
if (SUCCEEDED(DwmGetWindowAttribute(handle, DWMWA_EXTENDED_FRAME_BOUNDS, dwm_box, sizeof(dwm_box)))) | |
{ | |
int left_offset = dwm_box.left - window_box.left; | |
tl.x -= left_offset; | |
wh.x += left_offset - (dwm_box.right - window_box.right); | |
wh.y += window_box.bottom - dwm_box.bottom; | |
} | |
SetWindowPos(handle, nullptr, tl.x, tl.y, wh.x, wh.y, SWP_ASYNCWINDOWPOS); | |
} | |
//------------------------------------------------------------------------------ | |
static int go() | |
{ | |
SetCurrentDirectoryW(L"c:/"); | |
enum { ID = 0x04930000 }; | |
int modifiers = MOD_WIN|MOD_CONTROL|MOD_NOREPEAT; | |
for (int key : { 'A', 'W', 'Z' }) | |
if (!RegisterHotKey(nullptr, ID|key, modifiers, key)) | |
return 1; | |
SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); | |
MSG message; | |
while (GetMessage(&message, nullptr, 0, 0) > 0) | |
{ | |
if (message.message != WM_HOTKEY) | |
continue; | |
int hotkey_id = (message.wParam & 0x7fff0000); | |
if (hotkey_id != ID) | |
continue; | |
if (HWND handle = GetForegroundWindow()) | |
{ | |
int hotkey_key = (message.wParam & 0xffff); | |
on_hotkey(handle, hotkey_key); | |
} | |
} | |
return 0; | |
} | |
//------------------------------------------------------------------------------ | |
int main(int, char**) { return go(); } | |
int CALLBACK WinMain(HINSTANCE, HINSTANCE, LPSTR, int) { return go(); } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment