Update to v106r57 release.

byuu says:

I've added tool tips to hiro for Windows, GTK, and Qt. I'm unsure how to
add them for Cocoa. I wasted am embarrassing ~14 hours implementing tool
tips from scratch on Windows, because the `TOOLTIPS_CLASS` widget just
absolutely refused to show up, no matter what I tried. As such, they're
not quite 100% native, but I would really appreciate any patch
submissions to help improve my implementation.

I added tool tips to all of the confusing settings in bsnes. And of
course, for those of you who don't like them, there's a configuration
file setting to turn them off globally.

I also improved Mega Drive handling of the Game Genie a bit, and
restructured the way the Settings class works in bsnes.

Starting now, I'm feature-freezing bsnes and higan. From this point
forward:

  - polishing up and fixing bugs caused by the ruby/hiro changes
  - adding DRC to XAudio2, and maybe exclusive mode to WGL
  - correcting FEoEZ (English) to load and work again out of the box

Once that's done, a final beta of bsnes will go out, I'll fix any
reported bugs that I'm able to, and then v107 should be ready. This time
with higan being functional, but marked as v107 beta. v108 will restore
higan to production status again, alongside bsnes.
This commit is contained in:
Tim Allen
2018-08-08 18:46:58 +10:00
parent 3b4e8b6d75
commit 93a6a1ce7e
121 changed files with 2014 additions and 1310 deletions

View File

@@ -61,7 +61,7 @@ auto pApplication::initialize() -> void {
CoInitialize(0);
InitCommonControls();
WNDCLASS wc;
WNDCLASS wc{};
#if defined(Hiro_Window)
wc.cbClsExtra = 0;
@@ -91,43 +91,29 @@ auto pApplication::initialize() -> void {
RegisterClass(&wc);
#endif
#if defined(Hiro_Canvas)
#if defined(Hiro_Widget)
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.hCursor = LoadCursor(0, IDC_ARROW);
wc.hIcon = LoadIcon(0, IDI_APPLICATION);
wc.hInstance = GetModuleHandle(0);
wc.lpfnWndProc = Canvas_windowProc;
wc.lpszClassName = L"hiroCanvas";
wc.lpfnWndProc = ToolTip_windowProc;
wc.lpszClassName = L"hiroToolTip";
wc.lpszMenuName = 0;
wc.style = CS_HREDRAW | CS_VREDRAW;
RegisterClass(&wc);
#endif
#if defined(Hiro_Label)
#if defined(Hiro_Widget)
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.hCursor = LoadCursor(0, IDC_ARROW);
wc.hIcon = LoadIcon(0, IDI_APPLICATION);
wc.hInstance = GetModuleHandle(0);
wc.lpfnWndProc = Label_windowProc;
wc.lpszClassName = L"hiroLabel";
wc.lpszMenuName = 0;
wc.style = CS_HREDRAW | CS_VREDRAW;
RegisterClass(&wc);
#endif
#if defined(Hiro_Viewport)
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hbrBackground = CreateSolidBrush(RGB(0, 0, 0));
wc.hCursor = LoadCursor(0, IDC_ARROW);
wc.hIcon = LoadIcon(0, IDI_APPLICATION);
wc.hInstance = GetModuleHandle(0);
wc.lpfnWndProc = Viewport_windowProc;
wc.lpszClassName = L"hiroViewport";
wc.lpfnWndProc = Default_windowProc;
wc.lpszClassName = L"hiroWidget";
wc.lpszMenuName = 0;
wc.style = CS_HREDRAW | CS_VREDRAW;
RegisterClass(&wc);
@@ -137,6 +123,11 @@ auto pApplication::initialize() -> void {
pWindow::initialize();
}
auto pApplication::state() -> State& {
static State state;
return state;
}
static auto Application_keyboardProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) -> bool {
if(msg != WM_KEYDOWN && msg != WM_SYSKEYDOWN && msg != WM_KEYUP && msg != WM_SYSKEYUP) return false;

View File

@@ -10,6 +10,11 @@ struct pApplication {
static auto setScreenSaver(bool screenSaver) -> void;
static auto initialize() -> void;
struct State {
pToolTip* toolTip = nullptr; //active toolTip
};
static auto state() -> State&;
};
}

View File

@@ -80,3 +80,11 @@
#if !defined(TBS_TRANSPARENTBKGND)
#define TBS_TRANSPARENTBKGND 0x1000
#endif
#if !defined(TTP_STANDARD)
#define TTP_STANDARD 1
#endif
#if !defined(TTSS_NORMAL)
#define TTSS_NORMAL 1
#endif

View File

@@ -27,6 +27,8 @@
#include "sizable.cpp"
#include "tool-tip.cpp"
#include "widget/widget.cpp"
#include "widget/button.cpp"
#include "widget/canvas.cpp"

View File

@@ -49,6 +49,8 @@ static vector<wObject> windows;
#include "sizable.hpp"
#include "tool-tip.hpp"
#include "widget/widget.hpp"
#include "widget/button.hpp"
#include "widget/canvas.hpp"

View File

@@ -13,6 +13,7 @@ auto pSizable::minimumSize() const -> Size {
}
auto pSizable::setGeometry(Geometry geometry) -> void {
self().doSize();
}
}

161
hiro/windows/tool-tip.cpp Normal file
View File

