Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Auto Wrapping long text within a custom label control?

Status
Not open for further replies.

djjd47130

Programmer
Nov 1, 2010
480
0
0
US
As mentioned in a previous thread post of mine, I am building a custom label which I do my own custom drawing in, rather than the inherited text drawing. It's specifically a 3-D style which I'm doing, but that's besides the point. The fact is I am custom drawing the text into the canvas.

Now my dilemma is figuring out how to mimic these two properties of the label: Alignment and WordWrap.

Supposedly you're able to identify the width of a given text/font before you draw it, which is exactly what I need to be able to do this, but I haven't figured it out yet. I can't simply count the number of characters, because they can vary in size, especially across different fonts. I need to know how wide a given text/font will be before I draw it.

With this ability, I will then need to identify word by word what's the most I can put in one line of text. I should then draw what will fit on the first line, then move on to the next line, and so on until there is no text left to draw.

It's a subject that has me very puzzled, since it requires a lot of match, which I'm horrible at (bad subject to not know well being a programmer).

Basically, I have to try to mimic all the functionality of the inherited label manually.

ss-TJDLabel.png




JD Solutions
 
Haha dyslexic today... "requires a lot of *Math... " (not Match)

JD Solutions
 
Looking at the third code block on the last post I made should be useful, as it applies the same idea that is required for your question.

thread102-1647066

It is not possible for anyone to acknowledge truth when their salary depends on them not doing it.
 
TCanvas.TextWidth is what you use to measure the length of a text. For wordwrap, find each of the spaces and then call that function to see if it is close to the length of the canvas.

It is not possible for anyone to acknowledge truth when their salary depends on them not doing it.
 
Beautiful. I got the Layout property working now. Next is the Alignment, and then finally WordWrap... I also have to catch if a single word is larger than it can fit, I have to add a " - " and split the word.

JD Solutions
 
For Alignment, there is a little math involved. Left justification is simple. As for the other two, we start by assuming the word is less in length than the canvas you are drawing on (certain things will obviously have to be adjusted otherwise). To right justify, you subtract the width of the text from your canvas size and then draw your text there. To center, you subtract the width of the text from the canvas size and then divide by 2.

It is not possible for anyone to acknowledge truth when their salary depends on them not doing it.
 
Thanks, already got the Alignment. WordWrap is friggen hard though, with the way I'm doing it, I keep either losing the last word of the sentence or the last entire line of the display. I'll play with it some more and get back here if I can't figure it out.

JD Solutions
 
Now I have at least the very last word showing properly, except that the very last word isn't accounted for in the width comparison, therefore if it's too large to fit on the last line, it doesn't wrap down. Ugg, I'll keep fighting it :p

JD Solutions
 
Here's a preview of what it looks like now:

ss-TJDLabel-Wrap.png


Notice the last word "wrapping" is not constrained inside the control. It should go down to the next line, but isn't. Here's the component I'm building...

Note the procedure 'TJDLabel.Refresh' which is the main drawing method.

Unit: JDCtrls
Component: TJDLabel
Displays 3-D label with special properties to control style
Code:
unit JDCtrls;

////////////////////////////////////////////////////////////////////////////////
//  JDCtrls
//  Components:
//    TJDLabel - Custom 3D label
////////////////////////////////////////////////////////////////////////////////

interface

uses
  Classes, Controls, SysUtils, Windows, StdCtrls, ExtCtrls, Graphics, StrUtils;

