Skip to content

Commit 8fd1a47

Browse files
committed
feat(win): support native UTF-8 emojis, cPicture properties and layout resize responsiveness
1 parent f92ff86 commit 8fd1a47

11 files changed

Lines changed: 690 additions & 14 deletions

File tree

ChangeLog.txt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,19 @@ HarbourBuilder - Cross-platform visual IDE for Harbour
33

44
2026-05-31
55

6+
- feat(win): native UTF-8 / Unicode emoji rendering support. Added helper
7+
functions SetWindowTextDetectUTF8, DrawTextDetectUTF8, and
8+
CreateWindowExDetectUTF8 to detect valid UTF-8 sequences (such as color
9+
emojis) and translate them to UTF-16 wide-character strings, invoking the
10+
Unicode versions of the Win32 API (SetWindowTextW, DrawTextW,
11+
CreateWindowExW) with fallback to ANSI/ACP.
12+
- feat(win): added cPicture property support for TBitBtn, TButton,
13+
TSpeedButton, and TImage, allowing loading of PNG images using GDI+ flat
14+
API (LoadPngAsBitmap) and applying them via BM_SETIMAGE and STM_SETIMAGE,
15+
supporting relative paths with ".." via GetFullPathNameA resolution.
16+
- feat(win): added form resizing layout responsiveness. Implemented
17+
OnResize block in the accounting RAD sample to dynamically adjust control
18+
bounds (Sidebar, TListView, KPI cards) when the main form is resized.
619
- fix(win): TMemo code generation. RegenerateFormCode matched
720
nType == 9 for TMemo, but the palette registers it as type 24
821
(CT_MEMO in hbide.h). Controls of type 24 fell through to the

_stress_build.ps1

Lines changed: 55 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,16 +38,34 @@ Copy-Item "$resPath\classes.prg" "$buildD\classes.prg" -Force
3838
Copy-Item "$repo\include\hbbuilder.ch" "$buildD\hbbuilder.ch" -Force
3939
Copy-Item "$repo\include\hbide.ch" "$buildD\hbide.ch" -Force
4040

41+
# Detect if any project .prg file already includes classes.prg
42+
$compileClasses = $true
43+
foreach ($p in $prgs) {
44+
$content = Get-Content $p.FullName -Raw
45+
if ($content -match '(?i)classes\.prg') {
46+
$compileClasses = $false
47+
break
48+
}
49+
}
50+
4151
$prgList = @() + $prgs.Name
42-
"[1] harbour: $($prgList -join ', '), classes.prg"
52+
if ($compileClasses) {
53+
"[1] harbour: $($prgList -join ', '), classes.prg"
54+
} else {
55+
"[1] harbour: $($prgList -join ', ') (classes.prg auto-included)"
56+
}
57+
4358
foreach ($p in $prgs) {
4459
Copy-Item $p.FullName "$buildD\$($p.Name)" -Force
4560
$base = [System.IO.Path]::GetFileNameWithoutExtension($p.Name)
4661
& "$hbBin\harbour.exe" "$buildD\$($p.Name)" -n -w -es2 -q -I"$hbInc" -I"$repo\include" -I"$buildD" -I"$resPath" -o"$buildD\$base.c" 2>&1 | Out-Null
4762
if ($LASTEXITCODE -ne 0) { Write-Error "harbour $($p.Name) failed"; exit 1 }
4863
}
49-
& "$hbBin\harbour.exe" "$buildD\classes.prg" -n -w -es2 -q -I"$hbInc" -I"$repo\include" -I"$buildD" -o"$buildD\classes.c" 2>&1 | Out-Null
50-
if ($LASTEXITCODE -ne 0) { Write-Error "harbour classes failed"; exit 1 }
64+
65+
if ($compileClasses) {
66+
& "$hbBin\harbour.exe" "$buildD\classes.prg" -n -w -es2 -q -I"$hbInc" -I"$repo\include" -I"$buildD" -o"$buildD\classes.c" 2>&1 | Out-Null
67+
if ($LASTEXITCODE -ne 0) { Write-Error "harbour classes failed"; exit 1 }
68+
}
5169

