[Okta Webinar] Learn how to a build a cloud-first strategyRegister Now

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 237
  • Last Modified:

Flickerless coloured text

I want to display a text (Tlabel) and part of letters must have one colour (e.g. blue) and part must have
another colour (e.g. red).

Example:
Situation 1: all letters are red
Situation 2: the leftmost letter is blue, the rest is red
Situation 3: the 2 most left letters are blue, the rest is red
Situation 4: all letters are blue.

About 20 times every second a letter must change its colour, WITHOUT any flickering.
So far, I have tried the following with and without
REPAINT, REDRAW.

I tried using 2 labels (the left label initially empty,
and then growing with 1 letter at the time, while the
second label initially is full, and looses 1 letter at
the time): FLICKERING occurs.

I tried using 1 label with a shape over it, colouring
the letters. The shape initially has a width of zero,
and grows the width of a letters every time:
FLICKERING occurs.

I tried using 1 label with up to ten shapes colouring the
letters (initially all shapes have a width of zero,
and then grow in turn with 1,2,3,4,..or 10 letters width): FLICKERING occurs).


Due to the target of the program: I can't use Canvas.Paint
or at least I rather wouldn't want to do this, because
the label can be anywhere on the screen, and be moved
around at runtime.

I'm totally out of idears.
0
Willem083097
Asked:
Willem083097
  • 3
1 Solution
 
icampbe1Commented:
I guess that the best approach would be to set csOpaque in the ControlStyle property.  You can further explore the use of invalidate on changes.  Finally, you can process the WM_ERASEBKGND message and return a non zero (although I'm not sure that descendants of TGraphicControl get that message).  

I believe that setting csOpaquw will do it though.  Hope this helps.

Ian C.

PS:  I think I will write a little component this afternoon to play with the idea.  If it works... its yours.

0
 
icampbe1Commented:
Ok, ignnore my previous comment.  here is a lable that I named TStateLabel (you can change its state ).  It is a TLabel with two new properties:  HiliteColor and HiliteState.

Hilite color is default Blue but you can change it with this property.
The default text color is Red, but you can change that (Font.Color).
The HiliteState property has the following values:
 hsNone - No hilited characters
 hsOne - the first character
 hsTwo - the first two characters
 hsAll - all hilited.

Just install it on your palette and drop it on your form.  You can set the properties in the IDE or from your program.  I assume that you will mostly want to change the HiliteState property.

NO FLICKER!!!

If I had your E-Mail I would send it that way... Oh well, here it is.

UNIT StateLabel;

INTERFACE

USES
 Windows, Messages, Classes, Controls, Graphics, StdCtrls;

TYPE
  THiliteState = ( hsNone, hsOne, hsTwo, hsAll );

  TStateLabel = CLASS( TLabel )
  PRIVATE
    FHiliteState: THiliteState;
    FHiliteFont:  TFont;
    PROCEDURE SetHiliteColor( AValue: TCOlor );
    FUNCTION  GetHiliteColor: TColor;
    PROCEDURE SetHiliteState( AValue: THiliteState );
    PROCEDURE CMFontChanged( VAR Message: TMessage ); Message CM_FONTCHANGED;
  PROTECTED
    PROCEDURE Paint;  Override;
  PUBLIC
    CONSTRUCTOR Create( AOwner: TComponent );  Override;
    DESTRUCTOR Destroy;  Override;
  PUBLISHED
    PROPERTY HiliteColor: TColor  read GetHiliteColor write SetHiliteColor;
    PROPERTY HiliteState: THiliteState  read FHiliteState write SetHiliteState;
  END;

PROCEDURE Register;


IMPLEMENTATION


CONSTRUCTOR TStateLabel.Create( AOwner: TComponent );
BEGIN
   Inherited Create( AOwner );
   FHiliteFont := TFont.Create;
   FHiliteFont.Assign(Font);
   FHiliteFont.Color := clBlue;

   Font.Color := clRed;
   FHiliteState := hsNone;
END;

DESTRUCTOR TStateLabel.Destroy;
BEGIN
   FHiliteFont.Free;
   Inherited Destroy;
END;

FUNCTION TStateLabel.GetHiliteColor: TColor;
BEGIN
   Result := FHiliteFont.Color;
END;

PROCEDURE TStateLabel.SetHiliteColor( AValue: TCOlor );
BEGIN
   IF FHiliteFont.Color <> AValue THEN BEGIN
      FHiliteFont.Color := AValue;
      Invalidate;
      END;
END;

PROCEDURE TStateLabel.CMFontChanged( VAR Message: TMessage );
VAR Temp: TColor;
BEGIN
   Inherited;
   Temp := FHiliteFont.Color;
   FHiliteFont.Assign(Font);
   FHiliteFont.Color := Temp;
   Invalidate;
END;

PROCEDURE TStateLabel.SetHiliteState( AValue: THiliteState );
BEGIN
   IF FHiliteState <> AValue THEN BEGIN
      FHiliteState := AValue;
      Invalidate;
      END;
END;

PROCEDURE TStateLabel.Paint;
VAR
  Rect: TRect;
  DrawStyle: Integer;
  Text: String;
BEGIN
   Inherited Paint;
   Text := GetLabelText;
   IF FHiliteState <> hsNone THEN
      WITH Canvas DO BEGIN
         Brush.Style := bsClear;
         Rect := ClientRect;
         DrawStyle := DT_EXPANDTABS;
         IF WordWrap THEN DrawStyle := DrawStyle OR DT_WORDBREAK;
         CASE Alignment OF
           taLeftJustify:  DrawStyle := DrawStyle OR DT_LEFT;
           taRightJustify: DrawStyle := DrawStyle OR DT_RIGHT;
           taCenter:       DrawStyle := DrawStyle OR DT_CENTER;
           END;
         IF Layout <> tlTop THEN BEGIN
            DrawText(Handle,PChar(Text),Length(Text), Rect, DrawStyle OR DT_CALCRECT);
            IF Layout = tlBottom THEN
               OffsetRect( Rect, 0, Height-Rect.Bottom )
            ELSE
               OffsetRect( Rect, 0, (Height-Rect.Bottom) DIV 2 );
            END;
         CASE FHiliteState OF
           hsOne:  Text := Copy( Text, 1, 1 );
           hsTwo:  Text := Copy( Text, 1, 2 );
           END;
         Canvas.Font := FHiliteFont;
         DrawText( Canvas.Handle, PChar(Text), Length(Text), Rect, DrawStyle );
         END;
END;


PROCEDURE Register;
BEGIN
  RegisterComponents('Samples', [TStateLabel]);
END;

end.


Have fun  :)