@@ -0,0 +1,161 @@
namespace hiro {
static auto CALLBACK ToolTip_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) -> LRESULT {
if(auto toolTip = (pToolTip*)GetWindowLongPtr(hwnd, GWLP_USERDATA)) {
if(auto result = toolTip->windowProc(hwnd, msg, wparam, lparam)) {
return result();
}
}
return DefWindowProc(hwnd, msg, wparam, lparam);
}
pToolTip::pToolTip(const string& toolTipText) {
text = toolTipText;
htheme = OpenThemeData(hwnd, L"TOOLTIP");
hwnd = CreateWindowEx(
WS_EX_TOOLWINDOW | WS_EX_TOPMOST | (htheme ? WS_EX_LAYERED : 0), L"hiroToolTip", L"",
WS_POPUP, 0, 0, 0, 0,
0, 0, GetModuleHandle(0), 0
);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)this);
tracking.x = -1;
tracking.y = -1;
timeout.setInterval(5000);
timeout.onActivate([&] { hide(); });
}
pToolTip::~pToolTip() {
hide();
if(htheme) { CloseThemeData(htheme); htheme = nullptr; }
if(hwnd) { DestroyWindow(hwnd); hwnd = nullptr; }
}
auto pToolTip::drawLayered() -> void {
auto hdcOutput = GetDC(nullptr);
uint32_t* below = nullptr;
auto hdcBelow = CreateCompatibleDC(hdcOutput);
auto hbmBelow = CreateBitmap(hdcBelow, size.cx, size.cy, below);
SelectObject(hdcBelow, hbmBelow);
RECT rc{};
rc.left = 0, rc.top = 0, rc.right = size.cx, rc.bottom = size.cy;
DrawThemeBackground(htheme, hdcBelow, TTP_STANDARD, TTSS_NORMAL, &rc, nullptr);
uint32_t* above = nullptr;
auto hdcAbove = CreateCompatibleDC(hdcOutput);
auto hbmAbove = CreateBitmap(hdcAbove, size.cx, size.cy, above);
SelectObject(hdcAbove, hbmAbove);
memory::copy<uint32_t>(above, below, size.cx * size.cy);
auto hfont = pFont::create(Font());
SelectObject(hdcAbove, hfont);
SetBkMode(hdcAbove, TRANSPARENT);
SetTextColor(hdcAbove, RGB(0, 0, 0));
utf16_t drawText(text);
rc.left += 6, rc.top += 6, rc.right -= 6, rc.bottom -= 6;
DrawText(hdcAbove, drawText, -1, &rc, DT_LEFT | DT_TOP);
DeleteObject(hfont);
for(uint n : range(size.cx * size.cy)) {
below[n] = (below[n] & 0xff000000) | (above[n] & 0x00ffffff);
}
BLENDFUNCTION blend{};
blend.BlendOp = AC_SRC_OVER;
blend.SourceConstantAlpha = 255;
blend.AlphaFormat = AC_SRC_ALPHA;
POINT zero{};
zero.x = 0, zero.y = 0;
UpdateLayeredWindow(hwnd, hdcOutput, &position, &size, hdcBelow, &zero, RGB(0, 0, 0), &blend, ULW_ALPHA);
DeleteObject(hbmAbove);
DeleteObject(hbmBelow);
DeleteDC(hdcAbove);
DeleteDC(hdcBelow);
ReleaseDC(nullptr, hdcOutput);
}
auto pToolTip::drawOpaque() -> void {
PAINTSTRUCT ps;
BeginPaint(hwnd, &ps);
RECT rc{};
GetClientRect(hwnd, &rc);
auto brush = CreateSolidBrush(RGB(0, 0, 0));
FillRect(ps.hdc, &rc, brush);
DeleteObject(brush);
rc.left += 1, rc.top += 1, rc.right -= 1, rc.bottom -= 1;
brush = CreateSolidBrush(RGB(255, 255, 225));
FillRect(ps.hdc, &rc, brush);
DeleteObject(brush);
rc.left += 5, rc.top += 5, rc.right -= 5, rc.bottom -= 5;
SetBkMode(ps.hdc, TRANSPARENT);
auto font = pFont::create(Font());
SelectObject(ps.hdc, font);
SetTextColor(ps.hdc, RGB(0, 0, 0));
DrawText(ps.hdc, utf16_t(text), -1, &rc, DT_LEFT | DT_TOP);
DeleteObject(font);
EndPaint(hwnd, &ps);
}
auto pToolTip::show() -> void {
if(auto toolTip = pApplication::state().toolTip) {
if(toolTip != this) toolTip->hide();
}
pApplication::state().toolTip = this;
GetCursorPos(&position);
if(position.x == tracking.x && position.y == tracking.y) return;
tracking.x = position.x, tracking.y = position.y;
position.y += 18;
auto textSize = pFont::size(Font(), text ? text : " ");
size.cx = 12 + textSize.width();
size.cy = 12 + textSize.height();
//try to keep the tool-tip onscreen
auto desktop = pDesktop::size();
if(position.x + size.cx >= desktop.width ()) position.x = desktop.width () - size.cx;
if(position.y + size.cy >= desktop.height()) position.y = desktop.height() - size.cy;
if(position.x < 0) position.x = 0;
if(position.y < 0) position.y = 0;
SetWindowPos(hwnd, HWND_TOP, position.x, position.y, size.cx, size.cy, SWP_NOACTIVATE | SWP_SHOWWINDOW);
if(htheme) drawLayered();
timeout.setEnabled(true);
}
auto pToolTip::hide() -> void {
pApplication::state().toolTip = nullptr;
timeout.setEnabled(false);
ShowWindow(hwnd, SW_HIDE);
GetCursorPos(&tracking);
}
auto pToolTip::windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) -> maybe<LRESULT> {
switch(msg) {
case WM_ERASEBKGND:
case WM_PAINT:
if(htheme) break;
drawOpaque();
return msg == WM_ERASEBKGND;
case WM_MOUSEMOVE:
case WM_MOUSELEAVE: {
POINT point{};
GetCursorPos(&point);
if(point.x != tracking.x || point.y != tracking.y) hide();
} break;
case WM_LBUTTONDOWN: case WM_LBUTTONUP:
case WM_MBUTTONDOWN: case WM_MBUTTONUP:
case WM_RBUTTONDOWN: case WM_RBUTTONUP:
hide();
break;
}
return {};
}
}

22
hiro/windows/tool-tip.hpp Normal file
View File

@@ -0,0 +1,22 @@
namespace hiro {
struct pToolTip {
pToolTip(const string& text);
~pToolTip();
auto drawLayered() -> void;
auto drawOpaque() -> void;
auto show() -> void;
auto hide() -> void;
auto windowProc(HWND, UINT, WPARAM, LPARAM) -> maybe<LRESULT>;
HWND hwnd = nullptr;
HTHEME htheme = nullptr;
POINT position{};
SIZE size{};
POINT tracking{};
string text;
Timer timeout;
};
}

View File

