Recent

Author Topic: [SOLVED] TCheckListBox — drawing items with CheckBoxes  (Read 4441 times)

furious programming

  • Hero Member
  • *****
  • Posts: 858
[SOLVED] TCheckListBox — drawing items with CheckBoxes
« on: July 09, 2019, 02:25:40 am »
I have a standard TCheckListBox filled with data, and I need to use custom drawing event, because the items must be taller than the default and looks different.

But if I set the ItemHeight property to the needed height (for example 32) and set the Style to the lbOwnerDrawFixed, checkboxes are not painted by default, even if I don't generate an OnDrawItem event (see the attachment).


So, how can I draw items in an OnDrawItem event with checkboxes? These checkboxes must look like system controls — I don't want to use custom graphics. Thank you in advance for eny tips.
« Last Edit: July 10, 2019, 04:44:47 am by furious programming »
Lazarus 3.2 with FPC 3.2.2, Windows 10 — all 64-bit

Working solo on an acrade, action/adventure game in retro style (pixelart), programming the engine and shell from scratch, using Free Pascal and SDL. Release planned in 2026.

jamie

  • Hero Member
  • *****
  • Posts: 6130
Re: TCheckListBox — drawing items with CheckBoxes
« Reply #1 on: July 09, 2019, 03:35:33 am »
you need to draw your own check box/Mark.

You also need to move your text over to make room for it..
 
 The event gives you the state of the control so you can make your choice of images or marks
 
 If you want to do Unicode you can because it does have a check box in the list but personally I would
use a bitmap image..
 
The only true wisdom is knowing you know nothing

furious programming

  • Hero Member
  • *****
  • Posts: 858
Re: TCheckListBox — drawing items with CheckBoxes
« Reply #2 on: July 09, 2019, 03:46:57 am »
you need to draw your own check box/Mark.

So it sucks...

Quote
You also need to move your text over to make room for it..

Of course, but I don't know how much space I should leave because GetCheckWidth method is protected and I can't use it. I tried to use a code similar to that of this method, but I could not write the correct solution.
 
Quote
If you want to do Unicode you can because it does have a check box in the list but personally I would use a bitmap image..

Yes, but I still don't know how much space is needed for checkboxes.

But I have an idea — I can use ThemeServices. 8)
« Last Edit: July 09, 2019, 03:52:37 am by furious programming »
Lazarus 3.2 with FPC 3.2.2, Windows 10 — all 64-bit

Working solo on an acrade, action/adventure game in retro style (pixelart), programming the engine and shell from scratch, using Free Pascal and SDL. Release planned in 2026.

jamie

  • Hero Member
  • *****
  • Posts: 6130
Re: TCheckListBox — drawing items with CheckBoxes
« Reply #3 on: July 09, 2019, 04:10:37 am »
why not use a Timagelist and pick out an image from that.

Also I think TimageList also has a way to scale the drawing automatically to your DPI.., I would need to
look into that, but in any case you can stretchDraw the image..

 A TRect is given to you on the drawing cycle so you should already know the work space you have..
The only true wisdom is knowing you know nothing

furious programming

  • Hero Member
  • *****
  • Posts: 858
Re: TCheckListBox — drawing items with CheckBoxes
« Reply #4 on: July 09, 2019, 04:20:46 am »
why not use a Timagelist and pick out an image from that.

Because I want a native look — native checkboxes, native colors of the font and background, but text rendered in a different way (some bold, some multiline, some with additional graphics etc.).
Lazarus 3.2 with FPC 3.2.2, Windows 10 — all 64-bit

Working solo on an acrade, action/adventure game in retro style (pixelart), programming the engine and shell from scratch, using Free Pascal and SDL. Release planned in 2026.

ASerge

  • Hero Member
  • *****
  • Posts: 2241
Re: TCheckListBox — drawing items with CheckBoxes
« Reply #5 on: July 09, 2019, 03:20:58 pm »
Because I want a native look — native checkboxes, native colors of the font and background, but text rendered in a different way (some bold, some multiline, some with additional graphics etc.).
Rendering is implemented in the descendants of TWSCustomCheckListBox, depending on the platform. For example for Windows in Win32WSCheckLst.pp, see DefaultWndHandler.DrawCheckListBoxItem.
And Yes, it will have to work hard.

furious programming

  • Hero Member
  • *****
  • Posts: 858
Re: TCheckListBox — drawing items with CheckBoxes
« Reply #6 on: July 09, 2019, 08:40:54 pm »
Thanks, @ASerge. I tried to use the code of the GetCheckWidth inside an OnDrawItem event in this way:

Code: Pascal  [Select][+][-]
  1. if HandleAllocated then
  2.   CheckBoxWidth := TWSCustomCheckListBoxClass(MyListBox.WidgetSetClass).GetCheckWidth(MyListBox)
  3. else
  4.   CheckBoxWidth := 0;

but I got SIGSEGV. But this is not important — I can use ThemeServices manually and do everything by myself, just like in the DefaultWndHandler method. It is only a few lines, I can do it.
Lazarus 3.2 with FPC 3.2.2, Windows 10 — all 64-bit

Working solo on an acrade, action/adventure game in retro style (pixelart), programming the engine and shell from scratch, using Free Pascal and SDL. Release planned in 2026.

wp

  • Hero Member
  • *****
  • Posts: 11916
Re: TCheckListBox — drawing items with CheckBoxes
« Reply #7 on: July 09, 2019, 09:08:47 pm »
The best code library is contained in Lazarus itself.

Look at TCustomGrid (unit Grids), method TCustomGrid.DrawGridCheckBoxBitmaps, in the else branch wherever you see ThemeServices.

Or TAChart's TChartListbox (unit TAChartListBox). It is a customdrawn listbox with checkboxes: DrawItem, MeasureItem, CalcRects.
« Last Edit: July 09, 2019, 09:16:03 pm by wp »

furious programming

  • Hero Member
  • *****
  • Posts: 858
Re: TCheckListBox — drawing items with CheckBoxes
« Reply #8 on: July 09, 2019, 09:50:20 pm »
I made it — I ported the code from the DefaultWndHandler, but I used a high-level classes/types to set canvas properties, draw text and so on. The working solution is below (visual result in the attachment):

Code: Pascal  [Select][+][-]
  1. procedure TScoresListForm.CScoresListBoxDrawItem(AControl: TWinControl; AIndex: Integer; ARect: TRect; AState: TOwnerDrawState);
  2. const
  3.   CHECKBOX_STATE: array [TCheckBoxState, Boolean] of TThemedButton = (
  4.     (tbCheckBoxUncheckedDisabled, tbCheckBoxUncheckedNormal),
  5.     (tbCheckBoxCheckedDisabled, tbCheckBoxCheckedNormal),
  6.     (tbCheckBoxMixedDisabled, tbCheckBoxMixedNormal)
  7.   );
  8. var
  9.   ListBox: TCheckListBox absolute AControl;
  10. var
  11.   CheckBoxDetails: TThemedElementDetails;
  12.   CheckBoxSize: TSize;
  13.   CheckBoxEnabled: Boolean;
  14. var
  15.   CheckBoxRect, TextRect: TRect;
  16.   TextStyle: TTextStyle;
  17. begin
  18.   // item enabled state
  19.   CheckBoxEnabled := ListBox.Enabled and ListBox.ItemEnabled[AIndex];
  20.  
  21.   // listbox background (usually white)
  22.   ListBox.Canvas.Brush.Color := Windows.GetSysColor(COLOR_WINDOW);
  23.   ListBox.Canvas.FillRect(ARect);
  24.  
  25.   // checkbox details
  26.   CheckBoxDetails := ThemeServices.GetElementDetails(CHECKBOX_STATE[ListBox.State[AIndex], CheckBoxEnabled]);
  27.   CheckBoxSize := ThemeServices.GetDetailSize(CheckBoxDetails);
  28.   CheckBoxRect := Bounds(ARect.Left + 4, ARect.Top + (ARect.Height - CheckBoxSize.Height) div 2, CheckBoxSize.Width, CheckBoxSize.Height);
  29.  
  30.   // checkbox drawing
  31.   ThemeServices.DrawElement(ListBox.Canvas.Handle, CheckBoxDetails, CheckBoxRect);
  32.  
  33.   // text area
  34.   TextRect := ARect;
  35.   TextRect.Left := CheckBoxRect.Right + 4;
  36.  
  37.   // text background (in my system blue)
  38.   if odSelected in AState then
  39.   begin
  40.     ListBox.Canvas.Brush.Color := Windows.GetSysColor(COLOR_HIGHLIGHT);
  41.     ListBox.Canvas.FillRect(TextRect);
  42.   end;
  43.  
  44.   // text properties
  45.   TextStyle := ListBox.Canvas.TextStyle;
  46.   TextStyle.Layout := tlCenter;
  47.  
  48.   // first item is always bolded
  49.   if AIndex = 0 then
  50.     ListBox.Canvas.Font.Style := [fsBold];
  51.  
  52.   // text drawing
  53.   ListBox.Canvas.Brush.Style := bsClear;
  54.   ListBox.Canvas.TextRect(TextRect, TextRect.Left + 4, TextRect.Top, ListBox.Items[AIndex], TextStyle);
  55.  
  56.   // focus rect drawing
  57.   if odFocused in AState then
  58.   begin
  59.     ListBox.Canvas.DrawFocusRect(ARect); // this covers the default focus rect, wide as whole item
  60.     ListBox.Canvas.DrawFocusRect(TextRect); // this draws a correct focus rect, only around the text
  61.   end;
  62. end;

