TSpeedButton with TAction - Auto-generated disabled image only considers black
Asked Answered
F

1

7

First of all, I already know that when you are implementing a TSpeedButton, when you assign its Glyph, if you do not have more than one glyph, then NumGlyphs should be 1, and when it's disabled, it will automatically use the same image to auto-generate a disabled version of the glyph.

However, in my situation, I am assigning an action to this speed button. The TActionManager has both the Images and DisabledImages pointed to the very same TImageList. I've also tested without any DisabledImages assigned at all, as well as creating a copy of the images using an all-black mask, and assigning that via the DisabledImages.

The glyph shows just fine (based on the image set up on the action) when it's enabled. But when it's disabled, only pure black colors in the images will be used for disabled images. Anything which has any sort of color besides pure black, even slightly off-black, is completely ignored, and not included when rendering the disabled image.

Here's a comparison between a test enabled image and the auto-generated disabled image. The image is just a 16x16 bitmap with some vertical lines. Starting from the far right, a completely black line, followed by slightly lighter lines to the left of it.

Comparing actions enabled and disabled

As you can see, when disabled, it only considers the completely black line. The next line over is only very slightly lighter than complete black.

I cannot assign the Glyph property myself, because it will be overwritten by the action which is assigned to it. I also cannot set up the two-glyph-wide images on the image list either, because these images are widely used in many other places which have no concept of multiple glyphs. I also do not want to use all-black for my images.

How do I get a disabled TSpeedButton (as result of disabled TAction) to show a disabled (grayed out) image when it has an action assigned to it?

NOTE: The same happens with the TBitBtn, or otherwise any control which relies on a Glyph.


u44169002.pas

unit u44169002;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.Buttons, System.ImageList,
  Vcl.ImgList, System.Actions, Vcl.ActnList, Vcl.PlatformDefaultStyleActnCtrls, Vcl.ActnMan, Vcl.StdCtrls;

type
  TForm1 = class(TForm)
    Actions: TActionManager;
    actTest: TAction;
    ImageList: TImageList;
    SpeedButton1: TSpeedButton;
    BitBtn1: TBitBtn;
    procedure actTestExecute(Sender: TObject);
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.actTestExecute(Sender: TObject);
begin
  Self.actTest.Enabled:= False;
end;

end.

u44169002.dfm

object Form1: TForm1
  Left = 0
  Top = 0
  Caption = 'Form1'
  ClientHeight = 231
  ClientWidth = 405
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  PixelsPerInch = 96
  TextHeight = 13
  object SpeedButton1: TSpeedButton
    Left = 24
    Top = 24
    Width = 105
    Height = 33
    Action = actTest
    Flat = True
  end
  object BitBtn1: TBitBtn
    Left = 160
    Top = 24
    Width = 105
    Height = 33
    Action = actTest
    Caption = 'Test Action'
    TabOrder = 0
  end
  object Actions: TActionManager
    DisabledImages = ImageList
    Images = ImageList
    Left = 24
    Top = 128
    StyleName = 'Platform Default'
    object actTest: TAction
      Caption = 'Test Action'
      Hint = 'This is just a test action'
      ImageIndex = 0
      OnExecute = actTestExecute
    end
  end
  object ImageList: TImageList
    Left = 24
    Top = 72
    Bitmap = {
      494C010101000800440010001000FFFFFFFFFF10FFFFFFFFFFFFFFFF424D3600
      0000000000003600000028000000400000001000000001002000000000000010
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      00000000000000000000000000000000000082828200000000006B6B6B000000
      0000555555000000000041414100000000002D2D2D00000000001D1D1D000000
      00000C0C0C000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      00000000000000000000000000000000000082828200000000006B6B6B000000
      0000555555000000000041414100000000002D2D2D00000000001D1D1D000000
      00000C0C0C000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      00000000000000000000000000000000000082828200000000006B6B6B000000
      0000555555000000000041414100000000002D2D2D00000000001D1D1D000000
      00000C0C0C000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      00000000000000000000000000000000000082828200000000006B6B6B000000
      0000555555000000000041414100000000002D2D2D00000000001D1D1D000000
      00000C0C0C000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      00000000000000000000000000000000000082828200000000006B6B6B000000
      0000555555000000000041414100000000002D2D2D00000000001D1D1D000000
      00000C0C0C000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      00000000000000000000000000000000000082828200000000006B6B6B000000
      0000555555000000000041414100000000002D2D2D00000000001D1D1D000000
      00000C0C0C000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      00000000000000000000000000000000000082828200000000006B6B6B000000
      0000555555000000000041414100000000002D2D2D00000000001D1D1D000000
      00000C0C0C000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      00000000000000000000000000000000000082828200000000006B6B6B000000
      0000555555000000000041414100000000002D2D2D00000000001D1D1D000000
      00000C0C0C000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      00000000000000000000000000000000000082828200000000006B6B6B000000
      0000555555000000000041414100000000002D2D2D00000000001D1D1D000000
      00000C0C0C000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      00000000000000000000000000000000000082828200000000006B6B6B000000
      0000555555000000000041414100000000002D2D2D00000000001D1D1D000000
      00000C0C0C000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      00000000000000000000000000000000000082828200000000006B6B6B000000
      0000555555000000000041414100000000002D2D2D00000000001D1D1D000000
      00000C0C0C000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      00000000000000000000000000000000000082828200000000006B6B6B000000
      0000555555000000000041414100000000002D2D2D00000000001D1D1D000000
      00000C0C0C000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      00000000000000000000000000000000000082828200000000006B6B6B000000
      0000555555000000000041414100000000002D2D2D00000000001D1D1D000000
      00000C0C0C000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      00000000000000000000000000000000000082828200000000006B6B6B000000
      0000555555000000000041414100000000002D2D2D00000000001D1D1D000000
      00000C0C0C000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      000000000000000000000000000000000000424D3E000000000000003E000000
      2800000040000000100000000100010000000000800000000000000000000000
      000000000000000000000000FFFFFF00FFFF0000000000005555000000000000
      5555000000000000555500000000000055550000000000005555000000000000
      5555000000000000555500000000000055550000000000005555000000000000
      5555000000000000555500000000000055550000000000005555000000000000
      5555000000000000FFFF00000000000000000000000000000000000000000000
      000000000000}
  end