@@ -7,31 +7,37 @@ static const uint Windows7 = 0x0601;
static auto Button_CustomDraw(HWND, PAINTSTRUCT&, bool, bool, bool, unsigned, const Font&, const image&, Orientation, const string&) -> void;
static auto OsVersion() -> unsigned {
static auto OsVersion() -> uint {
OSVERSIONINFO versionInfo{0};
versionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&versionInfo);
return (versionInfo.dwMajorVersion << 8) + (versionInfo.dwMajorVersion << 0);
}
static auto CreateBitmap(HDC hdc, uint width, uint height, uint32_t*& data) -> HBITMAP {
BITMAPINFO info{};
info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
info.bmiHeader.biWidth = width;
info.bmiHeader.biHeight = -(int)height; //bitmaps are stored upside down unless we negate height
info.bmiHeader.biPlanes = 1;
info.bmiHeader.biBitCount = 32;
info.bmiHeader.biCompression = BI_RGB;
info.bmiHeader.biSizeImage = width * height * sizeof(uint32_t);
void* bits = nullptr;
auto bitmap = CreateDIBSection(hdc, &info, DIB_RGB_COLORS, &bits, nullptr, 0);
data = (uint32_t*)bits;
return bitmap;
}
static auto CreateBitmap(image icon) -> HBITMAP {
icon.alphaMultiply(); //Windows AlphaBlend() requires premultiplied image data
icon.transform();
HDC hdc = GetDC(0);
BITMAPINFO bitmapInfo;
memset(&bitmapInfo, 0, sizeof(BITMAPINFO));
bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bitmapInfo.bmiHeader.biWidth = icon.width();
bitmapInfo.bmiHeader.biHeight = -(signed)icon.height(); //bitmaps are stored upside down unless we negate height
bitmapInfo.bmiHeader.biPlanes = 1;
bitmapInfo.bmiHeader.biBitCount = 32;
bitmapInfo.bmiHeader.biCompression = BI_RGB;
bitmapInfo.bmiHeader.biSizeImage = icon.size();
void* bits = nullptr;
HBITMAP hbitmap = CreateDIBSection(hdc, &bitmapInfo, DIB_RGB_COLORS, &bits, NULL, 0);
if(bits) memory::copy(bits, icon.data(), icon.size());
ReleaseDC(0, hdc);
return hbitmap;
uint32_t* data = nullptr;
auto hdc = GetDC(nullptr);
auto bitmap = CreateBitmap(hdc, icon.width(), icon.height(), data);
memory::copy(data, icon.data(), icon.size());
ReleaseDC(nullptr, hdc);
return bitmap;
}
static auto CreateRGB(const Color& color) -> COLORREF {
@@ -60,9 +66,21 @@ static auto DropPaths(WPARAM wparam) -> vector<string> {
return paths;
}
static auto WINAPI EnumVisibleChildWindowsProc(HWND hwnd, LPARAM lparam) -> BOOL {
auto children = (vector<HWND>*)lparam;
if(IsWindowVisible(hwnd)) children->append(hwnd);
return true;
}
static auto EnumVisibleChildWindows(HWND hwnd) -> vector<HWND> {
vector<HWND> children;
EnumChildWindows(hwnd, EnumVisibleChildWindowsProc, (LPARAM)&children);
return children;
}
static auto GetWindowZOrder(HWND hwnd) -> unsigned {
unsigned z = 0;
for(HWND next = hwnd; next != NULL; next = GetWindow(next, GW_HWNDPREV)) z++;
uint z = 0;
for(HWND next = hwnd; next != nullptr; next = GetWindow(next, GW_HWNDPREV)) z++;
return z;
}
@@ -111,6 +129,10 @@ static auto ScrollEvent(HWND hwnd, WPARAM wparam) -> unsigned {
return info.nPos;
}
static auto CALLBACK Default_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) -> LRESULT {
return DefWindowProc(hwnd, msg, wparam, lparam);
}
//separate because PopupMenu HWND does not contain GWLP_USERDATA pointing at Window needed for Shared_windowProc
static auto CALLBACK Menu_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) -> LRESULT {
switch(msg) {
@@ -383,6 +405,47 @@ static auto CALLBACK Shared_windowProc(WindowProc windowProc, HWND hwnd, UINT ms
break;
}
//catch mouse events over disabled windows
case WM_MOUSEMOVE:
case WM_MOUSELEAVE:
case WM_MOUSEHOVER: {
POINT p{};
GetCursorPos(&p);
ScreenToClient(hwnd, &p);
for(auto window : EnumVisibleChildWindows(hwnd)) {
if(auto widget = (mWidget*)GetWindowLongPtr(window, GWLP_USERDATA)) {
auto geometry = widget->geometry();
if(p.x < geometry.x()) continue;
if(p.y < geometry.y()) continue;
if(p.x >= geometry.x() + geometry.width ()) continue;
if(p.y >= geometry.y() + geometry.height()) continue;
if(msg == WM_MOUSEMOVE) {
TRACKMOUSEEVENT event{sizeof(TRACKMOUSEEVENT)};
event.hwndTrack = hwnd;
event.dwFlags = TME_LEAVE | TME_HOVER;
event.dwHoverTime = 1500;
TrackMouseEvent(&event);
POINT p{};
GetCursorPos(&p);
widget->self()->doMouseMove(p.x, p.y);
if(auto toolTip = pApplication::state().toolTip) {
toolTip->windowProc(hwnd, msg, wparam, lparam);
}
}
if(msg == WM_MOUSELEAVE) {
widget->self()->doMouseLeave();
}
if(msg == WM_MOUSEHOVER) {
widget->self()->doMouseHover();
}
}
}
break;
}
#if defined(Hiro_TableView)
case AppMessage::TableView_doPaint: {
if(auto tableView = (mTableView*)lparam) {
@@ -402,7 +465,7 @@ static auto CALLBACK Shared_windowProc(WindowProc windowProc, HWND hwnd, UINT ms
#endif
}
return windowProc(hwnd, msg, wparam, lparam);
return CallWindowProc(windowProc, hwnd, msg, wparam, lparam);
}
}

View File

@@ -2,49 +2,12 @@
namespace hiro {
static auto Button_paintProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam,
bool bordered, bool checked, bool enabled, const Font& font, const image& icon, Orientation orientation, const string& text
) -> LRESULT {
if(msg == WM_PAINT) {
PAINTSTRUCT ps;
BeginPaint(hwnd, &ps);
auto state = Button_GetState(hwnd);
Button_CustomDraw(hwnd, ps, bordered, checked, enabled, state, font, icon, orientation, text);
EndPaint(hwnd, &ps);
}
return DefWindowProc(hwnd, msg, wparam, lparam);
}
//BUTTON cannot draw borderless buttons on its own
//BS_OWNERDRAW will send WM_DRAWITEM; but will disable hot-tracking notifications
//to gain hot-tracking plus borderless buttons; BUTTON is superclassed and WM_PAINT is hijacked
//note: letting hiro paint bordered buttons will lose the fade animations on Vista+;
//however, it will allow placing icons immediately next to text (original forces icon left alignment)
static auto CALLBACK Button_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) -> LRESULT {
if(auto object = (mObject*)GetWindowLongPtr(hwnd, GWLP_USERDATA)) {
if(auto button = dynamic_cast<mButton*>(object)) {
if(auto self = button->self()) {
if(msg == WM_ERASEBKGND) return DefWindowProc(hwnd, msg, wparam, lparam);
if(msg == WM_PAINT) return Button_paintProc(hwnd, msg, wparam, lparam,
button->state.bordered, false, button->enabled(true), button->font(true),
button->state.icon, button->state.orientation, button->state.text
);
return self->windowProc(hwnd, msg, wparam, lparam);
}
}
}
return DefWindowProc(hwnd, msg, wparam, lparam);
}
auto pButton::construct() -> void {
hwnd = CreateWindow(
L"BUTTON", L"", WS_CHILD | WS_TABSTOP,
0, 0, 0, 0, _parentHandle(), nullptr, GetModuleHandle(0), 0
);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&reference);
windowProc = (WindowProc)GetWindowLongPtr(hwnd, GWLP_WNDPROC);
SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)Button_windowProc);
pWidget::_setState();
pWidget::construct();
_setState();
}
@@ -99,10 +62,35 @@ auto pButton::setVisible(bool visible) -> void {
_setState();
}
//
auto pButton::onActivate() -> void {
self().doActivate();
}
//BUTTON cannot draw borderless buttons on its own
//BS_OWNERDRAW will send WM_DRAWITEM; but will disable hot-tracking notifications
//to gain hot-tracking plus borderless buttons; BUTTON is superclassed and WM_PAINT is hijacked
//note: letting hiro paint bordered buttons will lose the fade animations on Vista+;
//however, it will allow placing icons immediately next to text (original forces icon left alignment)
auto pButton::windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) -> maybe<LRESULT> {
if(msg == WM_PAINT) {
PAINTSTRUCT ps;
BeginPaint(hwnd, &ps);
auto buttonState = Button_GetState(hwnd);
Button_CustomDraw(hwnd, ps,
state().bordered, false, self().enabled(true), buttonState,
self().font(true), state().icon, state().orientation, state().text
);
EndPaint(hwnd, &ps);
return false;
}
return pWidget::windowProc(hwnd, msg, wparam, lparam);
}
//
auto pButton::_setState() -> void {
InvalidateRect(hwnd, 0, false);
}

View File

@@ -15,10 +15,9 @@ struct pButton : pWidget {
auto setVisible(bool visible) -> void override;
auto onActivate() -> void;
auto windowProc(HWND, UINT, WPARAM, LPARAM) -> maybe<LRESULT> override;
auto _setState() -> void;
WindowProc windowProc = nullptr;
};
}

View File