type
  TJDLabel = class;

  //Customized bevels for the 3D text, not for the control's border
  TIOBevel = (ioRaised, ioLowered, ioNone);
  TIOBevelStyle = (isUpper, isLower, isBoth);

  TJDLabel = class(TCustomLabel)
  private
    fLst: TStringList;
    fBmp: TBitmap;
    fStrength: Integer;
    fDistance: Integer;
    fIndentLeft: Integer;
    fIndentTop: Integer;
    fBevel: TIOBevel;
    fBevelStyle: TIOBevelStyle;
    fCaption: String;
    procedure SetStrength(Value: Integer);
    procedure SetDistance(Value: Integer);
    procedure SetIndentLeft(Value: Integer);
    procedure SetIndentTop(Value: Integer);
    procedure SetBevel(Value: TIOBevel);
    procedure SetBevelStyle(Value: TIOBevelStyle);
    procedure SetCaption(Value: String);
  protected
    property AutoSize;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override; 
    procedure Paint; override;
    procedure Refresh;
  published
    //Custom properties
    property Distance: Integer read fDistance write SetDistance;
    property Strength: Integer read fStrength write SetStrength;
    property IndentLeft: Integer read fIndentLeft write SetIndentLeft;
    property IndentTop: Integer read fIndentTop write SetIndentTop;
    property Bevel: TIOBevel read fBevel write SetBevel;  //Text, not border
    property BevelStyle: TIOBevelStyle read fBevelStyle write SetBevelStyle;
    property Caption: String read fCaption write SetCaption; //Override of original
    //These properties need special attention to mimic their functionality
    property Alignment; 
    property Font;
    property Layout;      
    property ShowAccelChar;
    property Transparent;
    property WordWrap;
    //Remaining inherited properties
    property Align;
    property Anchors;
    property BiDiMode;
    property Color nodefault;
    property Constraints;
    property DragCursor;
    property DragKind;
    property DragMode;
    property Enabled;
    property FocusControl;
    property ParentBiDiMode;
    property ParentColor;
    property ParentFont;
    property ParentShowHint;
    property PopupMenu;
    property ShowHint;
    property Visible;
    property OnClick;
    property OnContextPopup;
    property OnDblClick;
    property OnDragDrop;
    property OnDragOver;
    property OnEndDock;
    property OnEndDrag;
    property OnMouseDown;
    property OnMouseMove;
    property OnMouseUp;
    property OnMouseEnter;
    property OnMouseLeave;
    property OnStartDock;
    property OnStartDrag;
  end;

function IntRange(const Value, Min, Max: Integer): Integer;

procedure Register;

////////////////////////////////////////////////////////////////////////////////
implementation                              
////////////////////////////////////////////////////////////////////////////////

procedure Register;
begin
  RegisterComponents('JD Custom', [TJDLabel]);
end;
                                      
////////////////////////////////////////////////////////////////////////////////
//                             Misc. Methods
////////////////////////////////////////////////////////////////////////////////

//Keeps an integer within a given range of possible values
function IntRange(const Value, Min, Max: Integer): Integer;
begin
  Result:= Value;
  if Value < Min then Result:= Min;
  if Value > Max then Result:= Max;
end;

////////////////////////////////////////////////////////////////////////////////
//                                TJDLabel
////////////////////////////////////////////////////////////////////////////////

constructor TJDLabel.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  fLst:= TStringList.Create;
  fCaption:= inherited Caption;
  inherited Caption:= '';
  Self.Parent:= TWinControl(AOwner);
  fBmp:= TBitmap.Create;
  fBevel:= ioLowered;
  fBevelStyle:= isBoth;
  fIndentLeft:= 8;
  fIndentTop:= 8;
  Distance:= 3;
  Strength:= 100;
  Font.Style:= Font.Style + [fsBold];
  Font.Size:= 20;
  Font.Color:= clNavy;
  Color:= $00A40000;
  AutoSize:= False;
  Height:= 49;
  Width:= 209;
end;

destructor TJDLabel.Destroy;
begin
  if assigned(fBmp) then fBmp.Free;
  if assigned(fLst) then fLst.Free;
  inherited Destroy;
end;

procedure TJDLabel.Paint;
begin
  inherited Paint;
  Self.Refresh;
end;

procedure TJDLabel.Refresh;
var
  R,G,B : Integer;
  TR, TG, TB: Integer;
  X, Y, S, D: Integer;
  TW, TH, TT: Integer;
  Tmp, TS: String;
  TTop, TLeft: Integer;