end

For the sake of example, here's the test bitmap I used:

Test Bitmap


In fact, in the Vcl.Buttons unit, in the function TButtonGlyph.CreateButtonGlyph, the following code seems to explicitly consider only the black areas:

{ Create a disabled version }
with MonoBmp do
begin
  Assign(FOriginal);
  HandleType := bmDDB;
  Canvas.Brush.Color := clBlack;
  Width := IWidth;
  if Monochrome then
  begin
    Canvas.Font.Color := clWhite;
    Monochrome := False;
    Canvas.Brush.Color := clWhite;
  end;
  Monochrome := True;
end;
with TmpImage.Canvas do
begin
  Brush.Color := clBtnFace;
  FillRect(IRect);
  Brush.Color := clBtnHighlight;
  SetTextColor(Handle, clBlack);
  SetBkColor(Handle, clWhite);
  BitBlt(Handle, 1, 1, IWidth, IHeight,
    MonoBmp.Canvas.Handle, 0, 0, ROP_DSPDxax);
  Brush.Color := clBtnShadow;
  SetTextColor(Handle, clBlack);
  SetBkColor(Handle, clWhite);
  BitBlt(Handle, 0, 0, IWidth, IHeight,
    MonoBmp.Canvas.Handle, 0, 0, ROP_DSPDxax);