@@ -2,64 +2,9 @@
namespace hiro {
static auto CALLBACK Canvas_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) -> LRESULT {
auto object = (mObject*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
if(!object) return DefWindowProc(hwnd, msg, wparam, lparam);
auto canvas = dynamic_cast<mCanvas*>(object);
if(!canvas) return DefWindowProc(hwnd, msg, wparam, lparam);
if(msg == WM_DROPFILES) {
if(auto paths = DropPaths(wparam)) canvas->doDrop(paths);
return false;
}
if(msg == WM_GETDLGCODE) {
return DLGC_STATIC | DLGC_WANTCHARS;
}
if(msg == WM_ERASEBKGND) {
if(auto self = canvas->self()) self->_paint();
return true;
}
if(msg == WM_PAINT) {
if(auto self = canvas->self()) self->_paint();
return false;
}
if(msg == WM_MOUSEMOVE) {
TRACKMOUSEEVENT tracker{sizeof(TRACKMOUSEEVENT), TME_LEAVE, hwnd};
TrackMouseEvent(&tracker);
canvas->doMouseMove({(int16_t)LOWORD(lparam), (int16_t)HIWORD(lparam)});
}
if(msg == WM_MOUSELEAVE) {
canvas->doMouseLeave();
}
if(msg == WM_LBUTTONDOWN || msg == WM_MBUTTONDOWN || msg == WM_RBUTTONDOWN) {
switch(msg) {
case WM_LBUTTONDOWN: canvas->doMousePress(Mouse::Button::Left); break;
case WM_MBUTTONDOWN: canvas->doMousePress(Mouse::Button::Middle); break;
case WM_RBUTTONDOWN: canvas->doMousePress(Mouse::Button::Right); break;
}
}
if(msg == WM_LBUTTONUP || msg == WM_MBUTTONUP || msg == WM_RBUTTONUP) {
switch(msg) {
case WM_LBUTTONUP: canvas->doMouseRelease(Mouse::Button::Left); break;
case WM_MBUTTONUP: canvas->doMouseRelease(Mouse::Button::Middle); break;
case WM_RBUTTONUP: canvas->doMouseRelease(Mouse::Button::Right); break;
}
}
return DefWindowProc(hwnd, msg, wparam, lparam);
}
auto pCanvas::construct() -> void {
hwnd = CreateWindow(L"hiroCanvas", L"", WS_CHILD, 0, 0, 0, 0, _parentHandle(), nullptr, GetModuleHandle(0), 0);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&reference);
pWidget::_setState();
hwnd = CreateWindow(L"hiroWidget", L"", WS_CHILD, 0, 0, 0, 0, _parentHandle(), nullptr, GetModuleHandle(0), 0);
pWidget::construct();
setDroppable(state().droppable);
update();
}
@@ -99,31 +44,101 @@ auto pCanvas::update() -> void {
_redraw();
}
//
auto pCanvas::doMouseLeave() -> void {
return self().doMouseLeave();
}
auto pCanvas::doMouseMove(int x, int y) -> void {
return self().doMouseMove({x, y});
}
auto pCanvas::windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) -> maybe<LRESULT> {
if(msg == WM_DROPFILES) {
if(auto paths = DropPaths(wparam)) self().doDrop(paths);
return false;
}
if(msg == WM_GETDLGCODE) {
return DLGC_STATIC | DLGC_WANTCHARS;
}
if(msg == WM_ERASEBKGND || msg == WM_PAINT) {
_paint();
return msg == WM_ERASEBKGND;
}
if(msg == WM_LBUTTONDOWN || msg == WM_MBUTTONDOWN || msg == WM_RBUTTONDOWN) {
switch(msg) {
case WM_LBUTTONDOWN: self().doMousePress(Mouse::Button::Left); break;
case WM_MBUTTONDOWN: self().doMousePress(Mouse::Button::Middle); break;
case WM_RBUTTONDOWN: self().doMousePress(Mouse::Button::Right); break;
}
}
if(msg == WM_LBUTTONUP || msg == WM_MBUTTONUP || msg == WM_RBUTTONUP) {
switch(msg) {
case WM_LBUTTONUP: self().doMouseRelease(Mouse::Button::Left); break;
case WM_MBUTTONUP: self().doMouseRelease(Mouse::Button::Middle); break;
case WM_RBUTTONUP: self().doMouseRelease(Mouse::Button::Right); break;
}
}
return pWidget::windowProc(hwnd, msg, wparam, lparam);
}
//
auto pCanvas::_paint() -> void {
PAINTSTRUCT ps;
BeginPaint(hwnd, &ps);
int sx = 0, sy = 0, dx = 0, dy = 0;
int width = this->width;
int height = this->height;
auto geometry = self().geometry();
if(width <= geometry.width()) {
sx = 0;
dx = (geometry.width() - width) / 2;
} else {
sx = (width - geometry.width()) / 2;
dx = 0;
width = geometry.width();
}
if(height <= geometry.height()) {
sy = 0;
dy = (geometry.height() - height) / 2;
} else {
sy = (height - geometry.height()) / 2;
dy = 0;
height = geometry.height();
}
HDC hdc = CreateCompatibleDC(ps.hdc);
BITMAPINFO bmi;
memset(&bmi, 0, sizeof(BITMAPINFO));
BITMAPINFO bmi{};
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biWidth = width;
bmi.bmiHeader.biHeight = -height; //GDI stores bitmaps upside now; negative height flips bitmap
bmi.bmiHeader.biSizeImage = pixels.size() * sizeof(uint32_t);
bmi.bmiHeader.biSizeImage = width * height * sizeof(uint32_t);
void* bits = nullptr;
HBITMAP bitmap = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, &bits, nullptr, 0);
if(bits) {
auto source = (const uint8_t*)pixels.data();
auto target = (uint8_t*)bits;
for(auto n : range(width * height)) {
target[0] = (source[0] * source[3]) / 255;
target[1] = (source[1] * source[3]) / 255;
target[2] = (source[2] * source[3]) / 255;
target[3] = (source[3]);
source += 4, target += 4;
for(uint y : range(height)) {
auto source = (const uint8_t*)pixels.data() + (sy + y) * this->width * sizeof(uint32_t) + sx * sizeof(uint32_t);
auto target = (uint8_t*)bits + y * width * sizeof(uint32_t);
for(uint x : range(width)) {
target[0] = (source[0] * source[3]) / 255;
target[1] = (source[1] * source[3]) / 255;
target[2] = (source[2] * source[3]) / 255;
target[3] = (source[3]);
source += 4, target += 4;
}
}
}
SelectObject(hdc, bitmap);
@@ -133,7 +148,7 @@ auto pCanvas::_paint() -> void {
DrawThemeParentBackground(hwnd, ps.hdc, &rc);
BLENDFUNCTION bf{AC_SRC_OVER, 0, (BYTE)255, AC_SRC_ALPHA};
AlphaBlend(ps.hdc, 0, 0, width, height, hdc, 0, 0, width, height, bf);
AlphaBlend(ps.hdc, dx, dy, width, height, hdc, 0, 0, width, height, bf);
DeleteObject(bitmap);
DeleteDC(hdc);

View File

@@ -13,6 +13,10 @@ struct pCanvas : pWidget {
auto setIcon(const image& icon) -> void;
auto update() -> void;
auto doMouseLeave() -> void override;
auto doMouseMove(int x, int y) -> void override;
auto windowProc(HWND, UINT, WPARAM, LPARAM) -> maybe<LRESULT> override;
auto _paint() -> void;
auto _rasterize() -> void;
auto _redraw() -> void;

View File

@@ -2,30 +2,11 @@
namespace hiro {
static auto CALLBACK CheckButton_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) -> LRESULT {
if(auto object = (mObject*)GetWindowLongPtr(hwnd, GWLP_USERDATA)) {
if(auto button = dynamic_cast<mCheckButton*>(object)) {
if(auto self = button->self()) {
if(msg == WM_ERASEBKGND) return DefWindowProc(hwnd, msg, wparam, lparam);
if(msg == WM_PAINT) return Button_paintProc(hwnd, msg, wparam, lparam,
button->state.bordered, button->state.checked, button->enabled(true), button->font(true),
button->state.icon, button->state.orientation, button->state.text
);
return self->windowProc(hwnd, msg, wparam, lparam);
}
}
}
return DefWindowProc(hwnd, msg, wparam, lparam);
}
auto pCheckButton::construct() -> void {
hwnd = CreateWindow(L"BUTTON", L"",
WS_CHILD | WS_TABSTOP | BS_CHECKBOX | BS_PUSHLIKE,
0, 0, 0, 0, _parentHandle(), nullptr, GetModuleHandle(0), 0);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&reference);
windowProc = (WindowProc)GetWindowLongPtr(hwnd, GWLP_WNDPROC);
SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)CheckButton_windowProc);
pWidget::_setState();
pWidget::construct();
_setState();
setChecked(state().checked);
}
@@ -85,12 +66,32 @@ auto pCheckButton::setVisible(bool visible) -> void {
_setState();
}
//
auto pCheckButton::onToggle() -> void {
state().checked = !state().checked;
setChecked(state().checked);
self().doToggle();
}
auto pCheckButton::windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) -> maybe<LRESULT> {
if(msg == WM_PAINT) {
PAINTSTRUCT ps;
BeginPaint(hwnd, &ps);
auto buttonState = Button_GetState(hwnd);
Button_CustomDraw(hwnd, ps,
state().bordered, state().checked, self().enabled(true), buttonState,
self().font(true), state().icon, state().orientation, state().text
);
EndPaint(hwnd, &ps);
return false;
}
return pWidget::windowProc(hwnd, msg, wparam, lparam);
}
//
auto pCheckButton::_setState() -> void {
InvalidateRect(hwnd, 0, false);
}

View File

@@ -16,10 +16,9 @@ struct pCheckButton : pWidget {
auto setVisible(bool visible) -> void override;
auto onToggle() -> void;
auto windowProc(HWND, UINT, WPARAM, LPARAM) -> maybe<LRESULT> override;
auto _setState() -> void;
WindowProc windowProc = nullptr;
};
}

View File

@@ -8,8 +8,7 @@ auto pCheckLabel::construct() -> void {
WS_CHILD | WS_TABSTOP | BS_CHECKBOX,
0, 0, 0, 0, _parentHandle(), nullptr, GetModuleHandle(0), 0
);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&reference);
pWidget::_setState();
pWidget::construct();
setChecked(state().checked);
setText(state().text);
}

View File

@@ -9,8 +9,7 @@ auto pComboButton::construct() -> void {
0, 0, 0, 0,
_parentHandle(), nullptr, GetModuleHandle(0), 0
);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&reference);
pWidget::_setState();
pWidget::construct();
for(auto& item : state().items) append(item);
}

View File

