How customdrawed content could make treeitems draw rectangle to fit them

ProMiNick 40 Reputation points
2025-02-02T18:55:42.9833333+00:00

I tried to reproduce in assembly one of MS official examples related to custom draw technology. from page About Custom Draw from sample SAMPLE: CustDTv Illustrates Custom Draw in a TreeView (Q248496) for now aloowed at github so here is code(language: assembler fasm with patched core to exetnd syntax to more convinient for me):

format PE GUI 4.0
entry start

include 'win32a.inc'

PE_IMAGE_BASE = $-rva $

IDD_DIALOG = 101
IDC_TREE   = 1000
FONTSINFO_SIZE_THAT_FIT_MY_OS_FONT_COUNT = $4000

macro HANDLE value:? { dd value }
struc HANDLE value:? { . dd value }
sizeof.HANDLE=4
ITEM_COLORS  = 1
ITEM_FONTS   = 2
;ITEM_UNKNOWN = 3;
MAX_COLORS = 99
ClrTagsize equ 21
sizeof.MyColorINFO = ClrTagsize+8+4
struc MyColorINFO [COLORREF,name] {
        dd                      COLORREF
        TCHAR[8]                `COLORREF,0
        TCHAR[ClrTagsize]      name,0 }

section '.text' code readable executable

  start: RTL_C

        invoke  DialogBoxParam,PE_IMAGE_BASE,101,HWND_DESKTOP,DialogProc,0
        invoke  ExitProcess,0
flush_locals

handleCustomDraw: RTL_C pNMTVCD
        ;frame  esp
        mov     eax,[pNMTVCD]
        mov     eax,[eax+NMCUSTOMDRAWINFO.dwDrawStage]
        cmp     eax,CDDS_PREPAINT
        jz      .onprepaint
        cmp     eax,CDDS_ITEMPREPAINT
        jz      .onitemprepaint
        cmp     eax,CDDS_ITEMPOSTPAINT
        jz      .itempostpaint
        jmp     .default
  .onprepaint:
        mov     eax,CDRF_NOTIFYPOSTPAINT or CDRF_NOTIFYITEMDRAW
        jmp     .finish
  .onitemprepaint:
        mov     [TVinserter.item.mask],TVIF_HANDLE or TVIF_PARAM
        mov     eax,[pNMTVCD]
        mov     eax,[eax+NMCUSTOMDRAWINFO.dwItemSpec]
        mov     [TVinserter.item.hItem],eax
        invoke  SendMessage,[hwndTV],TVM_GETITEM,0,TVinserter.item
        mov     eax,[TVinserter.item.lParam]
        test    eax,eax
        jz      .retpostpaintnewfont
        test    dword[eax],-1
        js      .onfontsprepaint
  .oncolorsprepaint:
        mov     eax,[pNMTVCD]
        test    [eax+NMCUSTOMDRAWINFO.uItemState],CDIS_FOCUS
        jz      .prepaintunfocus
  .prepaintfocus:
        invoke  SendMessage,[hwndTV],WM_GETFONT,0,0
        invoke  GetObject,eax,sizeof.LOGFONT,logFontTEMP
        or      [logFontTEMP.lfWeight],FW_BOLD
        invoke  CreateFontIndirect,logFontTEMP
        push    eax
        mov     eax,[pNMTVCD]
        invoke  SelectObject,[eax+NMCUSTOMDRAWINFO.hdc],?
        sub     esp,4
        invoke  DeleteObject,?
        jmp     .retpostpaintnewfont
  .prepaintunfocus:
        mov     edx,[TVinserter.item.lParam]
        mov     edx,[edx]
        mov     [eax+NMCUSTOMDRAW.clrText],edx
        jmp     .retpostpaintnewfont
  .onfontsprepaint:
        push    dword[eax+4]
        mov     eax,[pNMTVCD]
        invoke  SelectObject,[eax+NMCUSTOMDRAWINFO.hdc],?
  .retpostpaintnewfont:
        mov     eax,CDRF_NOTIFYPOSTPAINT or CDRF_NEWFONT
        jmp     .finish
  .itempostpaint:
        mov     eax,[pNMTVCD]
        mov     eax,[eax+NMCUSTOMDRAWINFO.dwItemSpec]
        mov     [TVinserter.item.hItem],eax
        mov     [rc.left],eax
        invoke  SendMessage,[hwndTV],TVM_GETITEMRECT,1,rc
        mov     eax,[rc.left]
        xchg    eax,[rc.right]
        add     eax,5
        mov     [rc.left],eax
        add     [rc.right],eax
        mov     [TVinserter.item.mask],TVIF_HANDLE + TVIF_PARAM + TVIF_TEXT
        mov     [TVinserter.item.pszText],szFace
        mov     [TVinserter.item.cchTextMax],64
        invoke  SendMessage,[hwndTV],TVM_GETITEM,0,TVinserter.item
        mov     eax,[TVinserter.item.lParam]
        test    eax,eax
        jz      .finish
        test    dword[eax],-1
        js      .onfontspostpaint
  .oncolorspostpaint:
        mov     eax,[pNMTVCD]
        test    [eax+NMCUSTOMDRAWINFO.uItemState],CDIS_FOCUS
        jz      .postpaintunfocus
  .postpaintfocus:
        invoke  SetTextColor,[eax+NMCUSTOMDRAWINFO.hdc],$FF0000
        push    eax
        sub     esp,4
        mov     eax,[TVinserter.item.lParam]
        add     eax,12
        invoke  DrawText,[esp+16],eax,-1,rc,DT_LEFT
        invoke  SetTextColor,?,?
        jmp     .default
  .postpaintunfocus:
        invoke  FillRect,[eax+NMCUSTOMDRAWINFO.hdc],rc,COLOR_WINDOW+1
        jmp     .default
  .onfontspostpaint:
        jmp     .default
  .default:
        xor     eax,eax ; CDRF_DODEFAULT
  .finish:
        ;exitf
        stack_cleanup callee
flush_locals

DialogProc: RTL_C hwnddlg,msg,wparam,lparam
        ;frame  esp
        mov     eax,[msg]
        cmp     eax,WM_COMMAND
        je      .wmcommand
        cmp     eax,WM_NOTIFY
        je      .wmnotify
        cmp     eax,WM_INITDIALOG
        je      .wminitdialog
        cmp     eax,WM_DESTROY
        je      .wmdestroy
  .unprocessed:
        xor     eax,eax
        jmp     .finish
  .wmcommand:
        mov     eax,[wparam]
        sub     eax,BN_CLICKED shl 16
        jz      .finish
        cmp     eax,IDCANCEL
        ja      .unprocessed
        invoke  EndDialog,[hwnddlg],eax
        jmp     .unprocessed
  .wminitdialog:
        invoke  GetDlgItem,[hwnddlg],IDC_TREE
        mov     [hwndTV],eax
        invoke  SetFocus,eax
        invoke  SendMessage,[hwndTV],WM_GETFONT,0,0
        mov     [hFontTV],eax
        invoke  GetObject,eax,sizeof.LOGFONT,logFontTV
        stdcall insertItems
        jmp     .unprocessed
  .wmdestroy:
        push    edi
        mov     edi,FontsINFO
      @@:
        cmp     dword[edi],-1
        jnz     @F
        invoke  DeleteObject,[edi+4]
        add     edi,8
        jmp     @B
      @@:
        pop     edi
        jmp     .unprocessed
  .wmnotify:
        cmp     [wparam],IDC_TREE
        jnz     .unprocessed
        mov     eax,[lparam]
        test    eax,eax
        jz      .finish
        cmp     [eax+NMHDR.code],NM_CUSTOMDRAW
        jnz     .unprocessed
        stdcall handleCustomDraw,[lparam]
  .handled:
        invoke  SetWindowLong,[hwnddlg],DWL_MSGRESULT,eax
  .processed:
        mov     eax,1
  .finish:
        ;exitf
        stack_cleanup callee
flush_locals

insertItems: RTL_C
        ;frame  esp
        mov     [TVinserter.item.mask],TVIF_TEXT
        mov     [TVinserter.item.pszText],szColors
        mov     [TVinserter.item.cchTextMax],szColors.length
        invoke  SendMessage,[hwndTV],TVM_INSERTITEM,0,TVinserter
        mov     [TVinserter.hParent],eax
        invoke  SendMessage,[hwndTV],TVM_SELECTITEM,TVGN_CARET,eax
        mov     [TVinserter.item.mask],TVIF_TEXT or TVIF_PARAM
        mov     [TVinserter.item.lParam],ColorsINFO-sizeof.MyColorINFO
        mov     [TVinserter.item.pszText],ColorsINFO+4-sizeof.MyColorINFO
        mov     [TVinserter.item.cchTextMax],8
        push    edi
        mov     edi,MAX_COLORS
      @@:
        add     [TVinserter.item.lParam],sizeof.MyColorINFO
        add     [TVinserter.item.pszText],sizeof.MyColorINFO
        invoke  SendMessage,[hwndTV],TVM_INSERTITEM,0,TVinserter
        dec     edi
        jne      @B
        mov     [TVinserter.hParent],TVI_ROOT
        mov     [TVinserter.item.mask],TVIF_TEXT
        mov     [TVinserter.item.pszText],szFonts
        mov     [TVinserter.item.cchTextMax],szFonts.length
        invoke  SendMessage,[hwndTV],TVM_INSERTITEM,0,TVinserter

        push    eax
        mov     [TVinserter.item.lParam],FontsINFO
      @@:
        mov     [TVinserter.hParent],eax
        mov     [TVinserter.item.mask],TVIF_TEXT
        mov     eax,20
        mul     edi
        add     eax,szCharSets
        mov     [TVinserter.item.pszText],eax
        mov     [TVinserter.item.cchTextMax],20
        invoke  SendMessage,[hwndTV],TVM_INSERTITEM,0,TVinserter
        mov     [TVinserter.hParent],eax
        mov     al,[edi+baCharSets]
        mov     [logFontTEMP.lfCharSet],al
        stdcall enumFonts
        mov     eax,[esp]
        inc     edi
        cmp     edi,14
        jb      @B
        pop     eax
        pop     edi
  .finish:
        ;exitf
        stack_cleanup callee
flush_locals

enumFonts: RTL_C
        ;frame  esp
        invoke  GetDC,NULL
        push    eax
        invoke  EnumFontFamiliesEx,eax,logFontTEMP,enumFontFamilyProc,0,0
        invoke  ReleaseDC,0,?
  .finish:
        ;exitf
        stack_cleanup callee
flush_locals

enumFontFamilyProc: RTL_C lpelfe, lpntme, FontType, lParam
        ;frame  esp
        mov     eax,[lpelfe]
        mov     al,[eax+LOGFONT.lfCharSet]
        cmp     al,[logFontTEMP.lfCharSet]
        jnz     .retContinueEnumeration
        mov     [TVinserter.item.mask],TVIF_TEXT or TVIF_PARAM
        mov     eax,[lpelfe]
        mov     edx,[logFontTV.lfHeight]
        mov     [eax+LOGFONT.lfHeight],edx
        mov     edx,[logFontTV.lfWidth]
        mov     [eax+LOGFONT.lfWidth],edx
        invoke  CreateFontIndirect,eax
        add     [TVinserter.item.lParam],8
        mov     edx,[TVinserter.item.lParam]
        or      dword[edx],-1
        mov     dword[edx+4],eax
        mov     eax,[lpelfe]
        add     eax,ENUMLOGFONTEX.elfFullName
        mov     [TVinserter.item.pszText],eax
        mov     [TVinserter.item.cchTextMax],64
        invoke  SendMessage,[hwndTV],TVM_INSERTITEM,0,TVinserter
  .retContinueEnumeration:
        or      al,TRUE
  .finish:
        ;exitf
        stack_cleanup callee
flush_locals

section '.data' data readable writeable
        ColorsINFO      MyColorINFO\
                        $FFFFFF,'White',\
                        $0000FF,'Red',\
                        $00FF00,'Green',\
                        $FF0000,'Blue',\
                        $FF00FF,'Magenta',\
                        $FFFF00,'Cyan',\
                        $00FFFF,'Yellow',\
                        $000000,'Black',\
                        $93DB70,'Aquamarine',\
                        $17335C,'Baker''s Chocolate',\
                        $9F5F9F,'Blue Violet',\
                        $42A6B5,'Brass',\
                        $19D9D9,'Bright Gold',\
                        $2A2AA6,'Brown',\
                        $53788C,'Bronze',\
                        $3D7DA6,'Bronze II',\
                        $9F9F5F,'Cadet Blue',\
                        $1987D9,'Cool Copper',\
                        $3373B8,'Copper',\
                        $007FFF,'Coral',\
                        $6F4242,'Corn Flower Blue',\
                        $33405C,'Dark Brown',\
                        $2F4F2F,'Dark Green',\
                        $6E764A,'Dark Green Copper',\
                        $2F4F4F,'Dark Olive Green',\
                        $CD3299,'Dark Orchid',\
                        $781F87,'Dark Purple',\
                        $8E236B,'Dark Slate Blue',\
                        $4F4F2F,'Dark Slate Grey',\
                        $4F6997,'Dark Tan',\
                        $DB9370,'Dark Turquoise',\
                        $425E85,'Dark Wood',\
                        $545454,'Dim Grey',\
                        $636385,'Dusty Rose',\
                        $7592D1,'Feldspar',\
                        $23238E,'Firebrick',\
                        $238E23,'Forest Green',\
                        $327FCD,'Gold',\
                        $70DBDB,'Goldenrod',\
                        $C0C0C0,'Grey',\
                        $767F52,'Green Copper',\
                        $70DB93,'Green Yellow',\
                        $215E21,'Hunter Green',\
                        $2F2F4E,'Indian Red',\
                        $5F9F9F,'Khaki',\
                        $D9D9C0,'Light Blue',\
                        $A8A8A8,'Light Grey',\
                        $BD8F8F,'Light Steel Blue',\
                        $A6C2E9,'Light Wood',\
                        $32CD32,'Lime Green',\
                        $3378E4,'Mandarian Orange',\
                        $6B238E,'Maroon',\
                        $99CD32,'Medium Aquamarine',\
                        $CD3232,'Medium Blue',\
                        $238E6B,'Medium Forest Green',\
                        $AEEAEA,'Medium Goldenrod',\
                        $DB7093,'Medium Orchid',\
                        $426F42,'Medium Sea Green',\
                        $FF007F,'Medium Slate Blue',\
                        $00FF7F,'Medium Spring Green',\
                        $DBDB70,'Medium Turquoise',\
                        $9370DB,'Medium Violet Red',\
                        $6480A6,'Medium Wood',\
                        $4F2F2F,'Midnight Blue',\
                        $8E2323,'Navy Blue',\
                        $FF4D4D,'Neon Blue',\
                        $C76EFF,'Neon Pink',\
                        $9C0000,'New Midnight Blue',\
                        $9EC7EB,'New Tan',\
                        $3BB5CF,'Old Gold',\
                        $007FFF,'Orange',\
                        $0024FF,'Orange Red',\
                        $DB70DB,'Orchid',\
                        $8FBC8F,'Pale Green',\
                        $8F8FBC,'Pink',\
                        $EAADEA,'Plum',\
                        $F3D9D9,'Quartz',\
                        $AB5959,'Rich Blue',\
                        $42426F,'Salmon',\
                        $17178C,'Scarlet',\
                        $688E23,'Sea Green',\
                        $26426B,'Semi-Sweet Chocolate',\
                        $236B8E,'Sienna',\
                        $FAE8E6,'Silver',\
                        $CC9932,'Sky Blue',\
                        $FF7F00,'Slate Blue',\
                        $AE1CFF,'Spicy Pink',\
                        $7FFF00,'Spring Green',\
                        $8E6B23,'Steel Blue',\
                        $DEB038,'Summer Sky',\
                        $7093DB,'Tan',\
                        $D8BFD8,'Thistle',\
                        $EAEAAD,'Turquoise',\
                        $33405C,'Very Dark Brown',\
                        $CDCDCD,'Very Light Grey',\
                        $4F2F4F,'Violet',\
                        $9932CC,'Violet Red',\
                        $BFD8D8,'Wheat',\
                        $32CC99,'Yellow Green'
        szColors        db 'Some Colors and their (HTML) names',0
                        .length = $-szColors-1
        szFonts        db 'Fonts installed in the system',0
                        .length = $-szFonts-1
        szCharSets      TCHAR[20] 'ANSI_CHARSET',0
                        TCHAR[20] 'BALTIC_CHARSET',0
                        TCHAR[20] 'CHINESEBIG5_CHARSET',0
                        TCHAR[20] 'DEFAULT_CHARSET',0
                        TCHAR[20] 'EASTEUROPE_CHARSET',0
                        TCHAR[20] 'GB2312_CHARSET',0
                        TCHAR[20] 'GREEK_CHARSET',0
                        TCHAR[20] 'HANGUL_CHARSET',0
                        TCHAR[20] 'MAC_CHARSET',0
                        TCHAR[20] 'OEM_CHARSET',0
                        TCHAR[20] 'RUSSIAN_CHARSET',0
                        TCHAR[20] 'SHIFTJIS_CHARSET',0
                        TCHAR[20] 'SYMBOL_CHARSET',0
                        TCHAR[20] 'TURKISH_CHARSET',0
        szFace          TCHAR[64]
        baCharSets      db ANSI_CHARSET,\
                           BALTIC_CHARSET,\
                           CHINESEBIG5_CHARSET,\
                           DEFAULT_CHARSET,\
                           EASTEUROPE_CHARSET,\
                           GB2312_CHARSET,\
                           GREEK_CHARSET,\
                           HANGEUL_CHARSET,\
                           MAC_CHARSET,\
                           OEM_CHARSET,\
                           RUSSIAN_CHARSET,\
                           SHIFTJIS_CHARSET,\
                           SYMBOL_CHARSET,\
                           TURKISH_CHARSET,\
                           $FF
        TVinserter      TV_INSERTSTRUCT TVI_ROOT,0
        hwndTV          HANDLE
        hFontTV         HANDLE
        logFontTV       LOGFONT
        logFontTEMP     LOGFONT
        rc              RECT
        FontsINFO       rb FONTSINFO_SIZE_THAT_FIT_MY_OS_FONT_COUNT

section '.idata' import data readable writeable

  library kernel,'KERNEL32.DLL',\
          user,'USER32.DLL',\
          gdi,'GDI32.DLL',\
          comctl,'COMCTL32.DLL'

  import kernel,\
         GetModuleHandle,'GetModuleHandleA',\
         ExitProcess,'ExitProcess'

  import user,\
         DialogBoxParam,'DialogBoxParamA',\
         DrawText,'DrawTextA',\
         CreateWindowEx,'CreateWindowExA',\
         CallWindowProc,'CallWindowProcA',\
         CheckRadioButton,'CheckRadioButton',\
         FillRect,'FillRect',\
         GetClientRect,'GetClientRect',\
         GetDC,'GetDC',\
         GetDlgItem,'GetDlgItem',\
         GetDlgItemText,'GetDlgItemTextA',\
         IsDlgButtonChecked,'IsDlgButtonChecked',\
         MessageBox,'MessageBoxA',\
         ReleaseDC,'ReleaseDC',\
         SetFocus,'SetFocus',\
         SetWindowLong,'SetWindowLongA',\
         SetWindowText,'SetWindowTextA',\
         SetProp,'SetPropA',\
         SendMessage,'SendMessageA',\
         EndDialog,'EndDialog'

  import gdi,\
         CreateFontIndirect,'CreateFontIndirectA',\
         DeleteObject,'DeleteObject',\
         GetObject,'GetObjectA',\
         EnumFontFamiliesEx,'EnumFontFamiliesExA',\
         SelectObject,'SelectObject',\
         SetTextColor,'SetTextColor'

  import comctl,\
         InitCommonControls,'InitCommonControls'
         virtual
         dd InitCommonControls
         end virtual

section '.rsrc' resource data readable

  directory RT_DIALOG,dialogs

  resource dialogs,\
           IDD_DIALOG,LANG_ENGLISH+SUBLANG_DEFAULT,demonstration

  dialog demonstration,'Custom Draw TreeView',0, 0, 318, 262,DS_SETFONT or DS_MODALFRAME or DS_CENTER or WS_POPUPWINDOW or WS_CAPTION
    dialogitem 'SysTreeView32','Tree1',IDC_TREE, 22, 19, 200, 224,WS_VISIBLE+TVS_HASBUTTONS + TVS_HASLINES + TVS_LINESATROOT + WS_BORDER + WS_TABSTOP
    dialogitem 'BUTTON','OK',IDOK,261, 7, 50, 14,WS_VISIBLE+WS_TABSTOP+BS_DEFPUSHBUTTON
    dialogitem 'BUTTON','C&ancel',IDCANCEL,261, 24, 50, 14,WS_VISIBLE+WS_TABSTOP+BS_PUSHBUTTON
    dialogitem 'BUTTON',' Colors && Fonts ',-1,7,7,230,248,WS_VISIBLE+BS_GROUPBOX
  enddialog

I made much of simplification: in original each tree item lParam pointed to runtime created in heap structure which fields used differently for items that represent colors and items that represent fonts. My tree items lParam for non custom drawed items pointed to nowhere, while for color & for fonts they points to different(and by size too) structures. For colors such structures staticaly initialized in design time. For fonts I left enought place to fit all needed structures in my OS.

My reproduction looks similar to original one:

  • colorized tree view items are colorized when they not selected, and their font become bold when they selected and textual description of color is added i that time;
  • font tree view items are made in font that they describe
  • But...изображение_2025-02-02_211148010

As seen on images when used bold font for selection text from my representation don`t fit to tree item rectangle, blue color textual color comment do not fit drawing rectangle too. Same thing for font tree items they limited with rectangle enought to fit font description in tree view font and not always enought to fit font description in tree item own font. (I research original code, disassemble compiled executable, but I found only things I already reproduced) and there is no code that makes content to fit in drawing rectangles. Is there way to recalculate tree item rectangle in custom drawing process. On which state it could be done (prepaint, itemprepaint, itempostpaint)? And one more: when font tree item is howered(in original) appear something tooltip like in treeview font with tree item font description.(I did not find styles or runtime code that do that).

(bytes order of colors shown in reverse order in my variant - that difference I do not want to resolve, I like mine).

lets summary my request: how to make text that bold or font altered in custom draw process to fit tree view item rectangle, or on which state of custom draw extend this rectangle. And how to do that? Thanks in advance.

Windows API - Win32
Windows API - Win32
A core set of Windows application programming interfaces (APIs) for desktop and server applications. Previously known as Win32 API.
2,723 questions
{count} votes

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.