'Black on black' combobox items with certain *nix desktops lazarus / 7-LCL ---------------------------------------------------------------------- Sieben · 2020-12-23 14:48 There is an annoying problem with items of TCustomComboBox and descendants like TColorBox being painted 'black on black' with certain *nix desktops which has already been discussed here and here which I might have found a solution to. Found a piece of code that calculates the 'intensity' of a background color like this: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i function Intensity(AColor: TColor): Integer;begin  AColor := ColorToRGB(AColor);  Result := GetRValue(AColor) * 61          + GetGValue(AColor) * 174          + GetBValue(AColor) * 21;end; and suggests a threshold of 32k for deciding whether to use a dark or a bright font color. Code based on this tested ok for TCustomComboBox.DrawItem as well as for overriden methods in descendants. Drawback is of course that every overridden method has to be aware of this pitfall. So I tried 'injecting' this into TCustomComboBox.LMDrawListItem prior to the call of DrawItem instead: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i function IsDarkBackground(AColor: TColor): Boolean;begin  AColor := ColorToRGB(AColor);  Result := (GetRValue(AColor) * 61           + GetGValue(AColor) * 174           + GetBValue(AColor) * 21) <= (128 * 256);end; {----------------------------------------------------------- -------------------  procedure TCustomComboBox.LMDrawListItem(var TheMessage : TLMDrawListItem);   Handler for custom drawing items. --- ---------------------------------------------------------------------- -----}procedure TCustomComboBox.LMDrawListItem(var TheMessage : TLMDrawListItem);begin  with TheMessage.DrawListItemStruct^ do  begin    FCanvas.Handle := DC;    if Font<>nil then    begin      FCanvas.Font := Font;      FCanvas.Font.PixelsPerInch := Font.PixelsPerInch;    end;    if Brush<>nil then      FCanvas.Brush := Brush;    if (ItemID <> UINT(-1)) and (odSelected in ItemState) then    begin      FCanvas.Brush.Color := clHighlight;      FCanvas.Font.Color := clHighlightText    end;     if DroppedDown                          //injection...    and (odBackgroundPainted in ItemState)    and IsDarkBackground(Canvas.Pixels[1,1]) then      Canvas.Font.Color := clHighLightText;     DrawItem(ItemID, Area, ItemState);    {if odFocused in ItemState then      DrawFocusRect(hDC, rcItem)};    FCanvas.Handle := 0;  end;end; This seems to work for every descendant of TCustomComboBox now. All an overridden DrawItem or custom OnDrawItem handler has to take care of then is not to just repaint the background but instead to use code like this: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i     if not (odBackgroundPainted in AState) then      Canvas.FillRect(ARect); as suggested by the 'original' DrawItem anyway. And not to reassign font colors of course as this is done already as well. I don't know, however, if and how this would affect any other widgetset but I think it is quite likely that it won't. I'm including a diff file done with trunk r64267 so that anyone who might be interested can test this. Some feedback would be nice... EDIT: removed diff file for nonfug, cf below. ---------------------------------------------------------------------- wp · 2020-12-23 18:05 Your code detects whether the background color is dark and then you draw the text in clHighLightText. But clHighlighttext is a themed color, it can be anything. How do you know that clHighlightText isn't dark either? Normally the system colors are well-balanced, there are only a few cases (Ubunto 16.04 and 18.04, and maybe some more) where the theme designers used their own rules. ---------------------------------------------------------------------- Sieben · 2020-12-23 18:31 It's just an assumption that served my tests here I have to admit, and it also depends on whether the canvas comes 'pre-painted' with other widgetsets. But it could easily be replaced with eg clWhite if it proves wrong. Or another test for the brightness of clWindowText vs clHighLightText. ---------------------------------------------------------------------- winni · 2020-12-23 19:31 Hi! Don't rely on assumptions on system colors. Use the complement of the given color: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i function  Complement (col: TColor): TColor;beginResult := $FFFFFF - ColorToRGB(col);end; Only one trap: If your input is in the midrange gray the output will also be in the midrange gray. Winni ---------------------------------------------------------------------- Sieben · 2020-12-23 20:54 Thanks again, this would at least result in a readable font color, but still doesn't look seamless. clWhite would do a slightly better job in this case. But the main misapprehension I took, and I don't really know why right now, was that this early background painting is unique to this specific widgetset. Now, what assumptions can be made with this issue...? 1) Font color used for item painting will always be either clWindowText or clHighLighText...? 2) Background is already painted, ie odBackgroundPainted in ItemState...? 3) Background is painted in a 'dark' color while Canvas.Brush.Color, indicating the controls 'normal' color, ist not...? Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i     if DroppedDown    and IsDark(Canvas.Pixels[1,1])    and not IsDark(Canvas.Brush.Color)    and (odBackgroundPainted in ItemState) then    begin      if IsDark(clWindowText) then        Canvas.Font.Color := clHighLightText      else        Canvas.Font.Color := clWindowText;    end; Are there any more conditions besides 2) and 3) to not interfere with font color that can be tested for? ---------------------------------------------------------------------- winni · 2020-12-24 16:09 Hi Sieben! I can't test your code because I get an compiler error: bigimage1.pas(102,5) Error: Internal error 2014010312 It shows the line with the begin Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i {---------- --------------------------------------------------------------------  procedure TCustomComboBox.LMDrawListItem(var TheMessage : TLMDrawListItem);   Handler for custom drawing items. ---------------- --------------------------------------------------------------}procedu re TCustomComboBox.LMDrawListItem(var TheMessage : TLMDrawListItem);begin   // <=====  fpc 3.04  laz 2.0.10 gtk2 lin64 Winni ---------------------------------------------------------------------- Sieben · 2020-12-27 18:24 Quote from: winniI can't test your code because I get an compiler error: I'm afraid there is not much I can do about that... But to continue with this little saga - two or maybe three more conditions: 4) only non-selected items are concerned 5) happens only with certain Linux desktops 6) - unfortunately I'm not sure here - happens only with Gtk2 Is there anyone out there who can confirm this really is an issue restricted to Gtk2 only? And anyone knows why Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i {$IFDEF LCLGTK2}// code here...{$ENDIF} does not gray out the code in between in the editor but doesn't compile it either...? Edit: Sorry for my premature posting earlier this week - I was so absorbed by this graphics stuff and that I finally seemed to have a hold on that that I totally forgot on the conditions when to interfere with font color settings at all. ---------------------------------------------------------------------- wp · 2020-12-27 18:57 Quote from: Sieben on December 27, 2020, 07:24:14 pmCode: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i {$IFDEF LCLGTK2}  WriteLn(1+);   // Note the missing '1' at the end.{$ENDIF} Above snippet will not compile if you are on gtk2. It will compile, when you replace the WriteLn argument by (1+1). But note that gtk2 alone is not your issue because there are other Linux systems using gtk2 where your issue does not occur. You must also detect the type and version of your Linux system, eg. 'Ubuntu 16.04' - Linux specialists will be able to tell you how to do this from Lazarus. ---------------------------------------------------------------------- Sieben · 2020-12-27 20:33 Quote from: wpAbove snippet will not compile if you are on gtk2. It will compile, when you replace the WriteLn argument by (1+1). Hm, what's the rule behind that...? It seems I can't get any code to compile in this here context with said condition... Quote from: wpBut note that gtk2 alone is not your issue because there are other Linux systems using gtk2 where your issue does not occur. You must also detect the type and version of your Linux system, eg. 'Ubuntu 16.04' - Linux specialists will be able to tell you how to do this from Lazarus. What about 'SESSION=Ubuntu', 'XDG_CURRENT_DESKTOP= Unity' or 'UPSTART_JOB=unity7' (environment variables) or any sensible combination as a valid condition for this? Can you possibly confirm that any of these are true as well with the 18.04 Ubuntu you installed, but not for other linux installations? That said it would of course be most desirable to have a sufficient condition, but if it turns out there is none a number of necessary conditions must be sought that a) turn down the probability of interfering without necessity and b) even if so to not make any settings that hurt. For the latter it would help to know if condition 1) as mentioned earlier is true: font color for this will in any case either be clWindowText or clHighLightText, tertium non datur. ---------------------------------------------------------------------- wp · 2020-12-27 21:57 Quote from: Sieben on December 27, 2020, 09:33:08 pmQuote from: wpAbove snippet will not compile if you are on gtk2. It will compile, when you replace the WriteLn argument by (1+1). Hm, what's the rule behind that...? It seems I can't get any code to compile in this here context with said condition... The idea is that when you are on gtk2 the compiler sees the code within the{$IFDEF LCLGTK2}..{$ENDIF}. To test this you can put some faulty code in there, and the compilation will stop here. Quote from: Sieben on December 27, 2020, 09:33:08 pmWhat about 'SESSION=Ubuntu', 'XDG_CURRENT_DESKTOP= Unity' or 'UPSTART_JOB=unity7' (environment variables) or any sensible combination as a valid condition for this? Can you possibly confirm that any of these are true as well with the 18.04 Ubuntu you installed, but not for other linux installations? First of all: I am not a Linux man, so maybe this is far easier than what I am saying... Anyway, here is the excerpt of the env command in all my Linuxes: Ubuntu 16.04 (Combobox behaviour as you describe)   SESSION=ubuntu   XDG_CURRENT_DESKTOP=Unity   UPSTART_JOB=Unity7 Ubuntu 18.04 (Combobox behaviour as you describe)   GDMSESSION=ubuntu   XDG_CURRENT_DESKTOP=ubuntu:GNOME   (UPSTART_JOB ... not found) Ubuntu 20.10 (Combobox behaviour CORRECT! But LCL system colors are not extracted from theme correctly: In dark mode, clWindow is still bright, but the background of TCombobox or TEdit is dark -- what a mess!)   GDMSESSION=ubuntu   XDG_CURRENT_DESKTOP=ubuntu:GNOME Lubuntu 20.10 (correct, but I was not able to switch into some kind of "dark mode")   DESKTOP_SESSION=Lubuntu   XDG_CURRENT_DESTKOP=LXQt LMDE 4 Cinnamon (correct theme support)   GDMSESSION=cinnamon   DESKTOP_SESSION=cinnamon   XDG_CURRENT_DESKTOP=X-Cinnamon My impression is that theme support in the various Linuxes is much more multi-facetted and is not mapped on the LCL system colors. But as I said, I am not a Linux man... ---------------------------------------------------------------------- Sieben · 2020-12-27 23:16 Thanks a lot for your effort, I really appreciate that. Quote from: wpAnyway, here is the excerpt of the env command in all my Linuxes: Ok, I'm very close to giving up as well now... perhaps the best 'solution' to this issue is providing some info here and/or the wiki on how to deal with it should someone experience this on a target system. I think this: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i     {$IFDEF UNIX}          // fix for 'black on black' items    if DroppedDown       // with certain gtk2 unix desktops    and (odBackgroundPainted in ItemState)    and not (odSelected in ItemState)    and IsDark(Canvas.Pixels[1,1])    and not IsDark(Canvas.Brush.Color) then    begin      if IsDark(clWindowText) then        Canvas.Font.Color := clHighLightText      else        Canvas.Font.Color := clWindowText;  end;    {$ENDIF} is about as close as you can get with necessary conditions, but testing any and all linux brands seems almost impossible, even if you add Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i     and (GetDefaultLCLWidgetType = lpGtk2) which would require LCLPlatFormDef in uses of StdCtrls. Anyone with any additional help or ideas...? ---------------------------------------------------------------------- lucamar · 2020-12-28 00:17 Theme drawing in Linux is a mix-match of several layers heavily dependent on the system (or rather, the desktop) configuration: there is the base X layout, over it, the Desktop Engine and base toolkit (qt, gtk, etc.), the Window Manager, the Composition manager and, somewhere in between, the Theme engine,  which can modify nilly-willy almost any sub-set of capabilities of the layers below it. In these conditions it's almost inevitable that sometimes, for apparently no-reason at all, the theming of some group of applications (those compiled with the same or similar widget libraries) fails to reflect some end-user selections (even default ones). There's little one can do about it other than trying to recognize the situation and "correct it" somehow, as shown in the previous posts. Or expanding (bloating) your "widget" libs to cope with it. Or going your own way and ignoring the system (like wxWidgets does). Or ... Such is life in the Savage Wildlands of Linux, home of the Free and Open Sources.