@@ -6,8 +6,7 @@ auto pFrame::construct() -> void {
hwnd = CreateWindow(L"BUTTON", L"",
WS_CHILD | BS_GROUPBOX,
0, 0, 0, 0, _parentHandle(), nullptr, GetModuleHandle(0), 0);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&reference);
pWidget::_setState();
pWidget::construct();
setText(state().text);
}

View File

@@ -2,77 +2,20 @@
namespace hiro {
static auto CALLBACK HexEdit_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) -> LRESULT {
auto object = (mObject*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
if(!object) return DefWindowProc(hwnd, msg, wparam, lparam);
auto hexEdit = dynamic_cast<mHexEdit*>(object);
if(!hexEdit) return DefWindowProc(hwnd, msg, wparam, lparam);
auto self = hexEdit->self();
if(!self) return DefWindowProc(hwnd, msg, wparam, lparam);
switch(msg) {
case WM_KEYDOWN:
if(self->keyPress(wparam)) return 0;
break;
case WM_MOUSEWHEEL: {
signed offset = -((int16_t)HIWORD(wparam) / WHEEL_DELTA);
self->scrollTo(self->scrollPosition() + offset);
return true;
}
case WM_SIZE: {
RECT rc;
GetClientRect(self->hwnd, &rc);
SetWindowPos(self->scrollBar, HWND_TOP, rc.right - 18, 0, 18, rc.bottom, SWP_SHOWWINDOW);
break;
}
case WM_VSCROLL: {
SCROLLINFO info{sizeof(SCROLLINFO)};
info.fMask = SIF_ALL;
GetScrollInfo((HWND)lparam, SB_CTL, &info);
switch(LOWORD(wparam)) {
case SB_LEFT: info.nPos = info.nMin; break;
case SB_RIGHT: info.nPos = info.nMax; break;
case SB_LINELEFT: info.nPos--; break;
case SB_LINERIGHT: info.nPos++; break;
case SB_PAGELEFT: info.nPos -= info.nMax >> 3; break;
case SB_PAGERIGHT: info.nPos += info.nMax >> 3; break;
case SB_THUMBTRACK: info.nPos = info.nTrackPos; break;
}
info.fMask = SIF_POS;
SetScrollInfo((HWND)lparam, SB_CTL, &info, TRUE);
GetScrollInfo((HWND)lparam, SB_CTL, &info); //get clamped position
self->scrollTo(info.nPos);
return true;
}
}
return self->windowProc(hwnd, msg, wparam, lparam);
}
auto pHexEdit::construct() -> void {
hwnd = CreateWindowEx(
WS_EX_CLIENTEDGE, L"EDIT", L"",
WS_CHILD | WS_TABSTOP | ES_AUTOHSCROLL | ES_READONLY | ES_MULTILINE | ES_WANTRETURN,
0, 0, 0, 0, _parentHandle(), nullptr, GetModuleHandle(0), 0
);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&reference);
scrollBar = CreateWindowEx(
0, L"SCROLLBAR", L"",
WS_VISIBLE | WS_CHILD | SBS_VERT,
0, 0, 0, 0, hwnd, nullptr, GetModuleHandle(0), 0
);
SetWindowLongPtr(scrollBar, GWLP_USERDATA, (LONG_PTR)&reference);
pWidget::construct();
windowProc = (WindowProc)GetWindowLongPtr(hwnd, GWLP_WNDPROC);
SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)HexEdit_windowProc);
pWidget::_setState();
setAddress(state().address);
setBackgroundColor(state().backgroundColor);
setLength(state().length);
@@ -149,7 +92,7 @@ auto pHexEdit::update() -> void {
Edit_SetSel(hwnd, LOWORD(cursorPosition), HIWORD(cursorPosition));
}
bool pHexEdit::keyPress(unsigned scancode) {
auto pHexEdit::keyPress(unsigned scancode) -> bool {
if(!state().onRead) return false;
signed position = LOWORD(Edit_GetSel(hwnd));
@@ -234,25 +177,67 @@ bool pHexEdit::keyPress(unsigned scancode) {
return true;
}
signed pHexEdit::rows() {
auto pHexEdit::rows() -> int {
return (max(1u, state().length) + state().columns - 1) / state().columns;
}
signed pHexEdit::rowsScrollable() {
auto pHexEdit::rowsScrollable() -> int {
return max(0u, rows() - state().rows);
}
signed pHexEdit::scrollPosition() {
auto pHexEdit::scrollPosition() -> int {
return state().address / state().columns;
}
void pHexEdit::scrollTo(signed position) {
auto pHexEdit::scrollTo(signed position) -> void {
if(position > rowsScrollable()) position = rowsScrollable();
if(position < 0) position = 0;
if(position == scrollPosition()) return;
self().setAddress(position * state().columns);
}
auto pHexEdit::windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) -> maybe<LRESULT> {
if(msg == WM_KEYDOWN) {
if(keyPress(wparam)) return 0;
}
if(msg == WM_MOUSEWHEEL) {
int offset = -((int16_t)HIWORD(wparam) / WHEEL_DELTA);
scrollTo(scrollPosition() + offset);
return true;
}
if(msg == WM_SIZE) {
RECT rc;
GetClientRect(hwnd, &rc);
SetWindowPos(scrollBar, HWND_TOP, rc.right - 18, 0, 18, rc.bottom, SWP_SHOWWINDOW);
}
if(msg == WM_VSCROLL) {
SCROLLINFO info{sizeof(SCROLLINFO)};
info.fMask = SIF_ALL;
GetScrollInfo((HWND)lparam, SB_CTL, &info);
switch(LOWORD(wparam)) {
case SB_LEFT: info.nPos = info.nMin; break;
case SB_RIGHT: info.nPos = info.nMax; break;
case SB_LINELEFT: info.nPos--; break;
case SB_LINERIGHT: info.nPos++; break;
case SB_PAGELEFT: info.nPos -= info.nMax >> 3; break;
case SB_PAGERIGHT: info.nPos += info.nMax >> 3; break;
case SB_THUMBTRACK: info.nPos = info.nTrackPos; break;
}
info.fMask = SIF_POS;
SetScrollInfo((HWND)lparam, SB_CTL, &info, TRUE);
GetScrollInfo((HWND)lparam, SB_CTL, &info); //get clamped position
scrollTo(info.nPos);
return true;
}
return pWidget::windowProc(hwnd, msg, wparam, lparam);
}
}
#endif

View File

@@ -18,8 +18,8 @@ struct pHexEdit : pWidget {
auto rowsScrollable() -> signed;
auto scrollPosition() -> signed;
auto scrollTo(signed position) -> void;
auto windowProc(HWND, UINT, WPARAM, LPARAM) -> maybe<LRESULT> override;
WindowProc windowProc = nullptr;
HWND scrollBar = nullptr;
HBRUSH backgroundBrush = nullptr;
};

View File

@@ -7,8 +7,7 @@ auto pHorizontalScrollBar::construct() -> void {
L"SCROLLBAR", L"", WS_CHILD | WS_TABSTOP | SBS_HORZ,
0, 0, 0, 0, _parentHandle(), nullptr, GetModuleHandle(0), 0
);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&reference);
pWidget::_setState();
pWidget::construct();
setLength(state().length);
setPosition(state().position);
}

View File

@@ -7,8 +7,7 @@ auto pHorizontalSlider::construct() -> void {
TRACKBAR_CLASS, L"", WS_CHILD | WS_TABSTOP | TBS_TRANSPARENTBKGND | TBS_NOTICKS | TBS_BOTH | TBS_HORZ,
0, 0, 0, 0, _parentHandle(), nullptr, GetModuleHandle(0), 0
);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&reference);
pWidget::_setState();
pWidget::construct();
setLength(state().length);
setPosition(state().position);
}

View File

