tvplanit: refactor handling of day/month/event rectangles in TVpGanttView.

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@8421 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2022-08-28 18:17:04 +00:00
parent 9d0e64788e
commit 31a3ed6a14
3 changed files with 386 additions and 126 deletions

View File

@ -13,6 +13,10 @@ type
private
FGanttView: TVpGanttView;
FDayFont: TFont;
FMonthFont: TFont;
FEventFont: TFont;
BevelHighlight: TColor;
BevelShadow: TColor;
BevelDarkShadow: TColor;
@ -22,14 +26,6 @@ type
RealLineColor: TColor;
RealRowHeadAttrColor: TColor;
FColHeadHeightTotal: Integer;
FColHeadRowHeightMonth: Integer;
FColHeadRowHeightDay: Integer;
FRowHeight: Integer;
FTextMargin: Integer;
function CountMonths(ADate1, ADate2: TDateTime): Integer;
protected
procedure Clear;
procedure DrawBorders;
@ -64,7 +60,6 @@ constructor TVpGanttViewPainter.Create(AGanttView: TVpGanttView;
begin
inherited Create(ARenderCanvas);
FGanttView := AGanttView;
FTextMargin := 2;
end;
procedure TVpGanttViewPainter.Clear;
@ -73,23 +68,6 @@ begin
RenderCanvas.FillRect(RenderIn);
end;
function TVpGanttViewPainter.CountMonths(ADate1, ADate2: TDateTime): Integer;
var
dt: TDateTime;
begin
if ADate1 > ADate2 then
exit;
// raise Exception.Create('[TVpGanttViewPainter.CountMonts] Dates not in order.');
Result := 0;
dt := ADate1;
while dt <= ADate2 do
begin
inc(Result);
dt := StartOfTheMonth(IncMonth(dt));
end;
end;
procedure TVpGanttViewPainter.DrawBorders;
var
R: TRect;
@ -129,7 +107,7 @@ end;
procedure TVpGanttViewPainter.DrawColHeader;
var
i: Integer;
i, n: Integer;
R, R1: TRect;
P: TPoint;
monthRec: TVpGanttMonthRec;
@ -141,7 +119,7 @@ begin
RenderCanvas.Brush.Color := RealColHeadAttrColor;
RenderCanvas.Pen.Color := RealLineColor;
R := Rect(RealLeft, RealTop, RealRight, FColHeadHeightTotal);
R := Rect(RealLeft, RealTop, RealRight, FGanttView.TotalColHeaderHeight);
TPSFillRect(RenderCanvas, Angle, RenderIn, R);
if FGanttView.DrawingStyle = ds3D then
@ -171,16 +149,23 @@ begin
TPSLineTo(RenderCanvas, Angle, RenderIn, RealRight, R.Bottom);
end;
dx := FGanttView.FixedColWidth - FGanttView.LeftCol * FGanttView.ColWidth;
// Offset due to scrolling
dx := FGanttView.LeftCol * FGanttView.ColWidth;
// Draw month rectangles and month captions
for i := 0 to High(TVpGanttViewOpener(FGanttView).FMonthRecords) do
RenderCanvas.Font.Assign(FMonthFont);
n := FGanttView.NumMonths;
for i := 0 to n-1 do
begin
monthRec := TVpGanttViewOpener(FGanttView).FMonthRecords[i];
monthRec := FGanttView.MonthRecords[i];
R := monthRec.Rect;
OffsetRect(R, dx , 0);
OffsetRect(R, -dx , 0);
// Clip at fixed col edge
if R.Left < FGanttView.FixedColWidth then
R.Left := FGanttView.FixedColWidth;
// Draw month box
if FGanttView.DrawingStyle = ds3D then
begin
R1 := R;
@ -199,31 +184,37 @@ begin
TPSLineTo(RenderCanvas, Angle, RenderIn, R.Right, R.Bottom);
end;
// Paint month name. Use short format if space is too small for long format.
str := FormatDateTime(FGanttView.MonthFormat, monthRec.Date);
strLen := RenderCanvas.TextWidth(str);
if strLen > R.Width - 2 * FTextMargin then
if strLen > R.Width - 2 * FGanttView.TextMargin then
begin
str := FormatDateTime(FGanttView.MonthFormat_short, monthRec.Date);
strLen := RenderCanvas.TextWidth(str);
end;
if strLen > R.Width - 2 * FTextMargin then
if strLen > R.Width - 2 * FGanttView.TextMargin then
str := '';
if str <> '' then
begin
P := Point((R.Left + R.Right - strLen) div 2, R.Top + FTextMargin);
P := Point((R.Left + R.Right - strLen) div 2, R.Top + FGanttView.TextMargin);
TPSTextOut(RenderCanvas, Angle, RenderIn, P.X, P.Y, str);
end;
end;
// Draw day dividing lines and day captions. Always at right side.
RenderCanvas.Font.Assign(FGanttView.ColHeaderAttributes.DayFont);
// Draw day captions (always centered) and dividing lines (always at right side).
RenderCanvas.Font.Assign(FDayFont);
strH := RenderCanvas.TextHeight('Tg');
for dayRec in TVpGanttViewOpener(FGanttView).FDayRecords do
n := FGanttView.NumDays;
for i := 0 to n - 1 do
begin
dayRec := FGanttView.DayRecords[i];
R := dayRec.Rect;
OffsetRect(R, dx, 0);
OffsetRect(R, -dx, 0);
if R.Left < FGanttView.FixedColWidth then
Continue;
// No dividing line at last day of month because it already has been
// drawn as the month divider.
if (DayOf(dayRec.Date) <> DaysInMonth(dayRec.Date)) then
begin
if FGanttView.DrawingStyle = ds3D then
@ -240,6 +231,8 @@ begin
TPSLineTo(RenderCanvas, Angle, RenderIn, R.Right, R.Bottom);
end;
end;
// Paint day name
str := FormatDateTime(FGanttView.DayFormat, dayRec.Date);
strLen := RenderCanvas.TextWidth(str);
P := Point((R.Left + R.Right - strLen) div 2, (R.Top + R.Bottom - strH) div 2);
@ -256,21 +249,25 @@ var
R: TRect;
dx, dy: Integer;
begin
dx := FGanttView.FixedColWidth - FGanttView.LeftCol * FGanttView.ColWidth;
dy := FColHeadHeightTotal - FGanttView.TopRow * FRowHeight;
for i := 0 to High(TVpGanttViewOpener(FGanttView).FEventRecords) do
dx := FGanttView.LeftCol * FGanttView.ColWidth;
dy := FGanttView.TopRow * FGanttView.RowHeight;
RenderCanvas.Font.Assign(FEventFont);
for i := 0 to FGanttView.NumEvents-1 do
begin
eventRec := TVpGanttViewOpener(FGanttView).FEventRecords[i];
eventRec := FGanttView.EventRecords[i];
event := eventRec.Event;
R := eventRec.EventRect;
OffsetRect(R, -dx, -dy);
inc(R.Top, 2);
dec(R.Bottom, 1); // 1 less than top due to grid linewidth.
if R.Top < FGanttView.TotalColHeaderHeight then
Continue;
if R.Left < FGanttView.FixedColWidth then
Continue;
cat := FGanttView.DataStore.CategoryColorMap.GetCategory(event.Category);
RenderCanvas.Pen.Color := cat.Color;
RenderCanvas.Brush.Color := cat.BackgroundColor;
R := eventRec.EventRect;
if R.Left = R.Right then R.Right := R.Left + 1;
OffsetRect(R, dx, dy);
InflateRect(R, 0, -2);
if (R.Top < FColHeadHeightTotal) or (R.Left < FGanttView.FixedColWidth) then
Continue;
TPSRectangle(RenderCanvas, Angle, RenderIn, R);
end;
end;
@ -278,53 +275,54 @@ end;
procedure TVpGanttViewPainter.DrawGrid;
var
x1, x2, y0, y1, y2: Integer;
i: Integer;
dx, dy: Integer;
i, n, numEvents: Integer;
eventRec: TVpGanttEventRec;
dayRec: TVpGanttDayRec;
monthRec: TVpGanttMonthRec;
numDays, numMonths, numEvents: Integer;
begin
RenderCanvas.Pen.Color := RealLineColor;
numDays := Length(TVpGanttViewOpener(FGanttView).FDayRecords);
numMonths := Length(TVpGanttViewOpener(FGanttView).FMonthRecords);
numEvents := Length(TVpGanttViewOpener(FGanttView).FEventRecords);
// Horizontal lines
x1 := RealLeft + FGanttView.FixedColWidth;
if numMonths > 0 then
dx := FGanttView.LeftCol * FGanttView.ColWidth;
n := FGanttView.NumMonths;
if n > 0 then
begin
monthRec := TVpGanttViewOpener(FGanttView).FMonthRecords[numMonths-1];
x2 := monthRec.Rect.Right + FGanttView.FixedColWidth;
monthRec := FGanttView.MonthRecords[n-1];
x2 := monthRec.Rect.Right - dx;
end else
x2 := RealRight;
y0 := FColHeadHeightTotal;
y0 := FGanttView.TotalColHeaderHeight;
if FGanttView.DrawingStyle = ds3D then dec(y0);
RenderCanvas.Line(x1, y0, x2, y0);
for i := 0 to numEvents-1 do
numEvents := FGanttView.NumEvents;
y0 := 0;
for i := 0 to numEvents - 1 do
begin
eventRec := TVpGanttViewOpener(FGanttView).FEventRecords[i];
eventRec := FGanttView.EventRecords[i];
y1 := y0 + eventRec.EventRect.Bottom;
RenderCanvas.Line(x1, y1, x2, y1);
end;
// Vertical lines
y1 := RealTop + FColHeadHeightTotal;
y1 := RealTop + FGanttView.TotalColHeaderHeight;
dy := FGanttView.TopRow * FGanttView.RowHeight;
if numEvents > 0 then
begin
eventRec := TVpGanttViewOpener(FGanttView).FEventRecords[numEvents-1];
y2 := eventRec.EventRect.Bottom + FColHeadHeightTotal;
eventRec := FGanttView.EventRecords[numEvents-1];
y2 := eventRec.EventRect.Bottom - dy;
end else
y2 := RealBottom;
for i := 0 to numDays-1 do
n := FGanttView.NumDays;
for i := 0 to n-1 do
begin
dayRec := TVpGanttViewOpener(FGanttView).FDayRecords[i];
x1 := dayRec.Rect.Right + FGanttView.FixedColWidth;
dayRec := FGanttView.DayRecords[i];
x1 := dayRec.Rect.Right - dx;
x2 := x1;
RenderCanvas.Line(x1, y1, x2, y2);
if x1 >= FGanttView.FixedColWidth then
RenderCanvas.Line(x1, y1, x2, y2);
end;
end;
@ -336,13 +334,14 @@ var
str: String;
i: Integer;
dy: Integer;
eventRec: TVpGanttEventRec;
begin
RenderCanvas.Brush.Color := RealRowHeadAttrColor;
if FGanttView.DrawingStyle = ds3d then begin
R.Left := RealLeft + 1;
R.Top := RealTop + FColHeadHeightTotal;
R.Right := RealLeft + FGanttView.FixedColWidth-1;
R.Top := RealTop;
R.Right := RealLeft + FGanttView.FixedColWidth - 1;
R.Bottom := RealBottom - 1;
TPSFillRect(RenderCanvas, Angle, RenderIn, R);
DrawBevelRect(
@ -352,22 +351,26 @@ begin
BevelShadow
);
end else begin
R := Rect(RealLeft, RealTop + FColHeadHeightTotal + 1, RealLeft + FGanttView.FixedColWidth, RealBottom);
R := Rect(RealLeft, RealTop + 1, RealLeft + FGanttView.FixedColWidth, RealBottom);
TPSFillRect(RenderCanvas, Angle, RenderIn, R);
RenderCanvas.Pen.Color := RealLineColor;
RenderCanvas.Line(R.Right, R.Top, R.Right, R.Bottom);
end;
RenderCanvas.Font.Assign(FGanttView.RowHeaderAttributes.EventFont);
RenderCanvas.Font.Assign(FEventFont);
strH := RenderCanvas.TextHeight('Tg');
RenderCanvas.Pen.Color := RealLineColor;
dy := FColHeadHeightTotal - FGanttView.TopRow * FRowHeight;
for i := 0 to High(TVpGanttViewOpener(FGanttView).FEventRecords) do
// Offset due to scrolling
dy := FGanttView.TopRow * FGanttView.RowHeight;
for i := 0 to FGanttView.NumEvents-1 do
begin
str := TVpGanttViewOpener(FGanttView).FEventRecords[i].Caption;
R := TVpGanttViewOpener(FGanttView).FEventRecords[i].HeadRect;
OffsetRect(R, 0, dy);
if R.Top < FColHeadHeightTotal then
eventRec := FGanttView.EventRecords[i];
str := eventRec.Caption;
R := eventRec.HeadRect;
OffsetRect(R, 0, -dy);
if R.Top < FGanttView.TotalColHeaderHeight then
Continue;
if FGanttView.DrawingStyle = ds3D then
begin
@ -382,7 +385,7 @@ begin
RenderCanvas.Line(R.Left, R.Bottom, R.Right, R.Bottom);
// Paint event description as header
P := Point(R.Left + FTextMargin, (R.Top + R.Bottom - strH) div 2);
P := Point(R.Left + FGanttView.TextMargin, (R.Top + R.Bottom - strH) div 2);
TPSTextOut(RenderCanvas, Angle, RenderIn, P.X, P.Y, Str);
end;
end;
@ -417,6 +420,10 @@ begin
RealColor := ColorToRGB(FGanttView.Color);
RealLineColor := ColorToRGB(FGanttView.LineColor);
end;
FDayFont := FGanttView.ColHeaderAttributes.DayFont;
FMonthFont := FGanttView.ColHeaderAttributes.MonthFont;
FEventFont := FGanttView.RowHeaderAttributes.EventFont;
end;
procedure TVpGanttViewPainter.RenderToCanvas(ARenderIn: TRect;
@ -443,8 +450,8 @@ begin
SetMeasurements;
{ Draw headers }
DrawColHeader;
DrawRowHeader;
DrawColHeader;
{ Draw grid }
DrawGrid;
@ -481,21 +488,8 @@ var
begin
inherited;
// Height of the event rows
RenderCanvas.Font.Assign(FGanttView.RowHeaderAttributes.EventFont);
FRowHeight := RenderCanvas.TextHeight('Tg') + 2 * FTextMargin;
// Height of the month part in the column header
RenderCanvas.Font.Assign(FGanttView.ColHeaderAttributes.MonthFont);
FColHeadRowHeightMonth := RenderCanvas.TextHeight('Tg') + FTextMargin;
// Height of the day part in the column header
RenderCanvas.Font.Assign(FGanttView.ColHeaderAttributes.DayFont);
FColHeadRowHeightDay := RenderCanvas.TextHeight('Tg') + 2 * FTextMargin;
// total height of header: month row + day row
FColHeadHeightTotal := FColHeadRowHeightMonth + FColHeadRowHeightDay;
FGanttView.Init;
(*
// Determine range of dates as well as the rectangles containing the day,
// column and event headers and the event data.
if (FGanttView.Datastore = nil) or (FGanttView.Datastore.Resource = nil) or
@ -561,7 +555,7 @@ begin
end;
x1 := round((t1 - FGanttView.StartDate) / numDays * totalWidth);
x2 := round((t2 - FGanttView.StartDate) / numDays * totalWidth);
y2 := y1 + FRowHeight;
y2 := y1 + FGanttView.RowHeight;
with TVpGanttViewOpener(FGanttView) do
begin
FEventRecords[i].Event := event;
@ -575,8 +569,8 @@ begin
// Populate day records
SetLength(TVpGanttViewOpener(FGanttView).FDayRecords, numDays);
x1 := 0; // Scrollable dimension
y1 := RealTop + FColHeadRowHeightMonth;
y2 := RealTop + FColHeadHeightTotal;
y1 := RealTop + FGanttView.MonthColHeaderHeight;
y2 := RealTop + FGanttView.TotalColHeaderHeight;
dt := trunc(FGanttView.StartDate);
for i := 0 to numDays-1 do
begin
@ -598,7 +592,7 @@ begin
dt := firstMonth;
x1 := 0; // Scrollable dimension;
y1 := RealTop;
y2 := RealTop + FColHeadHeightTotal;
y2 := RealTop + FGanttView.TotalColHeaderHeight;
for i := 0 to numMonths - 1do
begin
numDays := DaysInMonth(dt);
@ -617,12 +611,10 @@ begin
dt := IncMonth(dt, 1);
x1 := x2;
end;
*)
FGanttView.VisibleCols := (RealRight - RealLeft + FGanttView.FixedColWidth) div FGanttView.ColWidth;
FGanttView.VisibleRows := (RealBottom - RealTop + FColHeadHeightTotal) div FRowHeight;
FGanttView.RowCount := eventCount;
FGanttView.ColCount := numdays;
end;
FGanttView.VisibleCols := (RealRight - RealLeft - FGanttView.FixedColWidth) div FGanttView.ColWidth;
FGanttView.VisibleRows := (RealBottom - RealTop - FGanttView.TotalColHeaderHeight) div FGanttView.RowHeight;
end;
end.