Ian C.
0
 
icampbe1Commented:
Hi again,

I did a little cleanup on the label.  It now works exactly as it should (including alignments of left, right, center, and enabled etc).  My E-Mail is iancamp@interlog.com, send me a note with your E-Mail address, and I mail it to you instead of posting it.  I've tested it thoroughly and it works just fine.

Ian C.

0
 
Willem083097Author Commented:
Superb! There are no other words to describe the fast answer
of Ian Campbel.

Using both Delphi 2.0 and since shortly Delphi 3.0, his component
gave a compiler error: stupid me: I was running it under
Delphi 2.0. By the time I found out the reason, Ian had already
send me an email including a 2.0 version.

Thanks a lot Ian!. Excellent answer!.
I will use your component as soon as I figure out how I
can use a integerparameter for setting the number of letters
to be coloured in stead of a predefined hsOne, hsTwo state-
definition (which is not very useful of course when
I want to change the colour of the 134th letter).

BTW: I already discovered that a (loosy) alternative approach is
to use a transparent Tlabel for every letter with a
caption ' x ' where x = the letter. Precalc the width of
solely the x and start the following letter there, minus
the leading space (necessary because e.g. the f and j can
share the same x-coordinates)
Before changing the color, the original width must be retained because the width of e.g. an f in Arial 100 points is wrongly returned when measered on another (invisible) label.
This is both fast and not flickering. It is also not
a very good approach.

Willem
0
 
ShakilCommented:
If u could write a code to color 134th Char only.

Shakil
0

Featured Post

Free Tool: Site Down Detector

Helpful to verify reports of your own downtime, or to double check a downed website you are trying to access.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

  • 3
Tackle projects and never again get stuck behind a technical roadblock.
Join Now