@@ -3,11 +3,10 @@
namespace hiro {
auto pLabel::construct() -> void {
hwnd = CreateWindow(L"hiroLabel", L"",
hwnd = CreateWindow(L"hiroWidget", L"",
WS_CHILD | WS_CLIPSIBLINGS,
0, 0, 0, 0, _parentHandle(), nullptr, GetModuleHandle(0), 0);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&reference);
pWidget::_setState();
pWidget::construct();
setText(state().text);
}
@@ -33,20 +32,15 @@ auto pLabel::setForegroundColor(Color color) -> void {
}
auto pLabel::setText(const string& text) -> void {
SetWindowText(hwnd, utf16_t(text));
InvalidateRect(hwnd, 0, false);
}
static auto CALLBACK Label_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) -> LRESULT {
auto label = (mLabel*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
if(!label) return DefWindowProc(hwnd, msg, wparam, lparam);
auto window = label->parentWindow(true);
if(!window) return DefWindowProc(hwnd, msg, wparam, lparam);
auto pLabel::windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) -> maybe<LRESULT> {
if(msg == WM_GETDLGCODE) {
return DLGC_STATIC | DLGC_WANTCHARS;
}
switch(msg) {
case WM_GETDLGCODE: return DLGC_STATIC | DLGC_WANTCHARS;
case WM_ERASEBKGND:
case WM_PAINT: {
if(msg == WM_ERASEBKGND || msg == WM_PAINT) {
PAINTSTRUCT ps;
BeginPaint(hwnd, &ps);
RECT rc;
@@ -56,24 +50,25 @@ static auto CALLBACK Label_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM
auto hbmMemory = CreateCompatibleBitmap(ps.hdc, rc.right - rc.left, rc.bottom - rc.top);
SelectObject(hdcMemory, hbmMemory);
uint length = GetWindowTextLength(hwnd);
wchar_t text[length + 1];
GetWindowText(hwnd, text, length + 1);
text[length] = 0;
//todo: use DrawThemeParentBackground if Label is inside TabFrame
if(auto color = label->backgroundColor()) {
if(auto color = state().backgroundColor) {
auto brush = CreateSolidBrush(CreateRGB(color));
FillRect(hdcMemory, &rc, brush);
DeleteObject(brush);
} else if(auto brush = window->self()->hbrush) {
FillRect(hdcMemory, &rc, brush);
} else {
} else if(self().parentTabFrame(true)) {
DrawThemeParentBackground(hwnd, hdcMemory, &rc);
} else if(auto window = self().parentWindow(true)) {
if(auto color = window->backgroundColor()) {
auto brush = CreateSolidBrush(CreateRGB(color));
FillRect(hdcMemory, &rc, brush);
DeleteObject(brush);
} else {
DrawThemeParentBackground(hwnd, hdcMemory, &rc);
}
}
utf16_t text(state().text);
SetBkMode(hdcMemory, TRANSPARENT);
SelectObject(hdcMemory, label->self()->hfont);
SelectObject(hdcMemory, hfont);
DrawText(hdcMemory, text, -1, &rc, DT_CALCRECT | DT_END_ELLIPSIS);
uint height = rc.bottom;
@@ -81,12 +76,12 @@ static auto CALLBACK Label_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM
rc.top = (rc.bottom - height) / 2;
rc.bottom = rc.top + height;
uint horizontalAlignment = DT_CENTER;
if(label->alignment().horizontal() < 0.333) horizontalAlignment = DT_LEFT;
if(label->alignment().horizontal() > 0.666) horizontalAlignment = DT_RIGHT;
if(state().alignment.horizontal() < 0.333) horizontalAlignment = DT_LEFT;
if(state().alignment.horizontal() > 0.666) horizontalAlignment = DT_RIGHT;
uint verticalAlignment = DT_VCENTER;
if(label->alignment().vertical() < 0.333) verticalAlignment = DT_TOP;
if(label->alignment().vertical() > 0.666) verticalAlignment = DT_BOTTOM;
if(auto color = label->foregroundColor()) {
if(state().alignment.vertical() < 0.333) verticalAlignment = DT_TOP;
if(state().alignment.vertical() > 0.666) verticalAlignment = DT_BOTTOM;
if(auto color = state().foregroundColor) {
SetTextColor(hdcMemory, CreateRGB(color));
}
DrawText(hdcMemory, text, -1, &rc, DT_END_ELLIPSIS | horizontalAlignment | verticalAlignment);
@@ -99,9 +94,8 @@ static auto CALLBACK Label_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM
return msg == WM_ERASEBKGND;
}
}
return DefWindowProc(hwnd, msg, wparam, lparam);
return pWidget::windowProc(hwnd, msg, wparam, lparam);
}
}

View File

@@ -10,6 +10,8 @@ struct pLabel : pWidget {
auto setBackgroundColor(Color color) -> void;
auto setForegroundColor(Color color) -> void;
auto setText(const string& text) -> void;
auto windowProc(HWND, UINT, WPARAM, LPARAM) -> maybe<LRESULT> override;
};
}

View File

@@ -8,8 +8,7 @@ auto pLineEdit::construct() -> void {
WS_CHILD | WS_TABSTOP | ES_AUTOHSCROLL | ES_AUTOVSCROLL,
0, 0, 0, 0, _parentHandle(), nullptr, GetModuleHandle(0), 0
);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&reference);
pWidget::_setState();
pWidget::construct();
setBackgroundColor(state().backgroundColor);
setEditable(state().editable);
setText(state().text);

View File

@@ -6,8 +6,7 @@ auto pProgressBar::construct() -> void {
hwnd = CreateWindow(PROGRESS_CLASS, L"",
WS_CHILD | PBS_SMOOTH,
0, 0, 0, 0, _parentHandle(), nullptr, GetModuleHandle(0), 0);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&reference);
pWidget::_setState();
pWidget::construct();
SendMessage(hwnd, PBM_SETRANGE, 0, MAKELPARAM(0, 100));
SendMessage(hwnd, PBM_SETSTEP, MAKEWPARAM(1, 0), 0);
setPosition(state().position);

View File

@@ -2,30 +2,11 @@
namespace hiro {
static auto CALLBACK RadioButton_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) -> LRESULT {
if(auto object = (mObject*)GetWindowLongPtr(hwnd, GWLP_USERDATA)) {
if(auto button = dynamic_cast<mRadioButton*>(object)) {
if(auto self = button->self()) {
if(msg == WM_ERASEBKGND) return DefWindowProc(hwnd, msg, wparam, lparam);
if(msg == WM_PAINT) return Button_paintProc(hwnd, msg, wparam, lparam,
button->state.bordered, button->state.checked, button->enabled(true), button->font(true),
button->state.icon, button->state.orientation, button->state.text
);
return self->windowProc(hwnd, msg, wparam, lparam);
}
}
}
return DefWindowProc(hwnd, msg, wparam, lparam);
}
auto pRadioButton::construct() -> void {
hwnd = CreateWindow(L"BUTTON", L"",
WS_CHILD | WS_TABSTOP | BS_CHECKBOX | BS_PUSHLIKE,
0, 0, 0, 0, _parentHandle(), nullptr, GetModuleHandle(0), 0);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&reference);
windowProc = (WindowProc)GetWindowLongPtr(hwnd, GWLP_WNDPROC);
SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)RadioButton_windowProc);
pWidget::_setState();
pWidget::construct();
setGroup(state().group);
_setState();
}
@@ -111,12 +92,32 @@ auto pRadioButton::setVisible(bool visible) -> void {
_setState();
}
//
auto pRadioButton::onActivate() -> void {
if(state().checked) return;
self().setChecked();
self().doActivate();
}
auto pRadioButton::windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) -> maybe<LRESULT> {
if(msg == WM_PAINT) {
PAINTSTRUCT ps;
BeginPaint(hwnd, &ps);
auto buttonState = Button_GetState(hwnd);
Button_CustomDraw(hwnd, ps,
state().bordered, state().checked, self().enabled(true), buttonState,
self().font(true), state().icon, state().orientation, state().text
);
EndPaint(hwnd, &ps);
return false;
}
return pWidget::windowProc(hwnd, msg, wparam, lparam);
}
//
auto pRadioButton::_setState() -> void {
InvalidateRect(hwnd, 0, false);
}

View File

@@ -17,10 +17,9 @@ struct pRadioButton : pWidget {
auto setVisible(bool visible) -> void override;
auto onActivate() -> void;
auto windowProc(HWND, UINT, WPARAM, LPARAM) -> maybe<LRESULT> override;
auto _setState() -> void;
WindowProc windowProc = nullptr;
};
}

View File

@@ -8,7 +8,7 @@ auto pRadioLabel::construct() -> void {
0, 0, 0, 0, _parentHandle(), nullptr, GetModuleHandle(0), 0
);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&reference);
pWidget::_setState();
pWidget::construct();
setGroup(state().group);
setText(state().text);
}

