Creating an own control/gadget using Windows API
Creating an own control/gadget using Windows API
Hi,
I know that Windows offers a ton of APIs and I've already used half a ton of them. I also know that you can create your own controls/gadgets with things like focus frames and such. This is (at least in my eyes) a bit more beautiful that just using a plain canvas (which already opens the doors to do everything else). Now, let's say I want to create a simple two state button (which is what I really want). For now I used a canvas (because the standard button does not offer changing the text color, which is what I need). My button looks quite ok and tries to imitate the standard buttons, but this can get complicated when certain design elements are changed (though GetSysColor_ already provides the colors for the background and the frame). But I want the button to completely look like a standard button, that's why I need the focus frame (and some other things).
Now, the real question: I searched and searched and found pieces of information on that, but no real tutorial or something. Also, I haven't used ownerdrawing by myself (I copied some snips from the forum and such, but with no real understanding). So, does anyone have a link or something to help me with that? Has anyone already worked with the focus frames and such for creating and imitating a control/gadget? I don't want anyone to do my work, but reinventing the wheel is maybe not necessary.
Thanks in advance.
I know that Windows offers a ton of APIs and I've already used half a ton of them. I also know that you can create your own controls/gadgets with things like focus frames and such. This is (at least in my eyes) a bit more beautiful that just using a plain canvas (which already opens the doors to do everything else). Now, let's say I want to create a simple two state button (which is what I really want). For now I used a canvas (because the standard button does not offer changing the text color, which is what I need). My button looks quite ok and tries to imitate the standard buttons, but this can get complicated when certain design elements are changed (though GetSysColor_ already provides the colors for the background and the frame). But I want the button to completely look like a standard button, that's why I need the focus frame (and some other things).
Now, the real question: I searched and searched and found pieces of information on that, but no real tutorial or something. Also, I haven't used ownerdrawing by myself (I copied some snips from the forum and such, but with no real understanding). So, does anyone have a link or something to help me with that? Has anyone already worked with the focus frames and such for creating and imitating a control/gadget? I don't want anyone to do my work, but reinventing the wheel is maybe not necessary.
Thanks in advance.
Good morning, that's a nice tnetennba!
PureBasic 6.21/Windows 11 x64/Ryzen 7900X/32GB RAM/3TB SSD
Synology DS1821+/DX517, 130.9TB+50.8TB+2TB SSD
PureBasic 6.21/Windows 11 x64/Ryzen 7900X/32GB RAM/3TB SSD
Synology DS1821+/DX517, 130.9TB+50.8TB+2TB SSD
Re: Creating an own control/gadget using Windows API
You might want to take a look at ChrisR's "IceButtons Windows library" to give you some ideas: viewtopic.php?p=608752
Re: Creating an own control/gadget using Windows API
Are you looking for DrawFocusRect? See https://learn.microsoft.com/en-us/windo ... wfocusrect
Hex0r recently published a gadget that uses an image as a base rather than a canvas:
viewtopic.php?p=608455&hilit=slider#p608455
A lot of the examples for a truly native control implementation will be 'C' flavour:
https://learn.microsoft.com/en-us/windo ... rols-intro
https://www.codeproject.com/Articles/55 ... The-Basics
Code: Select all
Define.i dc, Evt
Define rc.RECT
If OpenWindow(0, 0, 0, 600, 230, "CalendarGadget", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
CalendarGadget(0, 10, 10, 250, 200)
rc\top = 14
rc\bottom = 202
rc\left = 14
rc\right = 254
dc = GetDC_(WindowID(0))
DrawFocusRect_(dc, @rc)
AddWindowTimer(0, 0, 1500)
Repeat
Evt = WaitWindowEvent()
If Evt = #PB_Event_Timer
DrawFocusRect_(dc, @rc)
EndIf
Until Evt = #PB_Event_CloseWindow
EndIf
viewtopic.php?p=608455&hilit=slider#p608455
A lot of the examples for a truly native control implementation will be 'C' flavour:
https://learn.microsoft.com/en-us/windo ... rols-intro
https://www.codeproject.com/Articles/55 ... The-Basics
Re: Creating an own control/gadget using Windows API
Aye, thanks you both. I'll try this out with custom button and combobox.
Is it also possible to really give the canvas the focus, like, to work with the tab key?
Is it also possible to really give the canvas the focus, like, to work with the tab key?
Good morning, that's a nice tnetennba!
PureBasic 6.21/Windows 11 x64/Ryzen 7900X/32GB RAM/3TB SSD
Synology DS1821+/DX517, 130.9TB+50.8TB+2TB SSD
PureBasic 6.21/Windows 11 x64/Ryzen 7900X/32GB RAM/3TB SSD
Synology DS1821+/DX517, 130.9TB+50.8TB+2TB SSD
Re: Creating an own control/gadget using Windows API
What I remember from experiments at VB6 Times
A Button and other standard Gagdets are created with the CreateWindow-API.
You need the correct ClassName for a Button.
It's existing an old implementation in VisualBasic6 from VBaccelerator.com
here the link!
https://www.vbaccelerator.com/home/VB/C ... ttons.html
But actually it is not possible to download it!
I searched in my archives but I don't have the code. But if we want to get this code, I'm sure some guys from
the activevb.de form have it.
But for now, I can supply a C++ Code how to do it with WinAPI. It's from the web, but the original link I don't have.
The example is a complete Class wich is registred in Windows.
The problem is: Setting the Callback Procedure is part of the Class-Registering function.
But for simple using a button, I guess registering a own class is not the way!
But how to get a callback without registering a own Class? I don't know this!
her the link to the CreateWindow API Help
http://winapi.freetechsecrets.com/win32 ... Window.htm
here the C++ Class Code for a owner drawn Button
A Button and other standard Gagdets are created with the CreateWindow-API.
You need the correct ClassName for a Button.
It's existing an old implementation in VisualBasic6 from VBaccelerator.com
here the link!
https://www.vbaccelerator.com/home/VB/C ... ttons.html
But actually it is not possible to download it!
I searched in my archives but I don't have the code. But if we want to get this code, I'm sure some guys from
the activevb.de form have it.
But for now, I can supply a C++ Code how to do it with WinAPI. It's from the web, but the original link I don't have.
The example is a complete Class wich is registred in Windows.
The problem is: Setting the Callback Procedure is part of the Class-Registering function.
But for simple using a button, I guess registering a own class is not the way!
But how to get a callback without registering a own Class? I don't know this!
her the link to the CreateWindow API Help
http://winapi.freetechsecrets.com/win32 ... Window.htm
here the C++ Class Code for a owner drawn Button
Code: Select all
// Test_Button.cpp : Defines the entry point for the application.
//
#include "framework.h"
#include "Test_Button.h"
#include <commctrl.h>
#pragma comment(lib,"Comctl32.lib")
#define MAX_LOADSTRING 100
#define IDC_OWNERDRAWBUTTON 101
// Global Variables:
HINSTANCE hInst; // current instance
WCHAR szTitle[MAX_LOADSTRING]; // The title bar text
WCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name
// Forward declarations of functions included in this code module:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK OwnerDrawButtonProc(HWND hWnd, UINT uMsg, WPARAM wParam,
LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
HWND hWndStatic;
HBITMAP hBmp1;
HBITMAP hBmp;
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// TODO: Place code here.
// Initialize global strings
LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadStringW(hInstance, IDC_TESTBUTTON, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// Perform application initialization:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_TESTBUTTON));
MSG msg;
// Main message loop:
while (GetMessage(&msg, nullptr, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int) msg.wParam;
}
//
// FUNCTION: MyRegisterClass()
//
// PURPOSE: Registers the window class.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_TESTBUTTON));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_TESTBUTTON);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassExW(&wcex);
}
//
// FUNCTION: InitInstance(HINSTANCE, int)
//
// PURPOSE: Saves instance handle and creates main window
//
// COMMENTS:
//
// In this function, we save the instance handle in a global variable and
// create and display the main program window.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance; // Store instance handle in our global variable
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
//
// FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// PURPOSE: Processes messages for the main window.
//
// WM_COMMAND - process the application menu
// WM_PAINT - Paint the main window
// WM_DESTROY - post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
{
hBmp = (HBITMAP)LoadImage(GetModuleHandle(NULL),
MAKEINTRESOURCE(IDB_BITMAP1),
IMAGE_BITMAP,
NULL,
NULL,
LR_DEFAULTCOLOR);
hBmp1 = (HBITMAP)LoadImage(GetModuleHandle(NULL),
MAKEINTRESOURCE(IDB_BITMAP2),
IMAGE_BITMAP,
NULL,
NULL,
LR_DEFAULTCOLOR);
hWndStatic = CreateWindowEx(0, L"button", NULL, WS_CHILD | WS_VISIBLE | BS_OWNERDRAW, 500, 400, 46, 29, hWnd, (HMENU)IDC_OWNERDRAWBUTTON, hInst, NULL);
SetWindowSubclass(hWndStatic, &OwnerDrawButtonProc, IDC_OWNERDRAWBUTTON, 0);
}
break;
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code that uses hdc here...
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
// Message handler for about box.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
LRESULT CALLBACK OwnerDrawButtonProc(HWND hWnd, UINT uMsg, WPARAM wParam,
LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
BITMAP bitmap01;
HDC hdcMem01;
HGDIOBJ oldBitmap01;
switch (uMsg)
{
case WM_LBUTTONDOWN:
{
RECT rc;
HDC hdc = GetDC(hWnd);
hdcMem01 = CreateCompatibleDC(hdc);
oldBitmap01 = SelectObject(hdcMem01, hBmp1);
GetObject(hBmp1, sizeof(bitmap01), &bitmap01);
BitBlt(hdc, 0, 0, bitmap01.bmWidth, bitmap01.bmHeight, hdcMem01, 0, 0, SRCCOPY);
SelectObject(hdcMem01, oldBitmap01);
DeleteDC(hdcMem01);
}
break;
case WM_PAINT:
{
RECT rc;
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
hdcMem01 = CreateCompatibleDC(hdc);
oldBitmap01 = SelectObject(hdcMem01, hBmp);
GetObject(hBmp, sizeof(bitmap01), &bitmap01);
BitBlt(hdc, 0, 0, bitmap01.bmWidth, bitmap01.bmHeight, hdcMem01, 0, 0, SRCCOPY);
SelectObject(hdcMem01, oldBitmap01);
DeleteDC(hdcMem01);
EndPaint(hWnd, &ps);
}
break;
case WM_LBUTTONUP:
{
HDC hdc = GetDC(hWnd);
hdcMem01 = CreateCompatibleDC(hdc);
oldBitmap01 = SelectObject(hdcMem01, hBmp);
GetObject(hBmp, sizeof(bitmap01), &bitmap01);
BitBlt(hdc, 0, 0, bitmap01.bmWidth, bitmap01.bmHeight, hdcMem01, 0, 0, SRCCOPY);
SelectObject(hdcMem01, oldBitmap01);
DeleteDC(hdcMem01);
}
break;
case WM_NCDESTROY:
RemoveWindowSubclass(hWnd, &OwnerDrawButtonProc, 1);
break;
}
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
Re: Creating an own control/gadget using Windows API
here the link to the complete vbAccelartaor-Archive on github!
maybe here you can find some code for owner drawn controls
https://github.com/tannerhelland/vbAcce ... ree/master
maybe here you can find some code for owner drawn controls
https://github.com/tannerhelland/vbAcce ... ree/master
Re: Creating an own control/gadget using Windows API
Hi SMaag,
I've already done that, that's no problem for me. It's just really about simulating "missing" features for existing controls, so...mimicking them as close as possible. But thanks!
I've already done that, that's no problem for me. It's just really about simulating "missing" features for existing controls, so...mimicking them as close as possible. But thanks!
Good morning, that's a nice tnetennba!
PureBasic 6.21/Windows 11 x64/Ryzen 7900X/32GB RAM/3TB SSD
Synology DS1821+/DX517, 130.9TB+50.8TB+2TB SSD
PureBasic 6.21/Windows 11 x64/Ryzen 7900X/32GB RAM/3TB SSD
Synology DS1821+/DX517, 130.9TB+50.8TB+2TB SSD
Re: Creating an own control/gadget using Windows API
If you just want to change the text color of any button, you can do something similar to the way in the post below.
CheckBox & Option Color Theme ?
viewtopic.php?t=78966
Note that if you want to create a button with a canvas gadget, the #PB_Canvas_DrawFocus and #PB_Canvas_Keyboard flags will be helpful.
CheckBox & Option Color Theme ?
viewtopic.php?t=78966
Note that if you want to create a button with a canvas gadget, the #PB_Canvas_DrawFocus and #PB_Canvas_Keyboard flags will be helpful.
Code: Select all
DeclareModule API_HookEngine
Declare.i Hook(*OldFunctionAddress, *NewFunctionAddress)
Declare.i UnHook(*hook_ptr)
Declare.i ProcAddress(ModuleName$, ProcName$)
EndDeclareModule
Module API_HookEngine
EnableExplicit
Structure opcode
CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
mov.u
CompilerElse
mov.a
CompilerEndIf
addr.i
push.a
ret.a
EndStructure
Structure hookstruct
addr.i
hook.opcode
orig.a[SizeOf(opcode)]
EndStructure
CompilerIf #PB_Compiler_Unicode
Import "kernel32.lib"
GetProcAddress(hModule, lpProcName.p-ascii)
EndImport
CompilerElse
Import "kernel32.lib"
GetProcAddress(hModule, lpProcName.s)
EndImport
CompilerEndIf
Procedure.i ProcAddress(ModuleName$, ProcName$)
Protected moduleH.i
moduleH = GetModuleHandle_(ModuleName$)
If moduleH = #Null
moduleH = LoadLibrary_(ModuleName$)
If moduleH = #Null
ProcedureReturn #Null
EndIf
EndIf
ProcedureReturn GetProcAddress(moduleH, ProcName$)
EndProcedure
Procedure Hook(*OldFunctionAddress, *NewFunctionAddress)
Protected *hook_ptr.hookstruct
If Not *OldFunctionAddress
ProcedureReturn #Null
EndIf
*hook_ptr = AllocateMemory(SizeOf(hookstruct))
*hook_ptr\addr = *OldFunctionAddress
CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
*hook_ptr\hook\mov = $B848
CompilerElse
*hook_ptr\hook\mov = $B8
CompilerEndIf
*hook_ptr\hook\addr = *NewFunctionAddress
*hook_ptr\hook\push = $50
*hook_ptr\hook\ret = $C3
CopyMemory(*OldFunctionAddress, @*hook_ptr\orig, SizeOf(opcode))
If Not WriteProcessMemory_(GetCurrentProcess_(), *OldFunctionAddress, @*hook_ptr\hook, SizeOf(opcode), #Null)
FreeMemory(*hook_ptr)
ProcedureReturn #Null
Else
ProcedureReturn *hook_ptr
EndIf
EndProcedure
Procedure.i UnHook(*hook_ptr.hookstruct)
Protected retValue.i
If *hook_ptr
If *hook_ptr\addr
If WriteProcessMemory_(GetCurrentProcess_(), *hook_ptr\addr, @*hook_ptr\orig, SizeOf(opcode), #Null)
retValue = *hook_ptr\addr
FreeMemory(*hook_ptr)
ProcedureReturn retValue
EndIf
EndIf
EndIf
ProcedureReturn #Null
EndProcedure
Procedure DisableHook(*hook_ptr.hookstruct)
If *hook_ptr
If *hook_ptr\addr
If WriteProcessMemory_(GetCurrentProcess_(), *hook_ptr\addr, @*hook_ptr\orig, SizeOf(opcode), #Null)
ProcedureReturn *hook_ptr\addr
EndIf
EndIf
EndIf
ProcedureReturn #Null
EndProcedure
Procedure EnableHook(*hook_ptr.hookstruct)
If *hook_ptr
If *hook_ptr\hook\addr
If WriteProcessMemory_(GetCurrentProcess_(), *hook_ptr\addr, @*hook_ptr\hook, SizeOf(opcode), #Null)
ProcedureReturn *hook_ptr\addr
EndIf
EndIf
EndIf
ProcedureReturn #Null
EndProcedure
EndModule
UseModule API_HookEngine
EnumerationBinary
#DTT_TEXTCOLOR ;(1 << 0) ;// crText has been specified
#DTT_BORDERCOLOR ;(1 << 1) ;// crBorder has been specified
#DTT_SHADOWCOLOR ;(1 << 2) ;// crShadow has been specified
#DTT_SHADOWTYPE ;(1 << 3) ;// iTextShadowType has been specified
#DTT_SHADOWOFFSET ;(1 << 4) ;// ptShadowOffset has been specified
#DTT_BORDERSIZE ;(1 << 5) ;// iBorderSize has been specified
#DTT_FONTPROP ;(1 << 6) ;// iFontPropId has been specified
#DTT_COLORPROP ;(1 << 7) ;// iColorPropId has been specified
#DTT_STATEID ;(1 << 8) ;// IStateId has been specified
#DTT_CALCRECT ;(1 << 9) ;// Use pRect as and in/out parameter
#DTT_APPLYOVERLAY ;(1 << 10) ;// fApplyOverlay has been specified
#DTT_GLOWSIZE ;(1 << 11) ;// iGlowSize has been specified
#DTT_CALLBACK ;(1 << 12) ;// pfnDrawTextCallback has been specified
#DTT_COMPOSITED ;(1 << 13) ;// Draws text with antialiased alpha (needs a DIB section)
EndEnumeration
#DTT_VALIDBITS = #DTT_TEXTCOLOR | #DTT_BORDERCOLOR | #DTT_SHADOWCOLOR | #DTT_SHADOWTYPE | #DTT_SHADOWOFFSET |
#DTT_BORDERSIZE | #DTT_FONTPROP | #DTT_COLORPROP | #DTT_STATEID | #DTT_CALCRECT |
#DTT_APPLYOVERLAY | #DTT_GLOWSIZE | #DTT_COMPOSITED
#BP_PUSHBUTTON = 1
#BP_RADIOBUTTON = 2
#BP_CHECKBOX = 3
Structure DTTOPTS
dwSize.l
dwFlags.l
crText.l
crBorder.l
crShadow.l
iTextShadowType.l
ptShadowOffset.POINT
iBorderSize.l
iFontPropId.l
iColorPropId.l
iStateId.l
fApplyOverlay.l
iGlowSize.l
*pfnDrawTextCallback
lParam.l
EndStructure
Prototype DrawThemeTextEx(hTheme, hdc, iPartId, iStateId, *pszText, cchText, dwTextFlags.l, *pRect, *pOptions)
Global DrawThemeTextEx.DrawThemeTextEx
Structure ColoredGadgetInfo
FrontColor.i
hBrushBackColor.i
EndStructure
Global NewMap ColoredGadget.ColoredGadgetInfo()
#App_BrushWidth = 2000 ;Set the value large enough in consideration of changing a gadget width.
Procedure MainWindow_Callback(hWnd, uMsg, wParam, lParam)
Protected Gadget, Result = #PB_ProcessPureBasicEvents
Select uMsg
Case #WM_CTLCOLORBTN
Gadget = GetProp_(lParam, "pb_id")
If IsGadget(Gadget) And GadgetType(Gadget) = #PB_GadgetType_Button
If FindMapElement(ColoredGadget(), Str(lParam))
;SetBkMode_(wParam, #TRANSPARENT)
Result = ColoredGadget()\hBrushBackColor
EndIf
EndIf
EndSelect
ProcedureReturn Result
EndProcedure
Procedure CreateBackgroundBrush(hwnd)
Protected hBrush, Img
If hwnd = 0 : ProcedureReturn 0 : EndIf
Img = CreateImage(#PB_Any, #App_BrushWidth, 1, 24, GetSysColor_(#COLOR_3DFACE))
If Img
If StartDrawing(ImageOutput(Img))
;Write the handle value in the last few bytes of the pattern brush.
Plot(#App_BrushWidth - 1, 0, hwnd & $FFFF)
Plot(#App_BrushWidth - 2, 0, (hwnd >> 16) & $FFFF)
CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
Plot(#App_BrushWidth - 3, 0, (hwnd >> 32) & $FFFF)
Plot(#App_BrushWidth - 4, 0, (hwnd >> 48) & $FFFF)
CompilerEndIf
StopDrawing()
hBrush = CreatePatternBrush_(ImageID(Img))
EndIf
FreeImage(Img)
EndIf
ProcedureReturn hBrush
EndProcedure
Procedure SetButtonGadgetTextColor(Gadget, FrontColor = #PB_Default)
Protected Result, hwnd
If IsGadget(Gadget)
If GadgetType(Gadget) <> #PB_GadgetType_Button
ProcedureReturn 0
EndIf
hwnd = GadgetID(Gadget)
If hwnd
SendMessage_(hwnd, #WM_SETREDRAW, 0, 0)
Repeat
With ColoredGadget()
;Check if the gadget already exists in the map.
If FindMapElement(ColoredGadget(), Str(hwnd))
If FrontColor = #PB_Default
;Remove the gadget from the map.
DeleteObject_(\hBrushBackColor)
DeleteMapElement(ColoredGadget())
Result = 1
Break
EndIf
If \FrontColor <> FrontColor
\FrontColor = FrontColor
If \hBrushBackColor
DeleteObject_(\hBrushBackColor)
\hBrushBackColor = 0
EndIf
\hBrushBackColor = CreateBackgroundBrush(hwnd)
EndIf
Result = 1
Break
EndIf
;If not, add it to the map.
If AddMapElement(ColoredGadget(), Str(hwnd))
\FrontColor = FrontColor
\hBrushBackColor = CreateBackgroundBrush(hwnd)
Result = 1
EndIf
Break
EndWith
ForEver
SendMessage_(hwnd, #WM_SETREDRAW, 1, 0)
RedrawWindow_(hwnd, 0, 0, #RDW_ERASE | #RDW_FRAME | #RDW_INVALIDATE | #RDW_ALLCHILDREN)
EndIf
EndIf
ProcedureReturn Result
EndProcedure
Procedure My_DrawThemeText(hTheme, hdc, iPartId, iStateId, *pszText, cchText, dwTextFlags, dwTextFlags2, *pRect)
Protected opt.DTTOPTS\dwSize = SizeOf(DTTOPTS)
Protected hBrush, BrushInfo.LOGBRUSH, hdcTemp, rt.RECT, hBitmap, old, hwnd
If iPartId = #BP_PUSHBUTTON
hBrush = GetCurrentObject_(hdc, #OBJ_BRUSH)
If hBrush
If GetObject_(hBrush, SizeOf(LOGBRUSH), @BrushInfo)
If BrushInfo\lbStyle = #BS_PATTERN
hdcTemp = CreateCompatibleDC_(hdc)
If hdcTemp
hBitmap = CreateCompatibleBitmap_(hdc, #App_BrushWidth, 1)
If hBitmap
old = SelectObject_(hdcTemp, hBitmap)
rt\right = #App_BrushWidth
rt\bottom = 1
FillRect_(hdcTemp, rt, hBrush)
CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
hwnd = (GetPixel_(hdcTemp, #App_BrushWidth - 1, 0) & $FFFF) | ((GetPixel_(hdcTemp, #App_BrushWidth - 2, 0) & $FFFF) << 16) |
((GetPixel_(hdcTemp, #App_BrushWidth - 3, 0) & $FFFF) << 32) | ((GetPixel_(hdcTemp, #App_BrushWidth - 4, 0) & $FFFF) << 48)
CompilerElse
hwnd = (GetPixel_(hdcTemp, #App_BrushWidth - 1, 0) & $FFFF) | ((GetPixel_(hdcTemp, #App_BrushWidth - 2, 0) & $FFFF) << 16)
CompilerEndIf
If FindMapElement(ColoredGadget(), Str(hwnd))
If ColoredGadget()\FrontColor <> #PB_Default
opt\dwFlags = #DTT_TEXTCOLOR
opt\crText = ColoredGadget()\FrontColor
EndIf
EndIf
DeleteObject_(SelectObject_(hdcTemp, old))
EndIf
DeleteDC_(hdcTemp)
EndIf
EndIf
EndIf
EndIf
EndIf
ProcedureReturn DrawThemeTextEx(hTheme, hdc, iPartId, iStateId, *pszText, cchText, dwTextFlags, *pRect, @opt)
EndProcedure
If OpenLibrary(0, "UxTheme.dll")
DrawThemeTextEx = GetFunction(0, "DrawThemeTextEx") ;There is no DrawThemeTextEx in Windows XP, so this method is not available.
EndIf
If DrawThemeTextEx
*DrawThemeText = Hook(ProcAddress("UxTheme.dll", "DrawThemeText"), @My_DrawThemeText())
EndIf
Enumeration Window
#MainWindow
EndEnumeration
Enumeration Gadgets
#g_0
#g_1
#g_2
#g_3
#g_4
#g_5
#g_6
EndEnumeration
If OpenWindow(#MainWindow, 0, 0, 400, 350, "Button Text Color", #PB_Window_SystemMenu | #PB_Window_SizeGadget | #PB_Window_ScreenCentered)
SetWindowColor(#MainWindow, RGB(54, 54, 54))
ButtonGadget(#g_0, 20, 20, 160, 30, "Default")
ButtonGadget(#g_1, 220, 20, 160, 30, "#Yellow")
ContainerGadget(#g_2, 10, 60, 390, 90, #PB_Container_Flat)
SetGadgetColor(#g_2, #PB_Gadget_BackColor, RGB(128, 128, 128))
TextGadget(#g_3, 5, 5, 160, 20, "Container_1")
SetGadgetColor(#g_3, #PB_Gadget_BackColor, GetGadgetColor(#g_2, #PB_Gadget_BackColor))
ButtonGadget(#g_4, 10, 30, 160, 30, "#Blue")
ButtonGadget(#g_5, 190, 30, 180, 40, "#Magenta")
CloseGadgetList()
CheckBoxGadget(#g_6, 20, 230, 180, 40, "enable/disable")
hFont = LoadFont(0, "arial", 20, #PB_Font_Bold)
SetGadgetFont(#g_5, hFont)
SetButtonGadgetTextColor(#g_1, #Yellow)
SetButtonGadgetTextColor(#g_4, #Blue)
SetButtonGadgetTextColor(#g_5, #Magenta)
;SetButtonGadgetTextColor(#g_4)
;SetButtonGadgetTextColor(#g_5)
;DisableGadget(#g_5, 1)
SetWindowCallback(@MainWindow_Callback(), #MainWindow)
Repeat
e = WaitWindowEvent()
If e = #PB_Event_Gadget And EventGadget() = #g_6
If GetGadgetState(#g_6)
SetButtonGadgetTextColor(#g_5)
Else
SetButtonGadgetTextColor(#g_5, #Magenta)
EndIf
DisableGadget(#g_5, GetGadgetState(#g_6))
EndIf
Until e = #PB_Event_CloseWindow
EndIf
If *DrawThemeText
UnHook(*DrawThemeText)
EndIf
CloseLibrary(0)
ForEach ColoredGadget()
DeleteObject_(ColoredGadget()\hBrushBackColor)
Next
Re: Creating an own control/gadget using Windows API
Thanks to all, I'll try this...when I have power again. There's currently a power outage where I live...again (it's annoying).
Also, I'm still working on the Gerber module, so I have to split my time. But that's all great suggestions to work with.
Also, I'm still working on the Gerber module, so I have to split my time. But that's all great suggestions to work with.
Good morning, that's a nice tnetennba!
PureBasic 6.21/Windows 11 x64/Ryzen 7900X/32GB RAM/3TB SSD
Synology DS1821+/DX517, 130.9TB+50.8TB+2TB SSD
PureBasic 6.21/Windows 11 x64/Ryzen 7900X/32GB RAM/3TB SSD
Synology DS1821+/DX517, 130.9TB+50.8TB+2TB SSD
Re: Creating an own control/gadget using Windows API
Is this correct?
Why are you selecting the object again? Shouldn't deleting it be like this:
I don't know, hence why I'm asking. It returns 1 for success when doing it my way.
(Trying to find why my app has increasing GDI objects in Task Manager).
Code: Select all
old = SelectObject_(hdcTemp, hBitmap)
[...]
DeleteObject_(SelectObject_(hdcTemp, old))
Code: Select all
DeleteObject_(old)
(Trying to find why my app has increasing GDI objects in Task Manager).
Re: Creating an own control/gadget using Windows API
The result is the same as the following code.BarryG wrote: Tue Jul 23, 2024 5:45 am Is this correct?
Why are you selecting the object again? Shouldn't deleting it be like this:Code: Select all
old = SelectObject_(hdcTemp, hBitmap) [...] DeleteObject_(SelectObject_(hdcTemp, old))
I don't know, hence why I'm asking. It returns 1 for success when doing it my way.Code: Select all
DeleteObject_(old)
(Trying to find why my app has increasing GDI objects in Task Manager).
Code: Select all
old = SelectObject_(hdcTemp, hBitmap)
[...]
SelectObject_(hdcTemp, old) ; The return value is the same as hBitmap.
DeleteObject_(hBitmap)
Re: Creating an own control/gadget using Windows API
So what should I be doing? Just "DeleteObject_(hBitmap)" then?
Re: Creating an own control/gadget using Windows API
You need to select the old object back before deleting the new one, so the code is correct.
Re: Creating an own control/gadget using Windows API
The two codes are actually the same code.BarryG wrote: Tue Jul 23, 2024 9:24 am So what should I be doing? Just "DeleteObject_(hBitmap)" then?
I ran my code and checked it in the task manager and there are no GDI object leaks, so you don't need to modify anything about the code above.