-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathknob.cpp
153 lines (127 loc) · 3.53 KB
/
knob.cpp
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
47
48
49
50
51
52
53
54
55
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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#include "knob.h"
#include <utility>
#include <ostream>
#include <optional>
#include <atlbase.h>
#include <atlwin.h>
#include "error_handling.h"
void ThrowGleErrorIfFalse (bool val, const char* msg, std::source_location location = std::source_location::current())
{
if (!val) [[unlikely]]
{
struct GleError final : TraceableException
{
explicit GleError (DWORD gle, const char* msg, std::source_location location):
TraceableException {msg, std::move (location)},
m_gle {gle}
{
}
protected:
// TraceableException
virtual void Format (std::ostream& os) const override
{
char err[256] = {};
::FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_MAX_WIDTH_MASK, nullptr, m_gle, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), err, std::size (err) - 1, nullptr);
os << "0x" << std::hex << m_gle << std::dec << " - \"" << err << '"';
}
private:
const DWORD m_gle;
};
throw GleError {::GetLastError(), msg, std::move (location)};
}
}
template <class CRTP>
class HotkeyWindow : public CWindowImpl<HotkeyWindow<CRTP>, CWindow, CWinTraits<WS_POPUP>>
{
public:
BEGIN_MSG_MAP(Impl)
MESSAGE_HANDLER(WM_HOTKEY, OnHotKey)
END_MSG_MAP()
HotkeyWindow()
{
this->Create (nullptr);
}
~HotkeyWindow()
{
// WTL needs this
if (GetHwnd())
this->DestroyWindow();
}
public:
auto GetHwnd() const
{
return this->m_hWnd;
}
private:
LRESULT OnHotKey (UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
static_cast<CRTP*>(this)->OnHotKey (static_cast<size_t>(wParam));
return 0;
}
};
enum class HandlerId
{
Push = 0,
SpinLeft,
SpinRight,
Last
};
class Knob::Impl : public HotkeyWindow<Impl>
{
public:
void SetHandler (Handler handler, UINT vk, HandlerId id)
{
m_handlers[static_cast<size_t>(id)].emplace (std::move (handler), vk, static_cast<size_t> (id), GetHwnd());
}
void OnHotKey (size_t id) noexcept
{
if (id < std::size (m_handlers) && m_handlers[id])
m_handlers[id]->Trigger();
}
private:
struct HotKey
{
public:
explicit HotKey (Handler handler, UINT vk, size_t id, HWND hWnd):
m_handler {std::move (handler)}, m_id (id), m_hWnd {hWnd}
{
ThrowGleErrorIfFalse (::RegisterHotKey (m_hWnd, m_id, 0, vk), "Hotkey registretion");
}
~HotKey()
{
::UnregisterHotKey (m_hWnd, m_id);
}
public:
void Trigger() noexcept
{
IgnoreException (m_handler);
}
private:
const Handler m_handler;
const size_t m_id;
const HWND m_hWnd;
};
std::optional<HotKey> m_handlers[static_cast<size_t>(HandlerId::Last)];
};
Knob::Knob()
try
:m_impl {std::make_unique<Impl>()}
{
}
catch (const std::bad_alloc&)
{
std::throw_with_nested (std::runtime_error {"Bad alloc while Knob::Impl construction"});
}
Knob::~Knob() = default;
void Knob::SetPushHandler (Handler handler)
{
m_impl->SetHandler (std::move (handler), VK_F20, HandlerId::Push);
}
void Knob::SetSpinLeftHandler (Handler handler)
{
m_impl->SetHandler (std::move (handler), VK_F18, HandlerId::SpinLeft);
}
void Knob::SetSpinRightHandler (Handler handler)
{
m_impl->SetHandler (std::move (handler), VK_F19, HandlerId::SpinRight);
}