View File

@@ -3,14 +3,11 @@
namespace hiro {
static auto CALLBACK TabFrame_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) -> LRESULT {
if(auto object = (mObject*)GetWindowLongPtr(hwnd, GWLP_USERDATA)) {
if(auto tabFrame = dynamic_cast<mTabFrame*>(object)) {
if(auto self = tabFrame->self()) {
return Shared_windowProc(self->windowProc, hwnd, msg, wparam, lparam);
}
if(auto tabFrame = (mTabFrame*)GetWindowLongPtr(hwnd, GWLP_USERDATA)) {
if(auto self = tabFrame->self()) {
return Shared_windowProc(self->defaultWindowProc, hwnd, msg, wparam, lparam);
}
}
return DefWindowProc(hwnd, msg, wparam, lparam);
}
@@ -19,10 +16,8 @@ auto pTabFrame::construct() -> void {
WC_TABCONTROL, L"", WS_CHILD | WS_TABSTOP,
0, 0, 0, 0, _parentHandle(), nullptr, GetModuleHandle(0), 0
);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&reference);
windowProc = (WindowProc)GetWindowLongPtr(hwnd, GWLP_WNDPROC);
pWidget::construct();
SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)TabFrame_windowProc);
pWidget::_setState();
for(auto& item : state().items) append(item);
}
@@ -88,6 +83,8 @@ auto pTabFrame::setVisible(bool visible) -> void {
}
}
//
auto pTabFrame::_buildImageList() -> void {
unsigned size = pFont::size(hfont, " ").height();

View File

@@ -17,7 +17,6 @@ struct pTabFrame : pWidget {
auto _buildImageList() -> void;
auto _synchronizeSizable() -> void;
WindowProc windowProc = nullptr;
HIMAGELIST imageList = nullptr;
};

View File

@@ -2,36 +2,14 @@
namespace hiro {
static auto CALLBACK TableView_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) -> LRESULT {
if(auto object = (mObject*)GetWindowLongPtr(hwnd, GWLP_USERDATA)) {
if(auto tableView = dynamic_cast<mTableView*>(object)) {
if(auto self = tableView->self()) {
if(!tableView->enabled(true)) {
if(msg == WM_KEYDOWN || msg == WM_KEYUP || msg == WM_SYSKEYDOWN || msg == WM_SYSKEYUP) {
//WC_LISTVIEW responds to key messages even when its HWND is disabled
//the control should be inactive when disabled; so we intercept the messages here
return false;
}
}
return self->windowProc(hwnd, msg, wparam, lparam);
}
}
}
return DefWindowProc(hwnd, msg, wparam, lparam);
}
auto pTableView::construct() -> void {
hwnd = CreateWindowEx(
WS_EX_CLIENTEDGE | LVS_EX_DOUBLEBUFFER, WC_LISTVIEW, L"",
WS_CHILD | WS_TABSTOP | LVS_REPORT | LVS_SHOWSELALWAYS,
0, 0, 0, 0, _parentHandle(), nullptr, GetModuleHandle(0), 0
);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&reference);
windowProc = (WindowProc)GetWindowLongPtr(hwnd, GWLP_WNDPROC);
SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)&TableView_windowProc);
ListView_SetExtendedListViewStyle(hwnd, LVS_EX_FULLROWSELECT | LVS_EX_SUBITEMIMAGES);
pWidget::_setState();
pWidget::construct();
setBackgroundColor(state().backgroundColor);
setBatchable(state().batchable);
setBordered(state().bordered);
@@ -301,6 +279,22 @@ auto pTableView::setSortable(bool sortable) -> void {
SetWindowLong(hwnd, GWL_STYLE, style);
}
//
auto pTableView::windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) -> maybe<LRESULT> {
if(msg == WM_KEYDOWN || msg == WM_KEYUP || msg == WM_SYSKEYDOWN || msg == WM_SYSKEYUP) {
if(!self().enabled(true)) {
//WC_LISTVIEW responds to key messages even when its HWND is disabled
//the control should be inactive when disabled; so we intercept the messages here
return false;
}
}
return pWidget::windowProc(hwnd, msg, wparam, lparam);
}
//
auto pTableView::_backgroundColor(unsigned _row, unsigned _column) -> Color {
if(auto item = self().item(_row)) {
if(auto cell = item->cell(_column)) {

View File

@@ -25,6 +25,7 @@ struct pTableView : pWidget {
auto onCustomDraw(LPARAM lparam) -> LRESULT;
auto onSort(LPARAM lparam) -> void;
auto onToggle(LPARAM lparam) -> void;
auto windowProc(HWND, UINT, WPARAM, LPARAM) -> maybe<LRESULT> override;
auto _backgroundColor(unsigned row, unsigned column) -> Color;
auto _cellWidth(unsigned row, unsigned column) -> unsigned;
@@ -34,7 +35,6 @@ struct pTableView : pWidget {
auto _setIcons() -> void;
auto _width(unsigned column) -> unsigned;
WindowProc windowProc = nullptr;
HIMAGELIST imageList = 0;
vector<image> icons;
};

View File

@@ -8,8 +8,7 @@ auto pTextEdit::construct() -> void {
WS_CHILD | WS_TABSTOP | WS_VSCROLL | ES_AUTOVSCROLL | ES_MULTILINE | ES_WANTRETURN | (!state().wordWrap ? WS_HSCROLL | ES_AUTOHSCROLL : 0),
0, 0, 0, 0, _parentHandle(), nullptr, GetModuleHandle(0), 0
);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&reference);
pWidget::_setState();
pWidget::construct();
setBackgroundColor(state().backgroundColor);
setEditable(state().editable);
setText(state().text);

View File

@@ -7,8 +7,7 @@ auto pVerticalScrollBar::construct() -> void {
L"SCROLLBAR", L"", WS_CHILD | SBS_VERT,
0, 0, 0, 0, _parentHandle(), nullptr, GetModuleHandle(0), 0
);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&reference);
pWidget::_setState();
pWidget::construct();
setLength(state().length);
setPosition(state().position);
}

View File

@@ -7,8 +7,7 @@ auto pVerticalSlider::construct() -> void {
TRACKBAR_CLASS, L"", WS_CHILD | WS_TABSTOP | TBS_TRANSPARENTBKGND | TBS_NOTICKS | TBS_BOTH | TBS_VERT,
0, 0, 0, 0, _parentHandle(), nullptr, GetModuleHandle(0), 0
);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&reference);
pWidget::_setState();
pWidget::construct();
setLength(state().length);
setPosition(state().position);
}

View File

