Last active
December 4, 2017 23:42
-
-
Save sayurin/c86c790d63ae326f74583c28f316fc83 to your computer and use it in GitHub Desktop.
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
#pragma once | |
#include <array> | |
#include <type_traits> | |
#include <Windows.h> | |
#include <windowsx.h> | |
template<int... controls> | |
struct Controls; | |
template<class RightAnchoredControls, class BottomAnchoredControls, class StretchingControls> | |
class Resizable; | |
template<int... anchorRight, int... anchorBottom, int... anchorStretch> | |
class Resizable<Controls<anchorRight...>, Controls<anchorBottom...>, Controls<anchorStretch...>> { | |
static const UINT flags = SWP_NOZORDER | SWP_NOREDRAW | SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOSENDCHANGING | SWP_DEFERERASE | SWP_ASYNCWINDOWPOS; | |
SIZE minimum; | |
SIZE current; | |
public: | |
Resizable() : current{} {} | |
Resizable(SIZE current) : current{ current } {} | |
SIZE getCurrent() const { return current; } | |
void onSize(HWND dialog, LONG cx, LONG cy) { | |
LONG dx = cx - current.cx, dy = cy - current.cy; | |
if (dx != 0) | |
for (auto id : std::array<int, sizeof...(anchorRight)>{ anchorRight... }) { | |
auto control = GetDlgItem(dialog, id); | |
RECT r; | |
GetWindowRect(control, &r); | |
POINT p{ r.left, r.top }; | |
ScreenToClient(dialog, &p); | |
SetWindowPos(control, 0, p.x + dx, p.y, 0, 0, SWP_NOSIZE | flags); | |
} | |
if (dy != 0) | |
for (auto id : std::array<int, sizeof...(anchorBottom)>{ anchorBottom... }) { | |
auto control = GetDlgItem(dialog, id); | |
RECT r; | |
GetWindowRect(control, &r); | |
POINT p{ r.left, r.top }; | |
ScreenToClient(dialog, &p); | |
SetWindowPos(control, 0, p.x, p.y + dy, 0, 0, SWP_NOSIZE | flags); | |
} | |
if (dx != 0 || dy != 0) | |
for (auto id : std::array<int, sizeof...(anchorStretch)>{ anchorStretch... }) { | |
auto control = GetDlgItem(dialog, id); | |
RECT r; | |
GetWindowRect(control, &r); | |
SetWindowPos(control, 0, 0, 0, r.right - r.left + dx, r.bottom - r.top + dy, SWP_NOMOVE | flags); | |
} | |
current = { cx, cy }; | |
InvalidateRect(dialog, nullptr, FALSE); | |
} | |
void onSizing(HWND dialog, RECT* targetSize, int edge) { | |
if (targetSize->right - targetSize->left < minimum.cx) { | |
if (edge == WMSZ_LEFT || edge == WMSZ_TOPLEFT || edge == WMSZ_BOTTOMLEFT) | |
targetSize->left = targetSize->right - minimum.cx; | |
else | |
targetSize->right = targetSize->left + minimum.cx; | |
} | |
if (targetSize->bottom - targetSize->top < minimum.cy) { | |
if (edge == WMSZ_TOP || edge == WMSZ_TOPLEFT || edge == WMSZ_TOPRIGHT) | |
targetSize->top = targetSize->bottom - minimum.cy; | |
else | |
targetSize->bottom = targetSize->top + minimum.cy; | |
} | |
onSize(dialog, targetSize->right - targetSize->left, targetSize->bottom - targetSize->top); | |
} | |
void initialize(HWND dialog) { | |
RECT r; | |
GetWindowRect(dialog, &r); | |
minimum = { r.right - r.left, r.bottom - r.top }; | |
if (current.cx == 0) | |
current = minimum; | |
else { | |
auto copied = current; | |
current = minimum; | |
SetWindowPos(dialog, 0, 0, 0, copied.cx, copied.cy, SWP_NOMOVE | flags); | |
} | |
} | |
}; | |
namespace detail { | |
template<class Data> | |
class Dialog { | |
template<class T> static constexpr auto hasOnInit() -> decltype(std::declval<T>().OnInit(HWND{}), true) { return true; } | |
template<class T> static constexpr auto hasOnInit(...) { return false; } | |
template<class T> static constexpr auto hasOnCommand1() -> decltype(std::declval<T>().OnCommand(HWND{}, WORD{}), true) { return true; } | |
template<class T> static constexpr auto hasOnCommand1(...) { return false; } | |
template<class T> static constexpr auto hasOnCommand2() -> decltype(std::declval<T>().OnCommand(HWND{}, WORD{}, WORD{}), true) { return true; } | |
template<class T> static constexpr auto hasOnCommand2(...) { return false; } | |
template<class T> static constexpr auto hasOnNotify() -> decltype(std::declval<T>().OnNotify(HWND{}, std::declval<NMHDR*>()), true) { return true; } | |
template<class T> static constexpr auto hasOnNotify(...) { return false; } | |
template<class T> static constexpr auto hasOnMessage() -> decltype(std::declval<T>().OnMessage(HWND{}, UINT{}, WPARAM{}, LPARAM{}), true) { return true; } | |
template<class T> static constexpr auto hasOnMessage(...) { return false; } | |
template<class T> static constexpr auto hasResizable() -> decltype(T::resizable, true) { return true; } | |
template<class T> static constexpr auto hasResizable(...) { return false; } | |
public: | |
static INT_PTR CALLBACK proc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { | |
if (uMsg == WM_INITDIALOG) { | |
auto ptr = reinterpret_cast<Data*>(lParam); | |
SetWindowLongPtrW(hwndDlg, GWLP_USERDATA, lParam); | |
auto result = TRUE; | |
if constexpr (hasOnInit<Data>()) | |
result = ptr->OnInit(hwndDlg); | |
if constexpr (hasResizable<Data>()) | |
ptr->resizable.initialize(hwndDlg); | |
return result; | |
} | |
auto get = [hwndDlg] { return reinterpret_cast<Data*>(GetWindowLongPtrW(hwndDlg, GWLP_USERDATA)); }; | |
if constexpr (hasOnCommand1<Data>()) { | |
if (uMsg == WM_COMMAND) | |
return get()->OnCommand(hwndDlg, GET_WM_COMMAND_ID(wParam, lParam)); | |
} else if constexpr (hasOnCommand2<Data>()) { | |
if (uMsg == WM_COMMAND) | |
return get()->OnCommand(hwndDlg, GET_WM_COMMAND_CMD(wParam, lParam), GET_WM_COMMAND_ID(wParam, lParam)); | |
} | |
if constexpr (hasResizable<Data>()) { | |
if (uMsg == WM_SIZING) { | |
get()->resizable.onSizing(hwndDlg, reinterpret_cast<RECT*>(lParam), static_cast<int>(wParam)); | |
return TRUE; | |
} | |
if (uMsg == WM_SIZE) { | |
RECT r; | |
GetWindowRect(hwndDlg, &r); | |
get()->resizable.onSize(hwndDlg, r.right - r.left, r.bottom - r.top); | |
return 0; | |
} | |
} | |
if constexpr (hasOnNotify<Data>()) { | |
if (uMsg == WM_NOTIFY) | |
return get()->OnNotify(hwndDlg, reinterpret_cast<NMHDR*>(lParam)); | |
} | |
if constexpr (hasOnMessage<Data>()) { | |
return get()->OnMessage(hwndDlg, uMsg, wParam, lParam); | |
} | |
return FALSE; | |
} | |
}; | |
} | |
// DialogBoxを表示します。 | |
// 次の要件を満たした型を受け入れます。 | |
// struct Data { | |
// // 必須。ダイアログプロシージャからEndDialog()を呼び出す際に使われる値の型を宣言します。そのままDialog()関数の戻り値になります。 | |
// using result_t = ...; | |
// // 任意。宣言されている場合はダイアログのサイズを可変にします。第1テンプレートパラメーターは右端に固定されるID、第2は下端に固定されるID、第3は伸縮するIDをそれぞれ指定します。 | |
// // resizable.getCurrent()で現在のダイアログサイズを取得できます。 | |
// Resizable<Controls<ID1, ID2, ...>, Controls<ID3, ID4, ...>, Controls<ID5, ID6, ...>> resizable; | |
// // 任意。WM_INITDIALOGメッセージを処理するコールバック。 | |
// INT_PTR OnInit(HWND); | |
// // 任意。WM_COMMANDメッセージを処理するコールバック。第2引数は押されたコマンドのIDです。 | |
// INT_PTR OnCommand(HWND, WORD); | |
// }; | |
template<class Data, class T = std::remove_reference_t<Data>> | |
static inline auto Dialog(HINSTANCE instance, int resourceId, HWND parent, Data&& data) { | |
return (T::result_t)DialogBoxParamW(instance, MAKEINTRESOURCEW(resourceId), parent, detail::Dialog<T>::proc, (LPARAM)&data); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment