Lazarus

Programming => LCL => Topic started by: furious programming on July 09, 2019, 02:25:40 am

Title: [SOLVED] TCheckListBox — drawing items with CheckBoxes
Post by: furious programming 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.
Title: Re: TCheckListBox — drawing items with CheckBoxes
Post by: jamie 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..
 
Title: Re: TCheckListBox — drawing items with CheckBoxes
Post by: furious programming 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)
Title: Re: TCheckListBox — drawing items with CheckBoxes
Post by: jamie 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..
Title: Re: TCheckListBox — drawing items with CheckBoxes
Post by: furious programming 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.).
Title: Re: TCheckListBox — drawing items with CheckBoxes
Post by: ASerge 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.
Title: Re: TCheckListBox — drawing items with CheckBoxes
Post by: furious programming 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.
Title: Re: TCheckListBox — drawing items with CheckBoxes
Post by: wp 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.
Title: Re: TCheckListBox — drawing items with CheckBoxes
Post by: furious programming 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)
Title: Re: TCheckListBox — drawing items with CheckBoxes
Post by: ASerge 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. ...
Title: Re: TCheckListBox — drawing items with CheckBoxes
Post by: wp 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)".
Title: Re: TCheckListBox — drawing items with CheckBoxes
Post by: ASerge on July 11, 2019, 12:59:28 am
  ListBox.Canvas.Brush.Color := ListBox.GetColorResolvingParent;
Wow, never seen this one!
See description in https://lazarus-ccr.sourceforge.io/docs/lcl/controls/tcontrol.color.html (https://lazarus-ccr.sourceforge.io/docs/lcl/controls/tcontrol.color.html)
Title: Re: [SOLVED] TCheckListBox — drawing items with CheckBoxes
Post by: furious programming 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.
Title: Re: [SOLVED] TCheckListBox — drawing items with CheckBoxes
Post by: jamie 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..
Title: Re: [SOLVED] TCheckListBox — drawing items with CheckBoxes
Post by: furious programming 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.
Title: Re: [SOLVED] TCheckListBox — drawing items with CheckBoxes
Post by: ASerge on July 13, 2019, 08:45:14 am
But you're right, it's better to use a color constant than to call a function.
Until you want to change the color of the listbox or set ParentColor :P
Title: Re: [SOLVED] TCheckListBox — drawing items with CheckBoxes
Post by: furious programming on July 15, 2019, 12:32:29 am
No problem — this will never happen. 8)
TinyPortal © 2005-2018