{%MainUnit ../stdctrls.pp} {****************************************************************************** TCustomLabel ****************************************************************************** ***************************************************************************** * * * This file is part of the Lazarus Component Library (LCL) * * * * See the file COPYING.modifiedLGPL.txt, included in this distribution, * * for details about the copyright. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * * ***************************************************************************** TODO: - Enable Tabbing/Focusing to focus FocusControl - Enable Escaped '&' Shortcut to focus FocusControl - Compare/Match AutoSize to Delphi/Kylix's - ?? Check For Full Delphi/Kylix Compatibility } procedure TCustomLabel.CalculatePreferredSize( var PreferredWidth, PreferredHeight: integer; WithThemeSpace: Boolean); // assumes: (Parent <> nil) and Parent.HandleAllocated var AWidth: Integer; begin if (Parent = nil) or (not Parent.HandleAllocated) then Exit; if WidthIsAnchored and WordWrap then AWidth:=Width else AWidth:=10000; CalculateSize(AWidth,PreferredWidth,PreferredHeight); end; procedure TCustomLabel.CalculateSize(MaxWidth: integer; var NeededWidth, NeededHeight: integer); var DC: HDC; R: TRect; OldFont: HGDIOBJ; Flags: cardinal; LabelText: String; begin LabelText := GetLabelText; if LabelText='' then begin NeededWidth:=1; NeededHeight:=1; exit; end; DC := GetDC(Parent.Handle); try R := Rect(0, 0, MaxWidth, 10000); OldFont := SelectObject(DC, HGDIOBJ(Font.Reference.Handle)); Flags := DT_CALCRECT or DT_EXPANDTABS; if WordWrap then Flags := Flags or DT_WORDBREAK else if not HasMultiLine then Flags := Flags or DT_SINGLELINE; if not ShowAccelChar then Flags := Flags or DT_NOPREFIX; DrawText(DC, PChar(LabelText), Length(LabelText), R, Flags); SelectObject(DC, OldFont); // add one to be able to display disabled label NeededWidth := R.Right - R.Left + 1; NeededHeight := R.Bottom - R.Top + 1; //DebugLn(['TCustomLabel.CalculatePreferredSize ',DbgSName(Self),' R=',dbgs(R),' MaxWidth=',MaxWidth,' DT_WORDBREAK=',(DT_WORDBREAK and Flags)>0,' LabelText="',LabelText,'"']); finally ReleaseDC(Parent.Handle, DC); end; end; procedure TCustomLabel.FontChanged(Sender: TObject); begin inherited FontChanged(Sender); UpdateSize; end; class function TCustomLabel.GetControlClassDefaultSize: TSize; begin Result.CX := 65; Result.CY := 17; end; function TCustomLabel.HasMultiLine: boolean; var s: String; begin s := GetLabelText; Result := (pos(#10, s) > 0) or (pos(#13, s) > 0); end; procedure TCustomLabel.DoAutoSize; begin inherited DoAutoSize; //debugln('TCustomLabel.DoAutoSize ',DbgSName(Self),' AutoSizing=',dbgs(AutoSizing),' AutoSize=',dbgs(AutoSize),' Parent=',DbgSName(Parent),' csLoading=',dbgs(csLoading in ComponentState),' Parnet.HandleAllocated=',dbgs((Parent<>nil) and (Parent.HandleAllocated))); end; procedure TCustomLabel.SetAlignment(Value : TAlignment); begin //debugln('TCustomLabel.SetAlignment Old=',dbgs(ord(Alignment)),' New=',dbgs(ord(Value)),' csLoading=',dbgs(csLoading in ComponentState)); if FAlignment <> Value then begin FAlignment := Value; Invalidate; end; end; procedure TCustomLabel.Notification(AComponent : TComponent; Operation : TOperation); begin inherited Notification(AComponent, Operation); if (AComponent = FFocusControl) and (Operation = opRemove) then FFocusControl := nil; end; procedure TCustomLabel.SetFocusControl(Value : TWinControl); begin if Value <> FFocusControl then begin if FFocusControl <> nil then FFocusControl.RemoveFreeNotification(Self); FFocusControl:= Value; if Value <> nil then Value.FreeNotification(Self); end; end; procedure TCustomLabel.WMActivate(var Message: TLMActivate); begin if (FFocusControl <> nil) and (FFocusControl.CanFocus) then FFocusControl.SetFocus; end; function TCustomLabel.GetLabelText: string; begin Result := Caption; end; procedure TCustomLabel.SetShowAccelChar(Value : Boolean); begin if FShowAccelChar <> Value then begin FShowAccelChar := Value; Invalidate; UpdateSize; end; end; procedure TCustomLabel.TextChanged; begin Invalidate; UpdateSize; end; procedure TCustomLabel.DoSetBounds(ALeft, ATop, AWidth, AHeight: integer); var WidthChanged: Boolean; begin WidthChanged:=AWidth<>Width; inherited DoSetBounds(ALeft, ATop, AWidth, AHeight); if OptimalFill and (not AutoSize) then AdjustFontForOptimalFill; if WidthChanged and WordWrap then begin InvalidatePreferredSize; AdjustSize; end; end; function TCustomLabel.CanTab: boolean; begin Result := False; end; procedure TCustomLabel.DoMeasureTextPosition(var TextTop: integer; var TextLeft: integer); var lTextHeight: integer; lTextWidth: integer; begin TextLeft := 0; if Layout = tlTop then begin TextTop := 0; end else begin CalculateSize(Width, lTextWidth, lTextHeight); case Layout of tlCenter: TextTop := (Height - lTextHeight) div 2; tlBottom: TextTop := Height - lTextHeight; end; end; end; constructor TCustomLabel.Create(TheOwner: TComponent); begin inherited Create(TheOwner); ControlStyle := [csSetCaption, csClickEvents, csDoubleClicks, csReplicatable]; with GetControlClassDefaultSize do SetInitialBounds(0, 0, CX, CY); FShowAccelChar := True; FInternalSetBounds := False; Color := clNone; AutoSize := True; end; function TCustomLabel.GetTransparent: boolean; begin Result := Color = clNone; end; procedure TCustomLabel.SetColor(NewColor: TColor); begin inherited; // if color = clnone then transparent, so not opaque if NewColor = clNone then ControlStyle := ControlStyle - [csOpaque] else ControlStyle := ControlStyle + [csOpaque]; end; {------------------------------------------------------------------------------ Method: TCustomLabel.SetLayout Params: None Returns: Nothing ------------------------------------------------------------------------------} procedure TCustomLabel.SetLayout(Value: TTextLayout); begin if FLayout <> Value then begin FLayout:= Value; Invalidate; end; end; procedure TCustomLabel.SetTransparent(NewTransparent: boolean); begin if Transparent = NewTransparent then exit; if NewTransparent then Color := clNone else if Color = clNone then Color := clBackground; end; {------------------------------------------------------------------------------ Method: TCustomLabel.SetWordWrap Params: None Returns: Nothing ------------------------------------------------------------------------------} procedure TCustomLabel.SetWordWrap(Value: Boolean); begin if FWordWrap <> Value then begin FWordWrap := Value; Invalidate; UpdateSize; end; end; function TCustomLabel.DialogChar(var Message: TLMKey): boolean; begin Result := False; if not FShowAccelChar then exit; if FFocusControl = nil then exit; if IsAccel(Message.CharCode, GetLabelText) and FFocusControl.CanFocus then begin Result := True; FFocusControl.SetFocus; end else Result := inherited DialogChar(Message); end; procedure TCustomLabel.Loaded; begin inherited Loaded; AdjustSize; end; procedure TCustomLabel.UpdateSize; begin InvalidatePreferredSize; if OptimalFill and (not AutoSize) then AdjustFontForOptimalFill; AdjustSize; end; {------------------------------------------------------------------------------- function TCustomLabel.CalcFittingFontHeight(const TheText: string; MaxWidth, MaxHeight: Integer; var FontSize, NeededWidth, NeededHeight: integer): Boolean; Calculates the maximum font size for TheText to fit into MaxWidth and MaxHeight. -------------------------------------------------------------------------------} function TCustomLabel.CalcFittingFontHeight(const TheText: string; MaxWidth, MaxHeight: Integer; var FontHeight, NeededWidth, NeededHeight: integer): Boolean; var R : TRect; DC : hDC; Flags: Cardinal; OldFont: HGDIOBJ; MinFontHeight: Integer; MaxFontHeight: Integer; AFont: TFont; CurFontHeight: LongInt; begin Result:=false; if AutoSizeDelayed or (TheText='') or (MaxWidth<1) or (MaxHeight<1) then exit; AFont:=TFont.Create; AFont.Assign(Font); CurFontHeight:=AFont.Height; MinFontHeight:=5; MaxFontHeight:=MaxHeight*2; if (CurFontHeightMaxFontHeight) then CurFontHeight:=(MinFontHeight+MaxFontHeight) div 2; Flags := DT_CALCRECT or DT_NOPREFIX or DT_EXPANDTABS; if WordWrap then inc(Flags, DT_WORDBREAK); // give a clipping rectangle with space, so that the bounds returned by // DrawText can be bigger and we know, the tried font is too big. R := Rect(0,0, MaxWidth, MaxHeight*2); DC := GetDC(Parent.Handle); try while (MinFontHeight<=MaxFontHeight) and (CurFontHeight>=MinFontHeight) and (CurFontHeight<=MaxFontHeight) do begin AFont.Height:=CurFontHeight; // NOTE: some TFont do not allow any integer //debugln('TCustomLabel.CalcFittingFontHeight A ',dbgs(MinFontHeight),'<=',dbgs(AFont.Height),'<=',dbgs(MaxFontHeight)); OldFont := SelectObject(DC, HGDIOBJ(AFont.Reference.Handle)); DrawText(DC, PChar(TheText), Length(TheText), R, Flags); SelectObject(DC, OldFont); NeededWidth := R.Right - R.Left; NeededHeight := R.Bottom - R.Top; //debugln('TCustomLabel.CalcFittingFontHeight B NeededWidth=',dbgs(NeededWidth),' NeededHeight=',dbgs(NeededHeight),' MaxWidth=',dbgs(MaxWidth),' MaxHeight=',dbgs(MaxHeight)); if (NeededWidth>0) and (NeededHeight>0) and (NeededWidth<=MaxWidth) and (NeededHeight<=MaxHeight) then begin // TheText fits into the bounds if (not Result) or (FontHeight try bigger (binary search) CurFontHeight:=(MaxFontHeight+CurFontHeight+1) div 2; // +1 to round up if CurFontHeight=MinFontHeight then break; end else begin // TheText does not fit into the bounds MaxFontHeight:=CurFontHeight-1; // -> try smaller (binary search) CurFontHeight:=(MinFontHeight+CurFontHeight) div 2; end; end finally ReleaseDC(Parent.Handle, DC); AFont.Free; end; end; function TCustomLabel.ColorIsStored: boolean; begin Result := (Color <> clNone); end; {------------------------------------------------------------------------------- function TCustomLabel.AdjustFontForOptimalFill: Boolean; Maximizes Font.Height Return true if Font.Height changed. -------------------------------------------------------------------------------} function TCustomLabel.AdjustFontForOptimalFill: Boolean; var NeededWidth: Integer; NeededHeight: Integer; NewFontHeight: Integer; OldFontHeight: LongInt; begin Result := False; if not CalcFittingFontHeight(GetLabelText, Width, Height, NewFontHeight, NeededWidth, NeededHeight) then Exit; if Font.Height = NewFontHeight then Exit; //debugln('TCustomLabel.AdjustFontForOptimalFill OldFontHeight=',dbgs(Font.Height),' NewFontHeight=',dbgs(NewFontHeight)); OldFontHeight := Font.Height; Font.Height := NewFontHeight; Result := OldFontHeight <> Font.Height; end; procedure TCustomLabel.Paint; var TR : TTextStyle; R : TRect; TextLeft, TextTop: integer; LabelText: string; OldFontColor: TColor; begin R := Rect(0,0,Width,Height); with Canvas do begin if Enabled then Brush.Color := Self.Color else Brush.Color := clNone; Font := Self.Font; if (Color<>clNone) and not Transparent then begin Brush.Style:=bsSolid; FillRect(R); end else Brush.Style:=bsClear; { If BorderStyle <> sbsNone then begin InflateRect(R,-2,-2); Pen.Style := psSolid; If BorderStyle = sbsSunken then Pen.Color := clBtnShadow else Pen.Color := clBtnHighlight; MoveTo(0, 0); LineTo(Width - 1,0); MoveTo(0, 0); LineTo(0,Height - 1); If BorderStyle = sbsSunken then Pen.Color := clBtnHighlight else Pen.Color := clBtnShadow; MoveTo(0,Height - 1); LineTo(Width - 1,Height - 1); MoveTo(Width - 1, 0); LineTo(Width - 1,Height); end; } //Brush.Color:=clRed; //FillRect(R); FillChar(TR,SizeOf(TR),0); with TR do begin Alignment := BidiFlipAlignment(Self.Alignment, UseRightToLeftAlignment); WordBreak := wordWrap; SingleLine:= not WordWrap and not HasMultiLine; Clipping := True; ShowPrefix := ShowAccelChar; SystemFont := False; RightToLeft := UseRightToLeftReading; ExpandTabs := True; end; DoMeasureTextPosition(TextTop, TextLeft); //debugln('TCustomLabel.Paint ',dbgs(Alignment=tacenter),' ',dbgs(Layout=tlCenter),' ',dbgs(TextLeft),' TextTop=',dbgs(TextTop),' ',dbgs(R)); LabelText := GetLabelText; OldFontColor := Font.Color; if not Enabled then begin Font.Color := clBtnHighlight; TextRect(R, TextLeft + 1, TextTop + 1, LabelText, TR); Font.Color := clBtnShadow; end; TextRect(R, TextLeft, TextTop, LabelText, TR); Font.Color := OldFontColor; end; end; procedure TCustomLabel.SetBounds(aLeft, aTop, aWidth, aHeight: integer); begin if (Left=aLeft) and (Top=aTop) and (Width=aWidth) and (Height=aHeight) then exit; if not FInternalSetBounds and AutoSize and WordWrap then InvalidatePreferredSize; inherited SetBounds(aLeft, aTop, aWidth, aHeight); end; procedure TCustomLabel.SetOptimalFill(const AValue: Boolean); begin if FOptimalFill = AValue then Exit; FOptimalFill := AValue; if OptimalFill and AutoSize then AutoSize := False; Invalidate; end; class procedure TCustomLabel.WSRegisterClass; begin inherited WSRegisterClass; RegisterCustomLabel; end; // included by stdctrls.pp