5270
"[2] cl PRG-derived C"
5371
$clFlags = @("/nologo", "/c", "/O2", "/EHsc", "/MD", "/D_CRT_SECURE_NO_WARNINGS",
@@ -59,9 +77,12 @@ foreach ($p in $prgs) {
5977
if ($LASTEXITCODE -ne 0) { Write-Error "cl $base.c failed"; exit 1 }
6078
$prgObjs += "$buildD\$base.obj"
6179
}
62-
& cl.exe @clFlags "$buildD\classes.c" "/Fo$buildD\classes.obj" 2>&1 | Out-Null
63-
if ($LASTEXITCODE -ne 0) { Write-Error "cl classes failed"; exit 1 }
64-
$prgObjs += "$buildD\classes.obj"
80+
81+
if ($compileClasses) {
82+
& cl.exe @clFlags "$buildD\classes.c" "/Fo$buildD\classes.obj" 2>&1 | Out-Null
83+
if ($LASTEXITCODE -ne 0) { Write-Error "cl classes failed"; exit 1 }
84+
$prgObjs += "$buildD\classes.obj"
85+
}
6586

6687
"[3] cl C++ core"
6788
$cppFiles = @("tform","hbbridge","tcontrol","tcontrols","hb_db_real")
@@ -70,10 +91,37 @@ foreach ($f in $cppFiles) {
7091
if ($LASTEXITCODE -ne 0) { Write-Error "cl $f.cpp failed"; exit 1 }
7192
}
7293

94+
# Compile stddlgs.c and generate/compile UI stubs
95+
& cl.exe @clFlags "$repo\resources\stddlgs.c" "/Fo$buildD\stddlgs.obj" 2>&1 | Out-Null
96+
if ($LASTEXITCODE -ne 0) { Write-Error "cl stddlgs.c failed"; exit 1 }
97+
98+
$stubsSrc = @"
99+
#include "hbapi.h"
100+
#include <windows.h>
101+
HB_FUNC( UI_MSGBOX ) { MessageBoxA( GetActiveWindow(), hb_parc(1), hb_parc(2) ? hb_parc(2) : "App", 0x40 ); }
102+
HB_FUNC( UI_MSGYESNO ) { hb_retl( MessageBoxA( GetActiveWindow(), hb_parc(1), hb_parc(2) ? hb_parc(2) : "Confirm", 0x24 ) == 6 ); }
103+
HB_FUNC( MAC_RUNTIMEERRORDIALOG ) { hb_retni( 0 ); }
104+
HB_FUNC( MAC_APPTERMINATE ) { }
105+
HB_FUNC( UI_SCENE3DNEW ) { hb_retnint( 0 ); }
106+
HB_FUNC( UI_EARTHVIEWNEW ) { hb_retnint( 0 ); }
107+
HB_FUNC( UI_MAPNEW ) { hb_retnint( 0 ); }
108+
HB_FUNC( UI_MAPSETREGION ) { }
109+
HB_FUNC( UI_MAPADDPIN ) { }
110+
HB_FUNC( UI_MAPCLEARPINS ) { }
111+
HB_FUNC( UI_MASKEDITNEW ) { hb_retnint( 0 ); }
112+
HB_FUNC( UI_STRINGGRIDNEW ) { hb_retnint( 0 ); }
113+
HB_FUNC( UI_GRIDSETCELL ) { }
114+
HB_FUNC( UI_GRIDGETCELL ) { hb_retc( "" ); }
115+
HB_FUNC( W32_ERRORDIALOG ) { MessageBoxA( GetActiveWindow(), hb_parc(1), "Error", 0x10 ); }
116+
"@
117+
[System.IO.File]::WriteAllText("$buildD\_stress_stubs.c", $stubsSrc)
118+
& cl.exe @clFlags "$buildD\_stress_stubs.c" "/Fo$buildD\_stress_stubs.obj" 2>&1 | Out-Null
119+
if ($LASTEXITCODE -ne 0) { Write-Error "cl _stress_stubs.c failed"; exit 1 }
120+
73121
"[4] link"
74122
$exePath = "$buildD\$Sample.exe"
75123
$cppObjs = $cppFiles | ForEach-Object { "$buildD\$_.obj" }
76-
$objs = $prgObjs + $cppObjs
124+
$objs = $prgObjs + $cppObjs + "$buildD\stddlgs.obj" + "$buildD\_stress_stubs.obj"
77125
$hbLibs = "hbvm.lib hbrtl.lib hbcommon.lib hblang.lib hbrdd.lib hbmacro.lib hbpp.lib hbcplr.lib hbct.lib hbhsx.lib hbsix.lib hbusrrdd.lib rddntx.lib rddnsx.lib rddcdx.lib rddfpt.lib hbcpage.lib hbpcre.lib hbzlib.lib hbdebug.lib hbsqlit3.lib sqlite3.lib gtgui.lib gtwin.lib gtwvt.lib".Split(" ")
78126
$sysLibs = "user32.lib kernel32.lib gdi32.lib comctl32.lib comdlg32.lib shell32.lib ole32.lib oleaut32.lib advapi32.lib uuid.lib ws2_32.lib winmm.lib msimg32.lib gdiplus.lib winspool.lib dwmapi.lib iphlpapi.lib".Split(" ")
79127
$linkOut = & link.exe /NOLOGO "/OUT:$exePath" /SUBSYSTEM:WINDOWS /NODEFAULTLIB:LIBCMT "/LIBPATH:$hbLib" $objs $hbLibs $sysLibs 2>&1

bin/hbbuilder_win.exe

2.5 KB
Binary file not shown.

include/hbide.h

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,7 @@ class TControl : public TObject
256256

257257
/* Non-visual database components (CT_DBFTABLE etc.) */
258258
char FFileName[260];
259+
HBITMAP FBitmap;
259260
char FRDD[16];
260261
BOOL FActive;
261262
BOOL FTransparent;
@@ -1035,4 +1036,102 @@ class TComponentPalette : public TControl
10351036
*/
10361037
TControl * CreateControlByType( BYTE bType );
10371038

1039+
/* Unicode / UTF-8 helper functions for native Emoji rendering */
1040+
#include <malloc.h>
1041+
#include <windows.h>
1042+
1043+
inline void SetWindowTextDetectUTF8( HWND hWnd, const char * szText )
1044+
{
1045+
if( !hWnd || !szText ) return;
1046+
int nLen = (int) strlen( szText );
1047+
int nWideLen = MultiByteToWideChar( CP_UTF8, MB_ERR_INVALID_CHARS, szText, nLen, NULL, 0 );
1048+
if( nWideLen > 0 )
1049+
{
1050+
WCHAR * wbuf = (WCHAR *) malloc( (nWideLen + 1) * sizeof(WCHAR) );
1051+
MultiByteToWideChar( CP_UTF8, 0, szText, nLen, wbuf, nWideLen );
1052+
wbuf[nWideLen] = 0;
1053+
SetWindowTextW( hWnd, wbuf );
1054+
free( wbuf );
1055+
}
1056+
else
1057+
{
1058+
nWideLen = MultiByteToWideChar( CP_ACP, 0, szText, nLen, NULL, 0 );
1059+
if( nWideLen > 0 )
1060+
{
1061+
WCHAR * wbuf = (WCHAR *) malloc( (nWideLen + 1) * sizeof(WCHAR) );
1062+
MultiByteToWideChar( CP_ACP, 0, szText, nLen, wbuf, nWideLen );
1063+
wbuf[nWideLen] = 0;
1064+
SetWindowTextW( hWnd, wbuf );
1065+
free( wbuf );
1066+
}
1067+
else
1068+
{
1069+
SetWindowTextA( hWnd, szText );
1070+
}
1071+
}
1072+
}
1073+
1074+
inline void DrawTextDetectUTF8( HDC hDC, const char * szText, int nCount, RECT * lpRect, UINT uFormat )
1075+
{
1076+
if( !szText ) return;
1077+
int nLen = (nCount < 0) ? (int) strlen( szText ) : nCount;
1078+
int nWideLen = MultiByteToWideChar( CP_UTF8, MB_ERR_INVALID_CHARS, szText, nLen, NULL, 0 );
1079+
if( nWideLen > 0 )
1080+
{
1081+
WCHAR * wbuf = (WCHAR *) malloc( (nWideLen + 1) * sizeof(WCHAR) );
1082+
MultiByteToWideChar( CP_UTF8, 0, szText, nLen, wbuf, nWideLen );
1083+
wbuf[nWideLen] = 0;
1084+
DrawTextW( hDC, wbuf, nWideLen, lpRect, uFormat );
1085+
free( wbuf );
1086+
}
1087+
else
1088+
{
1089+
nWideLen = MultiByteToWideChar( CP_ACP, 0, szText, nLen, NULL, 0 );
1090+
if( nWideLen > 0 )
1091+
{
1092+
WCHAR * wbuf = (WCHAR *) malloc( (nWideLen + 1) * sizeof(WCHAR) );
1093+
MultiByteToWideChar( CP_ACP, 0, szText, nLen, wbuf, nWideLen );
1094+
wbuf[nWideLen] = 0;
1095+
DrawTextW( hDC, wbuf, nWideLen, lpRect, uFormat );
1096+
free( wbuf );
1097+
}
1098+
else
1099+
{
1100+
DrawTextA( hDC, szText, nCount, lpRect, uFormat );
1101+
}
1102+
}
1103+
}
1104+
1105+
inline HWND CreateWindowExDetectUTF8( DWORD dwExStyle, const char * szClass, const char * szText, DWORD dwStyle,
1106+
int x, int y, int nWidth, int nHeight, HWND hParent, HMENU hMenu, HINSTANCE hInst, LPVOID lpParam )
1107+
{
1108+
WCHAR wClass[128] = {0};
1109+
MultiByteToWideChar( CP_ACP, 0, szClass, -1, wClass, 128 );
1110+
WCHAR * wText = NULL;
1111+
if( szText )
1112+
{
1113+
int nLen = (int) strlen( szText );
1114+
int nWideLen = MultiByteToWideChar( CP_UTF8, MB_ERR_INVALID_CHARS, szText, nLen, NULL, 0 );
1115+
if( nWideLen > 0 )
1116+
{
1117+
wText = (WCHAR *) malloc( (nWideLen + 1) * sizeof(WCHAR) );
1118+
MultiByteToWideChar( CP_UTF8, 0, szText, nLen, wText, nWideLen );
1119+
wText[nWideLen] = 0;
1120+
}
1121+
else
1122+
{
1123+
nWideLen = MultiByteToWideChar( CP_ACP, 0, szText, nLen, NULL, 0 );
1124+
if( nWideLen > 0 )
1125+
{
1126+
wText = (WCHAR *) malloc( (nWideLen + 1) * sizeof(WCHAR) );
1127+
MultiByteToWideChar( CP_ACP, 0, szText, nLen, wText, nWideLen );
1128+
wText[nWideLen] = 0;
1129+
}
1130+
}
1131+
}
1132+
HWND hWnd = CreateWindowExW( dwExStyle, wClass, wText, dwStyle, x, y, nWidth, nHeight, hParent, hMenu, hInst, lpParam );
1133+
if( wText ) free( wText );
1134+
return hWnd;
1135+
}
1136+
10381137
#endif /* _HBIDE_H_ */

0 commit comments

Comments
 (0)