From 65d9985bdcaed63b52bfe8c35c61d8a4a43a8292 Mon Sep 17 00:00:00 2001 From: Jonas Kümmerlin Date: Tue, 28 Apr 2020 10:57:22 +0200 Subject: first try at checkbox The size calculation is really messy and the background stuff does not work with comctl v5. Probably need to go full owner-drawn, again. --- Makefile.mingw | 7 +++- Makefile.vc6 | 5 ++- Makefile.vc6-ansi | 5 ++- alf/alf.h | 4 ++ alf/alfcheckbox.cpp | 118 ++++++++++++++++++++++++++++++++++++++++++++++++++++ alf/alflayout.cpp | 51 ++++++++++++++++++++++- alf/alfwindow.cpp | 7 ++++ widgetfactory.cpp | 2 + 8 files changed, 193 insertions(+), 6 deletions(-) create mode 100644 alf/alfcheckbox.cpp diff --git a/Makefile.mingw b/Makefile.mingw index f3c52ed..8b5a3e2 100644 --- a/Makefile.mingw +++ b/Makefile.mingw @@ -8,12 +8,15 @@ LDFLAGS = -luser32 -lcomctl32 -lshell32 -lversion -static all: out/widgetfactory.exe out/alf/alf.c out/widgetfactory-c.exe -out/widgetfactory.exe: out/widgetfactory.o out/alfbutton.o out/alfcombobox.o out/alfcompat.o out/alf.o out/alfdpiaware.o out/alfedit.o out/alflabel.o out/alflayout.o out/alfnotebook.o out/alfpanel.o out/alfwindow.o +out/widgetfactory.exe: out/widgetfactory.o out/alfbutton.o out/alfcheckbox.o out/alfcombobox.o out/alfcompat.o out/alf.o out/alfdpiaware.o out/alfedit.o out/alflabel.o out/alflayout.o out/alfnotebook.o out/alfpanel.o out/alfwindow.o $(CXX) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) out/alfbutton.o: alf/alfbutton.cpp alf/alfcompat.h alf/alf.h alf/alflayout.h alf/alflist.h alf/alfpriv.h $(CXX) $(CXXFLAGS) -c -o $@ $< +out/alfcheckbox.o: alf/alfcheckbox.cpp alf/alfcompat.h alf/alf.h alf/alflayout.h alf/alflist.h alf/alfpriv.h + $(CXX) $(CXXFLAGS) -c -o $@ $< + out/alfcombobox.o: alf/alfcombobox.cpp alf/alfcompat.h alf/alf.h alf/alflayout.h alf/alflist.h alf/alfpriv.h $(CXX) $(CXXFLAGS) -c -o $@ $< @@ -47,7 +50,7 @@ out/alfwindow.o: alf/alfwindow.cpp alf/alfcompat.h alf/alf.h alf/alflayout.h al out/widgetfactory.o: widgetfactory.cpp alf/alf.h $(CXX) $(CXXFLAGS) -c -o $@ $< -out/alf/alf.c: alf/alflist.h alf/alflayout.h alf/alfcompat.h alf/alfpriv.h alf/alfbutton.cpp alf/alfcombobox.cpp alf/alfcompat.cpp alf/alf.cpp alf/alfdpiaware.cpp alf/alfedit.cpp alf/alflabel.cpp alf/alflayout.cpp alf/alfnotebook.cpp alf/alfpanel.cpp alf/alfwindow.cpp +out/alf/alf.c: alf/alflist.h alf/alflayout.h alf/alfcompat.h alf/alfpriv.h alf/alfbutton.cpp alf/alfcheckbox.cpp alf/alfcombobox.cpp alf/alfcompat.cpp alf/alf.cpp alf/alfdpiaware.cpp alf/alfedit.cpp alf/alflabel.cpp alf/alflayout.cpp alf/alfnotebook.cpp alf/alfpanel.cpp alf/alfwindow.cpp @mkdir -p out/alf @printf "#include \"alf.h\"\\n" > $@ @cat $^ | grep -v "^#pragma once" | grep -v "^#include \"alf" >> $@ diff --git a/Makefile.vc6 b/Makefile.vc6 index 3f09e24..9f34366 100644 --- a/Makefile.vc6 +++ b/Makefile.vc6 @@ -4,12 +4,15 @@ CXX = cl.exe CFLAGS = -O2 -GA -W3 -DUNICODE -D_UNICODE -D_WIN32=0x0501 -D_WIN32_WINNT=0x0501 -D_WIN32_IE=0x0501 -nologo LDFLAGS = /link unicows.lib kernel32.lib user32.lib comctl32.lib shell32.lib gdi32.lib version.lib -out/widgetfactory.exe: out/widgetfactory.obj out/alfbutton.obj out/alfcombobox.obj out/alfcompat.obj out/alf.obj out/alfdpiaware.obj out/alfedit.obj out/alflabel.obj out/alflayout.obj out/alfnotebook.obj out/alfpanel.obj out/alfwindow.obj +out/widgetfactory.exe: out/widgetfactory.obj out/alfbutton.obj out/alfcheckbox.obj out/alfcombobox.obj out/alfcompat.obj out/alf.obj out/alfdpiaware.obj out/alfedit.obj out/alflabel.obj out/alflayout.obj out/alfnotebook.obj out/alfpanel.obj out/alfwindow.obj $(CXX) $(CFLAGS) -Fe$@ $** $(LDFLAGS) out/alfbutton.obj: alf/alfbutton.cpp alf/alfcompat.h alf/alf.h alf/alflayout.h alf/alflist.h alf/alfpriv.h $(CXX) $(CFLAGS) -c -Fo$@ alf/alfbutton.cpp +out/alfcheckbox.obj: alf/alfcheckbox.cpp alf/alfcompat.h alf/alf.h alf/alflayout.h alf/alflist.h alf/alfpriv.h + $(CXX) $(CFLAGS) -c -Fo$@ alf/alfcheckbox.cpp + out/alfcombobox.obj: alf/alfcombobox.cpp alf/alfcompat.h alf/alf.h alf/alflayout.h alf/alflist.h alf/alfpriv.h $(CXX) $(CFLAGS) -c -Fo$@ alf/alfcombobox.cpp diff --git a/Makefile.vc6-ansi b/Makefile.vc6-ansi index fece0b8..feaade7 100644 --- a/Makefile.vc6-ansi +++ b/Makefile.vc6-ansi @@ -4,12 +4,15 @@ CXX = cl.exe CFLAGS = -O2 -GA -W3 -D_WIN32=0x0501 -D_WIN32_WINNT=0x0501 -D_WIN32_IE=0x0501 -nologo LDFLAGS = /link /entry:_entry /opt:nowin98 /fixed:no kernel32.lib user32.lib comctl32.lib shell32.lib gdi32.lib version.lib -out/widgetfactory.exe: out/widgetfactory.obj out/alfbutton.obj out/alfcombobox.obj out/alfcompat.obj out/alf.obj out/alfdpiaware.obj out/alfedit.obj out/alflabel.obj out/alflayout.obj out/alfnotebook.obj out/alfpanel.obj out/alfwindow.obj +out/widgetfactory.exe: out/widgetfactory.obj out/alfbutton.obj out/alfcheckbox.obj out/alfcombobox.obj out/alfcompat.obj out/alf.obj out/alfdpiaware.obj out/alfedit.obj out/alflabel.obj out/alflayout.obj out/alfnotebook.obj out/alfpanel.obj out/alfwindow.obj $(CXX) $(CFLAGS) -Fe$@ $** $(LDFLAGS) out/alfbutton.obj: alf/alfbutton.cpp alf/alfcompat.h alf/alf.h alf/alflayout.h alf/alflist.h alf/alfpriv.h $(CXX) $(CFLAGS) -c -Fo$@ alf/alfbutton.cpp +out/alfcheckbox.obj: alf/alfcheckbox.cpp alf/alfcompat.h alf/alf.h alf/alflayout.h alf/alflist.h alf/alfpriv.h + $(CXX) $(CFLAGS) -c -Fo$@ alf/alfcheckbox.cpp + out/alfcombobox.obj: alf/alfcombobox.cpp alf/alfcompat.h alf/alf.h alf/alflayout.h alf/alflist.h alf/alfpriv.h $(CXX) $(CFLAGS) -c -Fo$@ alf/alfcombobox.cpp diff --git a/alf/alf.h b/alf/alf.h index d300342..037f478 100644 --- a/alf/alf.h +++ b/alf/alf.h @@ -408,6 +408,10 @@ ALF_NotebookRemoveFlag(HWND notebook, DWORD flag) { ALF_NotebookSetFlags(notebook, ALF_NotebookFlags(notebook) & ~flag); } +// checkbox +HWND +ALF_AddCheckbox(HWND parent, WORD id, int x, int y, const TCHAR *text); + #ifdef __cplusplus } // extern C #endif diff --git a/alf/alfcheckbox.cpp b/alf/alfcheckbox.cpp new file mode 100644 index 0000000..99a285f --- /dev/null +++ b/alf/alfcheckbox.cpp @@ -0,0 +1,118 @@ +#include "alfpriv.h" + +typedef struct { + ALFColor bgcolor; + WNDPROC origWndProc; +} ALFCheckboxPriv; + +static ALFCheckboxPriv * +ALF_Checkbox_InitializePriv(void) +{ + return ALF_New(ALFCheckboxPriv, 1); +} + +static void +ALF_Checkbox_FreePriv(ALFCheckboxPriv *priv) +{ + ALF_Free(priv); +} + +static BOOL +ALF_Checkbox_IsTransparencySupported(void) +{ + // FIXME: for safety, check if the control is actually a v6 control + return ALF_Compat_IsAppThemed(); +} + +static LRESULT +ALF_Checkbox_PaintBackground(HWND window, ALFCheckboxPriv *priv, HDC hDC) +{ + // FIXME: Drawing the (transparent) background in WM_CTLCOLORSTATIC is + // only supported in a themed V6 common control + + SetTextColor(hDC, GetSysColor(COLOR_BTNTEXT)); + if (ALF_Checkbox_IsTransparencySupported()) { + RECT r; + GetClientRect(window, &r); + + if (priv->bgcolor == ALF_COLOR_TRANSPARENT) { + ALF_Compat_DrawThemeParentBackground(window, hDC, &r); + SetBkMode(hDC, TRANSPARENT); + } else { + ALF_FillRect(hDC, &r, priv->bgcolor); + SetBkMode(hDC, OPAQUE); + SetBkColor(hDC, ALF_ColorToGdi(priv->bgcolor)); + } + return (LRESULT)GetStockObject(HOLLOW_BRUSH); + } else { + SetBkColor(hDC, GetSysColor(COLOR_BTNFACE)); + SetBkMode(hDC, OPAQUE); + return (LRESULT)GetSysColorBrush(COLOR_BTNFACE); + } +} + +static LRESULT CALLBACK +ALF_Checkbox_WindowProc(HWND window, UINT msg, WPARAM wparam, LPARAM lparam) +{ + ALFCheckboxPriv *priv = (ALFCheckboxPriv *)GetWindowLongPtr(window, GWLP_USERDATA); + + if (!priv) // fuck + return DefWindowProc(window, msg, wparam, lparam); + + if (msg == ALF_WM_SETBGCOLOR) { + ALFColor newColor = (ALFColor)lparam; + if (newColor != priv->bgcolor) { + priv->bgcolor = newColor; + if (ALF_Checkbox_IsTransparencySupported()) + InvalidateRect(window, NULL, TRUE); + } + return TRUE; + } + + if (msg == 0x2000 + WM_CTLCOLORSTATIC) { + return ALF_Checkbox_PaintBackground(window, priv, (HDC)wparam); + } + + if (msg == ALF_WM_BACKGROUNDCHANGE) { + if (priv->bgcolor == ALF_COLOR_TRANSPARENT && ALF_Checkbox_IsTransparencySupported()) { + InvalidateRect(window, NULL, TRUE); + } + return TRUE; + } + + if (msg == WM_THEMECHANGED) { + InvalidateRect(window, NULL, TRUE); + } + + if (msg == WM_DESTROY) { + WNDPROC orig = priv->origWndProc; + SetWindowLongPtr(window, GWLP_WNDPROC, (LONG_PTR)orig); + SetWindowLongPtr(window, GWLP_USERDATA, 0); + ALF_Checkbox_FreePriv(priv); + return CallWindowProc(orig, window, msg, wparam, lparam); + } + + return CallWindowProc(priv->origWndProc, window, msg, wparam, lparam); +} + +HWND +ALF_AddCheckbox(HWND parent, WORD id, int x, int y, const TCHAR *text) +{ + HWND hwnd = CreateWindowEx(0, + TEXT("BUTTON"), + text, + WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_AUTOCHECKBOX, + 0, 0, 100, 100, + parent, + (HMENU)(ULONG_PTR)id, + ALF_HINSTANCE, + NULL); + + ALFCheckboxPriv *priv = ALF_Checkbox_InitializePriv(); + SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)priv); + priv->origWndProc = (WNDPROC)SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)ALF_Checkbox_WindowProc); + + ALF_AddWidget(parent, x, y, hwnd, 0, 0, ALF_LAYOUT_SIZE_CHECKBOX | ALF_LAYOUT_INHERITFONT | ALF_LAYOUT_INHERITBGCOLOR | ALF_LAYOUT_SENDBGCHANGE); + + return hwnd; +} diff --git a/alf/alflayout.cpp b/alf/alflayout.cpp index 2e56886..6c5326d 100644 --- a/alf/alflayout.cpp +++ b/alf/alflayout.cpp @@ -101,8 +101,15 @@ ALF_Layout_ForwardFontToWidget(ALFLayout *layout, HWND window, ALFWidgetPriv *wi if (widget->flags & ALF_LAYOUT_INHERITFONT) { SendMessage(widget->hwnd, WM_SETFONT, (WPARAM)font, redraw); - if (widget->flags & ALF_LAYOUT_SIZE_EDIT) - ALF_Layout_Invalidate(layout, window); + switch (widget->flags & ALF_LAYOUT_SIZETYPE_MASK) { + case ALF_LAYOUT_SIZE_EDIT: + case ALF_LAYOUT_SIZE_CHECKBOX: + ALF_Layout_Invalidate(layout, window); + break; + default: + // do nothing + break; + } } } @@ -205,6 +212,42 @@ ALF_Layout_CalcEditSize(HWND hwndWindow, ALFLayout *layout, HWND hwndEdit, SIZE ReleaseDC(hwndEdit, hDc); } +static void +ALF_Layout_CalcCheckboxSize(HWND hwndWindow, ALFLayout *layout, HWND hwndCheckbox, SIZE *ps) +{ + (void)hwndWindow; + + int checkwidth = 12 * layout->dpi / 96 + 1; + + HDC hDC = GetDC(hwndCheckbox); + HFONT font = (HFONT)SendMessage(hwndCheckbox, WM_GETFONT, 0, 0); + HFONT oldfont = SelectFont(hDC, font); + + RECT r = { 0, 0, 10, 10 }; + + TCHAR *textbuf = ALF_Text(hwndCheckbox); + DrawText(hDC, textbuf, -1, &r, DT_CALCRECT); + ALF_Free(textbuf); + + if (!ps->cx) { + // lol + int cw = 0; + GetCharWidth(hDC, '0', '0', &cw); + + ps->cx = checkwidth + r.right - r.left + cw; + } + + if (!ps->cy) { + int height = r.bottom - r.top; + if (checkwidth > height) + height = checkwidth; + ps->cy = height; + } + + SelectFont(hDC, oldfont); + ReleaseDC(hwndCheckbox, hDC); +} + static void ALF_Layout_CalcMinWidgetSize(ALFLayout *layout, ALFWidgetPriv *c, HWND window, SIZE *s) { @@ -225,6 +268,10 @@ ALF_Layout_CalcMinWidgetSize(ALFLayout *layout, ALFWidgetPriv *c, HWND window, S break; case ALF_LAYOUT_SIZE_EDIT: ALF_Layout_CalcEditSize(window, layout, c->hwnd, s); + break; + case ALF_LAYOUT_SIZE_CHECKBOX: + ALF_Layout_CalcCheckboxSize(window, layout, c->hwnd, s); + break; default: // FIXME! unimplemented break; diff --git a/alf/alfwindow.cpp b/alf/alfwindow.cpp index 188f791..d3cbfcd 100644 --- a/alf/alfwindow.cpp +++ b/alf/alfwindow.cpp @@ -246,6 +246,13 @@ ALF_DefWindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) return ret; } + if (msg == WM_CTLCOLORSTATIC) { + LRESULT ret = SendMessage((HWND)lparam, 0x2000 + WM_CTLCOLORSTATIC, wparam, lparam); + + if (ret) + return ret; + } + if (msg == WM_ACTIVATE) { if (!HIWORD(wparam)) { // if !minimized if (LOWORD(wparam)) { diff --git a/widgetfactory.cpp b/widgetfactory.cpp index 3320666..6e398b3 100644 --- a/widgetfactory.cpp +++ b/widgetfactory.cpp @@ -297,6 +297,8 @@ WinMain ALF_SetBackgroundColor(hwndLbl6, ALF_COLOR_RGB(0, 0, 255)); ALF_SetTextColor(hwndLbl6, ALF_COLOR_RGB(255, 255, 255)); + ALF_AddCheckbox(win, (WORD)-1, 1, 5, TEXT("Some Checkbox with a very very really very very long text")); + //ALF_AddWidgetLayoutFlag(win, ALF_WidgetHwndById(win, ID_LBL6), ALF_VEXPAND); //ALF_AddWidgetLayoutFlag(win, ALF_WidgetHwndById(win, ID_B1), ALF_HEXPAND); -- cgit v1.2.3