begin
  if (assigned(fBmp)) and (assigned(fLst)) then begin
    if inherited Caption <> '' then
      inherited Caption:= '';
    fBmp.Canvas.Font.Assign(Font); 
    if fBmp.Width <> Width then
      fBmp.Width:= Width;
    if fBmp.Height <> Height then
      fBmp.Height:= Height;    
    fBmp.Transparent:= Transparent;
    Self.Canvas.Brush.Color:= Color;
    fBmp.Canvas.Brush.Color:= Color;
    fBmp.Canvas.FillRect(Rect(0, 0, Width, Height));
    if Transparent then begin
      fBmp.TransparentMode:= tmFixed;
      fBmp.TransparentColor:= Color;
    end;
    fBmp.Canvas.Brush.Style:= bsClear;
    TW:= Canvas.TextWidth(Caption) + (fDistance div 2);
    TH:= Canvas.TextHeight(Caption) + (fDistance div 2);
    TT:= 0;
    if Self.Layout = tlTop then begin
      TT:= fIndentTop;
    end else if Self.Layout = tlBottom then begin
      TT:= Height - TH - fIndentTop;
    end else begin
      TT:= (Height div 2) - (TH div 2) + fIndentTop;
    end;
    R:= (Color and $ff);
    G:= ((Color and $ff00) shr 8);
    B:= ((Color and $ff0000) shr 16);
    fLst.Clear;
    Tmp:= Caption + ' ';
    TS:= '';
    TTop:= 0;
    fLst.Delimiter:= Char(' ');
    fLst.DelimitedText:= Tmp;
    for Y:= 0 to fLst.Count-1 do begin
      if Y = fLst.Count-1 then
        TS:= TS + fLst[Y]+' ';
      if (fBmp.Canvas.TextWidth(TS+fLst[Y]) >= fBmp.Width-(fIndentLeft*2))
        or (Y = fLst.Count) then
      begin
        if Alignment = taLeftJustify then
          TLeft:= fIndentLeft
        else if Alignment = taRightJustify then
          TLeft:= Width - fBmp.Canvas.TextWidth(TS) - fIndentLeft
        else
          TLeft:= (Width div 2) - (Canvas.TextWidth(TS) div 2);
        if fBevel <> ioNone then begin
          for X:= 1 to fDistance do begin
            S:= Trunc((X / fDistance) * fStrength);
            if (fBevel = ioRaised) then
              S:= S * -1;
            D:= fDistance - X;
            if (fBevelStyle in [isLower, isBoth]) then begin
              TR:= IntRange(R + S, 0, 255);
              TG:= IntRange(G + S, 0, 255);
              TB:= IntRange(B + S, 0, 255);
              fBmp.Canvas.Font.Color:= RGB(TR, TG, TB);
              fBmp.Canvas.TextOut(TLeft + D, (TTop) + D, TS);
            end;
            if (fBevelStyle in [isUpper, isBoth]) then begin
              TR:= IntRange(R - S, 0, 255);
              TG:= IntRange(G - S, 0, 255);
              TB:= IntRange(B - S, 0, 255);
              fBmp.Canvas.Font.Color:= RGB(TR, TG, TB);
              fBmp.Canvas.TextOut(TLeft - D, (TTop) - D, TS);
            end;
          end;
        end;   
        fBmp.Canvas.Font.Color:= Font.Color;
        fBmp.Canvas.TextOut(TLeft, (TTop), TS);
        TTop:= TTop + fBmp.Canvas.TextHeight(TS) + 5;
        TS:= '';
      end;    
      TS:= TS + fLst[Y] + ' ';
    end;
    Canvas.Draw(0, 0, fBmp);
  end;
end;

procedure TJDLabel.SetStrength(Value: Integer);
begin
  fStrength:= IntRange(Value, 0, 255);
  Refresh;
end;

procedure TJDLabel.SetDistance(Value: Integer);
begin
  fDistance:= IntRange(Value, 0, 255);
  Refresh;
end;

procedure TJDLabel.SetIndentLeft(Value: Integer);
begin
  fIndentLeft:= IntRange(Value, 0, 500);
  Refresh;
end;

procedure TJDLabel.SetIndentTop(Value: Integer);
begin
  fIndentTop:= IntRange(Value, 0, 500);
  Refresh;
end;
      
procedure TJDLabel.SetBevel(Value: TIOBevel);
begin
  fBevel:= Value;
  Refresh;
end;
           
procedure TJDLabel.SetBevelStyle(Value: TIOBevelStyle);
begin
  fBevelStyle:= Value;
  Refresh;
end;

procedure TJDLabel.SetCaption(Value: String);
begin
  fCaption:= Value;
  Refresh;
end;

end.

A nice code cleanup or tip/trick would be nice :p



JD Solutions
 
On that matter, I actually am still losing the very last line of text. Wtf, this Refresh method is too complicated :(

JD Solutions
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top