Thank you all for help — problem solved. 8)
« Last Edit: July 10, 2019, 04:45:58 am by furious programming »
Lazarus 3.2 with FPC 3.2.2, Windows 10 — all 64-bit

Working solo on an acrade, action/adventure game in retro style (pixelart), programming the engine and shell from scratch, using Free Pascal and SDL. Release planned in 2026.

ASerge

  • Hero Member
  • *****
  • Posts: 2241
Re: TCheckListBox — drawing items with CheckBoxes
« Reply #9 on: July 10, 2019, 11:37:10 pm »
Code: Pascal  [Select][+][-]
  1. ...
  2.   // listbox background (usually white)
  3.   ListBox.Canvas.Brush.Color := Windows.GetSysColor(COLOR_WINDOW);
  4.   ListBox.Canvas.FillRect(ARect);
  5. ...
Can be no "usually"?
Code: Pascal  [Select][+][-]
  1. ...
  2.   ListBox.Canvas.Brush.Color := ListBox.GetColorResolvingParent;
  3.   ListBox.Canvas.FillRect(ARect);
  4. ...

wp

  • Hero Member
  • *****
  • Posts: 11916
Re: TCheckListBox — drawing items with CheckBoxes
« Reply #10 on: July 11, 2019, 12:45:54 am »
  ListBox.Canvas.Brush.Color := ListBox.GetColorResolvingParent;
Wow, never seen this one!

I would have used "clWindow" instead of "Windows.GetSysColor(COLOR_WINDOW)".

ASerge

  • Hero Member
  • *****
  • Posts: 2241
Re: TCheckListBox — drawing items with CheckBoxes
« Reply #11 on: July 11, 2019, 12:59:28 am »

furious programming

  • Hero Member
  • *****
  • Posts: 858
Re: [SOLVED] TCheckListBox — drawing items with CheckBoxes
« Reply #12 on: July 12, 2019, 09:46:32 pm »
Can be no "usually"?

In the default system theme, controls like Edit, ComboBox, ListBox etc. has white background. But there is a possibility to set the dark theme (high contrast or something like this) and the background will be dark (or even black). My application must look like other system windows, so I have to get the system color.

Your proposition works the same — background color of the items/control is proper. But more logic is needed, and finally the clWindow color will be used anyway. So, in total, I can use the clWindow color, instead of using the GetSysColor function.
« Last Edit: July 13, 2019, 03:54:32 am by furious programming »
Lazarus 3.2 with FPC 3.2.2, Windows 10 — all 64-bit

Working solo on an acrade, action/adventure game in retro style (pixelart), programming the engine and shell from scratch, using Free Pascal and SDL. Release planned in 2026.

jamie

  • Hero Member
  • *****
  • Posts: 6130
Re: [SOLVED] TCheckListBox — drawing items with CheckBoxes
« Reply #13 on: July 12, 2019, 11:22:14 pm »
You are better off using the clWindow because it performs much better, no need to call functions plus it
should be cross platform this way..
The only true wisdom is knowing you know nothing

furious programming

  • Hero Member
  • *****
  • Posts: 858
Re: [SOLVED] TCheckListBox — drawing items with CheckBoxes
« Reply #14 on: July 13, 2019, 03:54:03 am »
Cross-platform solution is not needed — this application is only for Windows systems.

But you're right, it's better to use a color constant than to call a function.
Lazarus 3.2 with FPC 3.2.2, Windows 10 — all 64-bit

Working solo on an acrade, action/adventure game in retro style (pixelart), programming the engine and shell from scratch, using Free Pascal and SDL. Release planned in 2026.

 

TinyPortal © 2005-2018