@@ -2,56 +2,11 @@
namespace hiro {
static auto CALLBACK Viewport_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) -> LRESULT {
auto object = (mObject*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
if(!object) return DefWindowProc(hwnd, msg, wparam, lparam);
auto viewport = dynamic_cast<mViewport*>(object);
if(!viewport) return DefWindowProc(hwnd, msg, wparam, lparam);
if(msg == WM_DROPFILES) {
if(auto paths = DropPaths(wparam)) viewport->doDrop(paths);
return false;
}
if(msg == WM_GETDLGCODE) {
return DLGC_STATIC | DLGC_WANTCHARS;
}
if(msg == WM_MOUSEMOVE) {
TRACKMOUSEEVENT tracker{sizeof(TRACKMOUSEEVENT), TME_LEAVE, hwnd};
TrackMouseEvent(&tracker);
viewport->doMouseMove({(int16_t)LOWORD(lparam), (int16_t)HIWORD(lparam)});
}
if(msg == WM_MOUSELEAVE) {
viewport->doMouseLeave();
}
if(msg == WM_LBUTTONDOWN || msg == WM_MBUTTONDOWN || msg == WM_RBUTTONDOWN) {
switch(msg) {
case WM_LBUTTONDOWN: viewport->doMousePress(Mouse::Button::Left); break;
case WM_MBUTTONDOWN: viewport->doMousePress(Mouse::Button::Middle); break;
case WM_RBUTTONDOWN: viewport->doMousePress(Mouse::Button::Right); break;
}
}
if(msg == WM_LBUTTONUP || msg == WM_MBUTTONUP || msg == WM_RBUTTONUP) {
switch(msg) {
case WM_LBUTTONUP: viewport->doMouseRelease(Mouse::Button::Left); break;
case WM_MBUTTONUP: viewport->doMouseRelease(Mouse::Button::Middle); break;
case WM_RBUTTONUP: viewport->doMouseRelease(Mouse::Button::Right); break;
}
}
return DefWindowProc(hwnd, msg, wparam, lparam);
}
auto pViewport::construct() -> void {
hwnd = CreateWindow(L"hiroViewport", L"",
hwnd = CreateWindow(L"hiroWidget", L"",
WS_CHILD | WS_DISABLED,
0, 0, 0, 0, _parentHandle(), nullptr, GetModuleHandle(0), 0);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&reference);
pWidget::_setState();
pWidget::construct();
setDroppable(state().droppable);
}
@@ -67,6 +22,57 @@ auto pViewport::setDroppable(bool droppable) -> void {
DragAcceptFiles(hwnd, droppable);
}
//
auto pViewport::doMouseLeave() -> void {
return self().doMouseLeave();
}
auto pViewport::doMouseMove(int x, int y) -> void {
return self().doMouseMove({x, y});
}
auto pViewport::windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) -> maybe<LRESULT> {
if(msg == WM_DROPFILES) {
if(auto paths = DropPaths(wparam)) self().doDrop(paths);
return false;
}
if(msg == WM_ERASEBKGND) {
PAINTSTRUCT ps;
BeginPaint(hwnd, &ps);
auto brush = CreateSolidBrush(RGB(0, 0, 0));
RECT rc{};
GetClientRect(hwnd, &rc);
FillRect(ps.hdc, &rc, brush);
DeleteObject(brush);
EndPaint(hwnd, &ps);
return true;
}
if(msg == WM_GETDLGCODE) {
return DLGC_STATIC | DLGC_WANTCHARS;
}
if(msg == WM_LBUTTONDOWN || msg == WM_MBUTTONDOWN || msg == WM_RBUTTONDOWN) {
switch(msg) {
case WM_LBUTTONDOWN: self().doMousePress(Mouse::Button::Left); break;
case WM_MBUTTONDOWN: self().doMousePress(Mouse::Button::Middle); break;
case WM_RBUTTONDOWN: self().doMousePress(Mouse::Button::Right); break;
}
}
if(msg == WM_LBUTTONUP || msg == WM_MBUTTONUP || msg == WM_RBUTTONUP) {
switch(msg) {
case WM_LBUTTONUP: self().doMouseRelease(Mouse::Button::Left); break;
case WM_MBUTTONUP: self().doMouseRelease(Mouse::Button::Middle); break;
case WM_RBUTTONUP: self().doMouseRelease(Mouse::Button::Right); break;
}
}
return pWidget::windowProc(hwnd, msg, wparam, lparam);
}
}
#endif

View File

@@ -7,6 +7,10 @@ struct pViewport : pWidget {
auto handle() const -> uintptr_t;
auto setDroppable(bool droppable) -> void;
auto doMouseLeave() -> void override;
auto doMouseMove(int x, int y) -> void override;
auto windowProc(HWND, UINT, WPARAM, LPARAM) -> maybe<LRESULT> override;
};
}

View File

@@ -2,17 +2,35 @@
namespace hiro {
static auto CALLBACK Widget_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) -> LRESULT {
if(auto object = (mObject*)GetWindowLongPtr(hwnd, GWLP_USERDATA)) {
if(auto widget = dynamic_cast<mWidget*>(object)) {
if(auto self = widget->self()) {
if(auto result = self->windowProc(hwnd, msg, wparam, lparam)) {
return result();
}
return CallWindowProc(self->defaultWindowProc, hwnd, msg, wparam, lparam);
}
}
}
return DefWindowProc(hwnd, msg, wparam, lparam);
}
auto pWidget::construct() -> void {
abstract = true;
//todo: create hiroWidget
hwnd = CreateWindow(L"hiroLabel", L"", WS_CHILD, 0, 0, 0, 0, _parentHandle(), nullptr, GetModuleHandle(0), 0);
if(!hwnd) {
abstract = true;
hwnd = CreateWindow(L"hiroWidget", L"", WS_CHILD, 0, 0, 0, 0, _parentHandle(), nullptr, GetModuleHandle(0), 0);
}
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&reference);
defaultWindowProc = (WindowProc)GetWindowLongPtr(hwnd, GWLP_WNDPROC);
SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)Widget_windowProc);
_setState();
}
auto pWidget::destruct() -> void {
DeleteObject(hfont);
DestroyWindow(hwnd);
toolTip.reset();
if(hfont) { DeleteObject(hfont); hfont = nullptr; }
if(hwnd) { DestroyWindow(hwnd); hwnd = nullptr; }
}
auto pWidget::focused() const -> bool {
@@ -48,7 +66,15 @@ auto pWidget::setGeometry(Geometry geometry) -> void {
geometry.setY(geometry.y() - displacement.y());
}
SetWindowPos(hwnd, nullptr, geometry.x(), geometry.y(), geometry.width(), geometry.height(), SWP_NOZORDER);
self().doSize();
pSizable::setGeometry(geometry);
}
auto pWidget::setToolTip(const string& toolTipText) -> void {
if(toolTipText) {
toolTip = new pToolTip{toolTipText};
} else {
toolTip.reset();
}
}
auto pWidget::setVisible(bool visible) -> void {
@@ -60,6 +86,44 @@ auto pWidget::setVisible(bool visible) -> void {
//
auto pWidget::doMouseHover() -> void {
if(toolTip) toolTip->show();
}
auto pWidget::doMouseLeave() -> void {
}
auto pWidget::doMouseMove(int x, int y) -> void {
}
auto pWidget::windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) -> maybe<LRESULT> {
if(msg == WM_MOUSEMOVE) {
TRACKMOUSEEVENT event{sizeof(TRACKMOUSEEVENT)};
event.hwndTrack = hwnd;
event.dwFlags = TME_LEAVE | TME_HOVER;
event.dwHoverTime = 1500;
TrackMouseEvent(&event);
POINT p{};
GetCursorPos(&p);
doMouseMove(p.x, p.y);
if(auto toolTip = pApplication::state().toolTip) {
toolTip->windowProc(hwnd, msg, wparam, lparam);
}
}
if(msg == WM_MOUSELEAVE) {
doMouseLeave();
}
if(msg == WM_MOUSEHOVER) {
doMouseHover();
}
return {};
}
//
auto pWidget::_parentHandle() -> HWND {
if(auto parent = _parentWidget()) return parent->hwnd;
if(auto parent = _parentWindow()) return parent->hwnd;
@@ -72,19 +136,20 @@ auto pWidget::_parentWidget() -> maybe<pWidget&> {
if(auto self = parent->self()) return *self;
}
#endif
return nothing;
return {};
}
auto pWidget::_parentWindow() -> maybe<pWindow&> {
if(auto parent = self().parentWindow(true)) {
if(auto self = parent->self()) return *self;
}
return nothing;
return {};
}
auto pWidget::_setState() -> void {
setEnabled(self().enabled());
setFont(self().font());
setToolTip(self().toolTip());
setVisible(self().visible());
}

View File

@@ -11,16 +11,24 @@ struct pWidget : pSizable {
auto setFocused() -> void;
auto setFont(const Font& font) -> void override;
virtual auto setGeometry(Geometry geometry) -> void;
auto setToolTip(const string& toolTip) -> void;
auto setVisible(bool visible) -> void override;
virtual auto doMouseHover() -> void;
virtual auto doMouseLeave() -> void;
virtual auto doMouseMove(int x, int y) -> void;
virtual auto windowProc(HWND, UINT, WPARAM, LPARAM) -> maybe<LRESULT>;
auto _parentHandle() -> HWND;
auto _parentWidget() -> maybe<pWidget&>;
auto _parentWindow() -> maybe<pWindow&>;
auto _setState() -> void;
bool abstract = false;
HWND hwnd = 0;
HFONT hfont = 0;
WindowProc defaultWindowProc = nullptr;
HWND hwnd = nullptr;
HFONT hfont = nullptr;
unique_pointer<pToolTip> toolTip;
};
}