end;
Finicking answered 24/5, 2017 at 21:46 Comment(28)
It's just that the disabled image generation algorithm is not good enough (TButtonGlyph.CreateButtonGlyph in buttons). Assign a dark image and you'll notice a faint gray disabled image.Wordless
@SertacAkyuz Just tried, using a completely black circle, on a completely white background, and Flat turned on. I still see nothing when disabled.Finicking
I traced with XE2, perhaps it's been changed since then, tried with blue arrows of glyfx.Wordless
@SertacAkyuz Apparently, my "completely black" wasn't black enough. It was more like charcoal (using one I already had). Now I am really using 100% black, and now I can see it. Not a single one of my icons will have any complete black in them though... I may be goth, but I'm not THAT goth :-/Finicking
@SertacAkyuz Edited my question to point this out.Finicking
You're mistaken, BTW. If you supply only one glyph, and the button is disabled, it does not use the same glyph. It creates a new glyph based on a mask of the normal one. In this case, the problem is that your glyph uses all one color, and masking negates that color and leaves you with basically a blank image. You can verify this by using a different color (a block in the middle of your existing glyph that is red, for instance), and you'll note that block appears as a faint gray section.Gamy
@Ken "It creates a new glyph based on a mask of the normal one." Is that not "using the same glyph"? Obviously I don't mean it "displays" the same glyph. I said it "uses" it. "the problem is that your glyph uses all one color" Where do you see the same color? I'm clearly describing how each line is a different color. I even include a complete sample demonstrating exactly this.Finicking
@Ken The problem is that the auto-generated disabled glyph only considers the completely black areas of the original image, and ignores everything else.Finicking
I've got many existing glyphs (and in image lists) that contain a mix of black and other colors, and the masking logic works fine with them. It's only when the majority (or all) of your image contains black that there's an issue. (For instance, I've got a glyph that's a red circle outlined outside and inside with black (like an O) with the inside filled with white and containing black text, and the default disabled image works fine. I've got others that contain predominantly black that don't, and have to have either a custom disabled image assigned or be custom drawn as needed)Gamy
@Ken But do you have an action assigned to them? That is the root of my issue... I'm not assigning the glyphs myself. They're at the mercy of the action assigned to it, and the image associated with that action. I had this problem in a production project, so I made this test project to prove it. None of my typical glyphs have any black in them at all. And they all show completely transparent. Please see what I've added to the end of my question.Finicking
I don't have that issue, unless the only glyph is almost (or is completely) in the same color, like the one you've shown. Most of the images in my imagelists work fine with actions, without using two separate image lists. For the ones that don't, I usually just throw an extra image to represent the disabled state, and programmatically change the image index in the action's update event based on the state.Gamy
@Ken The same happens with every image I have in my image lists. Originally, they were all transparent when disabled, because none of my images have any 100% black in them. Almost all of them are multi-color. They render nothing for the disabled version. Unless there's any 100% black in it, then just that black area gets copied to the disabled glyph. Nothing else.Finicking
@Ken Please keep in mind, when I say "100% black", I don't mean everything in the image is black. I mean $000000.Finicking
Yeah, I got that. :-) I don't have this issue at all with actions and imagelists, and I have about 200 images in the common imagelist for an app I work on nearly every day (and I also don't have the issue in the other dozen or so apps for which I'm responsible).Gamy
The more I study this, the more I realize that the solution might just be to implement a separate Disabled image list, using all-black versions of every glyph I have in the main list. And that means a different version for each size, as well. What a pain.Finicking
You can override the TImageList.DoDraw. This might help (or even a duplicate): #6003518 and this: #3117521Different
Generating disabled images is pretty easier. You can do the whole thing in code at runtime.Slavin
Coming back to my prior comment, no, providing a Disabled image list to the action manager makes no difference. It still seems to base its disabled version off of the main image, ignoring my disabled image list. @Different While that may be the right solution, both of those links are very different. They are talking about "ugly" disabled images in the main menu (which is actually what I want). I'm talking about a button glyph auto-created by an action only using black. So no, not a duplicate.Finicking
And I always get at least that one down-vote from my hater on almost every question I ask.Finicking
@David I'm not sure how I would go about that, since see my comment after yours. It ignores the "disabled" image list assigned to the action manager. As I explain in my question, I can't assign the glyphs myself because the action manager will overwrite them, and I can't use double-wide icons in the image lists either, because they're used elsewhere which does not have this issue. So as of now, I don't even have a work-around besides trying to implement TImageList.DoDraw like kobik suggests.Finicking
You could stop using speed buttons. Or you could have different lists for different purposes. I hope you don't build your image lists in .dfm files?Slavin
Even if the disabled image list was working, I'd still have unwanted results... A) If pre-loading the image list in design-time, my EXE size would be larger with hundreds of images, or B) If creating disabled image list on startup, then startup would be slower. I'd rather the built-in on-the-fly rendering be corrected.Finicking
@David The problem is also with the TBitBtn, or anything with a glyph. I don't see what loading images in the .dfm or not would be of any difference regarding my problem. It persists either way.Finicking
Well because if you load them at runtime, you can have one with double width and one with single width. And it's just better because you can keep your resources in separate files and visible in the source repo. You can choose icon sizes to match DPI, etc. As soon as I started supporting DPI scaling I switch away from image lists in the ,dfm file.Slavin
@David I already considered that and explained in my question, the same image lists are automatically used in other places. I don't have control over that. The image lists are assigned to the action manager. The action manager then uses those assigned images in various other places. So if I made these image lists double-wide, then I'd fix one place but screw up other places.Finicking
Yeah, that's action managers for you. Take away control. Action lists let you have the control back.Slavin
I'm at a loss trying to implement a work-around. TButtonGlyph is not exposed in the interface, thus cannot be overridden. And TImageList.DoDraw has a parameter Enabled which even when I override it and explicitly pass True all the time, it still draws nothing. Internally, I make the same call to ImageList_DrawEx for disabled as for enabled, but the Enabled always comes true anyway, it never even comes False in my debugging. So I have no idea how I can override this drawing.Finicking
It looks like my final work-around is to NOT assign actions to these buttons, but instead implement an OnClick event handler which calls the action. This way, I can assign the glyphs myself. On top of that, it also means that I need to manually enable/disable the buttons whenever the action's enabled state changes.Finicking
S
0

Put the speedbutton on a groupbox and disable the group box you do not need disabled glyphs and still only 1 image the image will be there but the button still clearly dis-abeled

Subjectivism answered 15/4, 2020 at 2:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.