diff --git a/components/tachart/tachartaxis.pas b/components/tachart/tachartaxis.pas index 3db01abd32..163731bc53 100644 --- a/components/tachart/tachartaxis.pas +++ b/components/tachart/tachartaxis.pas @@ -116,11 +116,14 @@ type FRange: TChartRange; FTitle: TChartAxisTitle; FTransformations: TChartAxisTransformations; + FWrappedTitle: String; FZPosition: TChartDistance; function GetMarks: TChartAxisMarks; inline; + function GetRealTitle: String; function GetValue(AIndex: Integer): TChartValueText; inline; function GetValueCount: Integer; inline; + function IsWordwrappedTitle: Boolean; function PositionIsStored: Boolean; procedure SetAtDataOnly(AValue: Boolean); procedure SetAxisPen(AValue: TChartAxisPen); @@ -139,6 +142,7 @@ type procedure SetTitle(AValue: TChartAxisTitle); procedure SetTransformations(AValue: TChartAxisTransformations); procedure SetZPosition(AValue: TChartDistance); + procedure WordwrapTitle(ADrawer: IChartDrawer; AClipRect: TRect); protected function GetDisplayName: String; override; @@ -160,8 +164,8 @@ type function IsFlipped: Boolean; override; function IsPointInside(const APoint: TPoint): Boolean; function IsVertical: Boolean; inline; - procedure Measure( - const AExtent: TDoubleRect; var AMeasureData: TChartAxisGroup); + procedure Measure(const AExtent: TDoubleRect; const AClipRect: TRect; + var AMeasureData: TChartAxisGroup); function MeasureLabelSize(ADrawer: IChartDrawer): Integer; function PositionToCoord(const ARect: TRect): Integer; procedure PrepareHelper( @@ -235,8 +239,8 @@ type procedure Draw(ACurrentZ: Integer; var AIndex: Integer); function GetAxisByAlign(AAlign: TChartAxisAlignment): TChartAxis; function GetEnumerator: TChartAxisEnumerator; - function Measure( - const AExtent: TDoubleRect; ADepth: Integer): TChartAxisMargins; + function Measure(const AExtent: TDoubleRect; const AClipRect: TRect; + ADepth: Integer): TChartAxisMargins; procedure Prepare(ARect: TRect); procedure PrepareGroups; procedure SetAxisByAlign(AAlign: TChartAxisAlignment; AValue: TChartAxis); @@ -276,7 +280,8 @@ type implementation uses - LResources, Math, PropEdits, TAChartStrConsts, TAGeometry, TAMath; + LResources, Math, PropEdits, + TAChartStrConsts, TAGeometry, TAMath; var VIdentityTransform: TChartAxisTransformations; @@ -636,7 +641,7 @@ begin end; TPointBoolArr(p)[IsVertical] := FTitlePos; p += FHelper.FZOffset; - Title.DrawLabel(FHelper.FDrawer, p, p, Title.Caption, FTitlePolygon); + Title.DrawLabel(FHelper.FDrawer, p, p, GetRealTitle, FTitlePolygon); end; function TChartAxis.GetAlignment: TChartAxisAlignment; @@ -665,6 +670,14 @@ begin Result := TChartAxisMarks(inherited Marks); end{%H-}; // to silence the compiler warning of impossible inherited inside inline proc +function TChartAxis.GetRealTitle: String; +begin + if Title.Visible and IsWordwrappedTitle then + Result := FWrappedTitle + else + Result := Title.Caption; +end; + procedure TChartAxis.GetMarkValues; var i: Integer; @@ -763,6 +776,13 @@ begin Result := Alignment in [calLeft, calRight]; end; +function TChartAxis.IsWordwrappedTitle: Boolean; +begin + Result := Title.Wordwrap and ( + ((FAlignment in [calLeft, calRight]) and (abs(Title.LabelFont.Orientation) = 900)) or + ((FAlignment in [calTop, calBottom]) and (Title.LabelFont.Orientation = 0)) ); +end; + function TChartAxis.MakeValuesInRangeParams( AMin, AMax: Double): TValuesInRangeParams; begin @@ -779,7 +799,7 @@ begin end; procedure TChartAxis.Measure( - const AExtent: TDoubleRect; var AMeasureData: TChartAxisGroup); + const AExtent: TDoubleRect; const AClipRect: TRect; var AMeasureData: TChartAxisGroup); var v: Boolean; d: IChartDrawer; @@ -788,7 +808,7 @@ var begin if not Title.Visible or (Title.Caption = '') then exit(0); // Workaround for issue #19780, fix after upgrade to FPC 2.6. - with Title.MeasureLabel(d, Title.Caption) do + with Title.MeasureLabel(d, GetRealTitle) do Result := IfThen(v, cx, cy); if Title.DistanceToCenter then Result := Result div 2; @@ -856,11 +876,16 @@ begin maxi := i; end; end; + + if Title.Visible and IsWordwrappedTitle then + WordwrapTitle(d, AClipRect); + with AMeasureData do begin FSize := Max(sz, FSize); FTitleSize := Max(TitleSize, FTitleSize); FMargin := Max(Margin, FMargin); end; + if minc < MaxInt then begin UpdateFirstLast(minc, mini, rmin, rmax); UpdateFirstLast(maxc, maxi, rmin, rmax); @@ -1139,6 +1164,24 @@ begin end; } +procedure TChartAxis.WordwrapTitle(ADrawer: IChartDrawer; AClipRect: TRect); +var + w: Integer; +begin + if Alignment in [calLeft, calRight] then + w := AClipRect.Height + else if Alignment in [calBottom, calTop] then + w := AClipRect.Width + else + begin + FWrappedTitle := Title.Caption; + exit; + end; + ADrawer.Font := Title.LabelFont; + FWrappedTitle := TADrawUtils.WordWrap(Title.Caption, ADrawer, w, Title.TextFormat); +end; + + { TChartAxisList } function TChartAxisList.Add: TChartAxis; inline; @@ -1210,8 +1253,8 @@ begin AList.Sort(ACompare); end; -function TChartAxisList.Measure( - const AExtent: TDoubleRect; ADepth: Integer): TChartAxisMargins; +function TChartAxisList.Measure(const AExtent: TDoubleRect; + const AClipRect: TRect; ADepth: Integer): TChartAxisMargins; var g: ^TChartAxisGroup; @@ -1239,14 +1282,14 @@ begin for j := 0 to g^.FCount - 1 do begin axis := TChartAxis(FGroupOrder[ai]); try - axis.Measure(AExtent, g^); + axis.Measure(AExtent, AClipRect, g^); except axis.Visible := false; raise; end; ai += 1; end; - // Axises of the same group should have the same Alignment, Position and ZPosition. + // Axes of the same group should have the same Alignment, Position and ZPosition. if axis.IsDefaultPosition then Result[axis.Alignment] += Max(0, g^.FSize + g^.FTitleSize + g^.FMargin + diff --git a/components/tachart/tachartaxisutils.pas b/components/tachart/tachartaxisutils.pas index 15461ee105..b8593b660d 100644 --- a/components/tachart/tachartaxisutils.pas +++ b/components/tachart/tachartaxisutils.pas @@ -41,11 +41,13 @@ type strict private FCaption: String; FPositionOnMarks: Boolean; + FWordwrap: Boolean; function GetFont: TFont; procedure SetCaption(AValue: String); procedure SetFont(AValue: TFont); procedure SetPositionOnMarks(AValue: Boolean); + procedure SetWordwrap(AValue: Boolean); public constructor Create(AOwner: TCustomChart); @@ -60,6 +62,7 @@ type read FPositionOnMarks write SetPositionOnMarks default false; property TextFormat; property Visible default false; + property Wordwrap: Boolean read FWordwrap write SetWordwrap default false; end; ICoordTransformer = interface @@ -603,6 +606,14 @@ begin StyleChanged(Self); end; +procedure TChartAxisTitle.SetWordwrap(AValue: Boolean); +begin + if FWordwrap = AValue then exit; + FWordwrap := AValue; + StyleChanged(Self); +end; + + { TCustomChartAxisMarks } constructor TCustomChartAxisMarks.Create(AOwner: TCustomChart); diff --git a/components/tachart/tadrawutils.pas b/components/tachart/tadrawutils.pas index 5fb88bc9df..6a6c59a466 100644 --- a/components/tachart/tadrawutils.pas +++ b/components/tachart/tadrawutils.pas @@ -15,7 +15,7 @@ unit TADrawUtils; interface uses - Classes, FPCanvas, FPImage, Types, TAChartUtils; + SysUtils, Classes, FPCanvas, FPImage, Types, TAChartUtils; type // Same types as in Graphics unit, but without dependency. @@ -191,10 +191,14 @@ type function FPColorToChartColor(AFPColor: TFPColor): TChartColor; function ColorDef(AColor, ADefaultColor: TChartColor): TChartColor; inline; + function Wordwrap(const AText: String; ADrawer: IChartDrawer; + AMaxWidth: Integer; ATextFormat: TChartTextFormat): String; + + implementation uses - Math, fasthtmlparser, htmlutil, TAGeometry, TAHtml; + StrUtils, Math, fasthtmlparser, htmlutil, TAGeometry, TAHtml; const LINE_INTERVAL = 2; @@ -798,5 +802,65 @@ begin Result := TChartTextOut.Create(Self); end; +// Inserts LineEndings into the provided string AText such that its width +// does not exceed the given width. +function WordWrap(const AText: String; ADrawer: IChartDrawer; + AMaxWidth: Integer; ATextFormat: TChartTextFormat): string; +var + L: TStrings; + sa: TStringArray; + line, testline: String; + s: String; + w, ws, wspace: Integer; + i: Integer; +begin + Result := ''; + + if ATextFormat = tfNormal then + begin + wspace := ADrawer.TextExtent(' ').X; + L := TStringList.Create; + try + L.Text := AText; + for i := 0 to L.Count-1 do + begin + sa := SplitString(L[i], ' '); + line := ''; + w := 0; + for s in sa do + begin + ws := ADrawer.TextExtent(s).X; + if w + wspace + ws <= AMaxWidth then + begin + line := IfThen(line='', s, line + ' ' + s); + w := w + wspace + ws; + end else + begin + if line = '' then + begin + Result := IfThen(Result='', s, Result + LineEnding + s); + line := ''; + w := 0; + end else + begin + Result := IfThen(Result='', line, Result + LineEnding + line); + line := s; + w := ws; + end; + end; + end; + if line <> '' then + Result := IfThen(Result='', line, Result + LineEnding + line); + if i <> L.Count-1 then + Result := Result + LineEnding; + end; + finally + L.Free; + end; + end else + // ToDo: Implement wordwrap for html format + Result := AText; +end; + end. diff --git a/components/tachart/tagraph.pas b/components/tachart/tagraph.pas index db609f016d..432f3de00c 100644 --- a/components/tachart/tagraph.pas +++ b/components/tachart/tagraph.pas @@ -1521,7 +1521,7 @@ begin CalculateTransformationCoeffs(scSeriesMargins, scChartMargins, scMinDataSpace); cr := FClipRect; for tries := 1 to 10 do begin - axisMargin := AxisList.Measure(CurrentExtent, scDepth); + axisMargin := AxisList.Measure(CurrentExtent, FClipRect, scDepth); axisMargin[calLeft] := Max(axisMargin[calLeft], scDepth); axisMargin[calBottom] := Max(axisMargin[calBottom], scDepth); FClipRect := cr;