3

I am targeting to draw a custom animated progress bar in VST

My goal is drawing a similar result as image below, I tried to do something like this OnBeforeCellPaint:

procedure TForm2.VTs1BeforeCellPaint(Sender: TBaseVirtualTree;
  TargetCanvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex;
  CellPaintMode: TVTCellPaintMode; CellRect: TRect; var ContentRect: TRect);
var
  NewRect : TRect;
  xOff, yOff : Integer;
  ProgressBarRect: TRect;
  Percents: Real;
  DrawProgressBar: Boolean;
begin
  //draw progress
  Percents := 10; // 40%
  // progressBar on Column 3
  begin
  // draw progressbar
    ProgressBarRect.Left := 0;
    ProgressBarRect.Top := CellRect.Top + 1;
    ProgressBarRect.Right := round((CellRect.Right - CellRect.Left) * Percents)  + CellRect.Left;
    ProgressBarRect.Bottom := CellRect.Bottom - 1;
    if (ProgressBarRect.Right - ProgressBarRect.Left) > 0 then
    begin
      TargetCanvas.Brush.Color := RGB(179,255,102);
      TargetCanvas.FillRect(ProgressBarRect);
    end;
  // ProgressBarRect
    inc(ProgressBarRect.Left);
    inc(ProgressBarRect.Top);
    dec(ProgressBarRect.Right);
    dec(ProgressBarRect.Bottom);
    if (ProgressBarRect.Right - ProgressBarRect.Left) > 0 then
    begin
      TargetCanvas.Brush.Color := RGB(221,255,187);
      TargetCanvas.FillRect(ProgressBarRect);
    end;
  end; 
end;

but I can't do the same result and reach the same approach as the image which follows:

enter image description here

That's the result I've got in coding:

enter image description here

The progress bars are coming along to the node not beside it and its not same design as showing in the image it comes yellow long back ground of the node I wanted to make it in the left side of the node and have the same design of the animated image that I've posted above.

Ian Boyd
  • 246,734
  • 253
  • 869
  • 1,219
Vlark.Lopin
  • 762
  • 10
  • 21
  • *"but i cant do the same result and reach the same approach of the image"* Why not? what is different, what doesn't work and in what way? – Tom Brunberg Aug 02 '16 at 09:54
  • added result image in question – Vlark.Lopin Aug 02 '16 at 10:15
  • This is before cell paint, so the actual cell paint may overwrite what you are doing. So, do you not need to modify the var ContentRect? – Dsm Aug 02 '16 at 10:17
  • i create new rect to show the progress bar beside that left of the node name also the design comes out one shape not like the image at all. i am trying to do same result of the animated gif image – Vlark.Lopin Aug 02 '16 at 10:19
  • But your rect width is a percentage of cell width. Surely it should be a percentage cell height, and a fixed width? – Dsm Aug 02 '16 at 10:22
  • percent it to allow the VSt on validate to animate the rect it could be 40 / 10 / 20 , or i am doing all wrong – Vlark.Lopin Aug 02 '16 at 10:24
  • I have no idea what you mean with the added image! Where is the VST? Where is the drawn progressbar? Please, try to formulate your problem verbally as good as you can, until then I must downvote, because your question is unclear. – Tom Brunberg Aug 02 '16 at 10:28
  • i mean the animated image that i posted in the question . the drawn progressbar is the yellow one in the second image of VST – Vlark.Lopin Aug 02 '16 at 10:29
  • @TomBrunberg Question edited with more details – Vlark.Lopin Aug 02 '16 at 10:31
  • I repeat, you are doing a percentage of the cell WIDTH, not the cell HEIGHT, which is what you need. Also you need to make your cell height bigger. – Dsm Aug 02 '16 at 13:02
  • @Dsm i dont know how to do this in coding can you answer the question with how to do it right ? i will appreciate it – Vlark.Lopin Aug 02 '16 at 13:04
  • my confusion is here `round((CellRect.Right - CellRect.Left)` should it be from bottom to top ? because i test that and the progress disappear – Vlark.Lopin Aug 02 '16 at 13:06
  • See my added answer. Don't forget that x% is really x/100 so that might be why the progress disappears – Dsm Aug 02 '16 at 13:13
  • I've reverted your edit: the question was answered based on the further details you've provided – fantaghirocco Aug 02 '16 at 15:13

2 Answers2

2

OnBeforeCellPaint triggers only once, before the cell is painted.

I've used a timer to repaint the VST in order to "animate" the rect.

Notice that Percents is a decimal, not a percentage value, so 100% is 1.

A very basic demo follows:

private
  Percents: Real;

. . .

implementation

procedure TForm2.FormCreate(Sender: TObject);
begin
  Percents := 0;
  VirtualStringTree1.AddChild(nil);
end;

procedure TForm2.Timer1Timer(Sender: TObject);
begin
  if Percents > 1 then
    Percents := 0
  else
    Percents := Percents + 0.025;

  VirtualStringTree1.Repaint;
end;

procedure TForm2.VirtualStringTree1BeforeCellPaint(Sender: TBaseVirtualTree;
  TargetCanvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex;
  CellPaintMode: TVTCellPaintMode; CellRect: TRect; var ContentRect: TRect);
const
  CPROGBARWIDTH = 30;//rect width
  CPROGBARSTEPS = 6;//how many rect is 100%
var
  r: TRect;
  h, n: Integer;
begin
  if Percents > 1 then
    Percents := 1
  else if Percents = 0 then
    Exit;//nothing to draw

  h := Round(CellRect.Height / CPROGBARSTEPS) - 1;

  r.Top := CellRect.bottom - h - 1;
  r.Left := 1;{align left}
  //r.Left := CellRect.Right - CPROGBARWIDTH - 1;{align right}
  r.Width := CPROGBARWIDTH;

  TargetCanvas.Brush.Color := clSkyBlue;

  n := Ceil(Percents * CPROGBARSTEPS);//how many rect to draw?

  while n > 0 do begin
    r.Height := h;
    TargetCanvas.FillRect(r);
    Dec(r.Top, 1 + h);
    Dec(n);
  end;
end;

August Holidays Bonus AKA "100% non-animated rect on the left side of the animated one"

This draws something similar to the animated GIF in the question.

Here a nested routine is used.

procedure drawProgress(AWidth: Integer; APercent: Real; ASteps: Integer; ALeft: Integer = 1);

AWidth the rectangle width
APercent the progress percentage
ASteps number of chunks which make the full 100% progress
ALeft horizontal coordinate of the upper-left corner point of the rectangle

procedure TForm2.VirtualStringTree1BeforeCellPaint(Sender: TBaseVirtualTree;
  TargetCanvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex;
  CellPaintMode: TVTCellPaintMode; CellRect: TRect; var ContentRect: TRect);

  procedure drawProgress(AWidth: Integer; APercent: Real; ASteps: Integer; ALeft: Integer = 1);
  var
    r: TRect;
    h, n: Integer;
  begin
    if APercent > 1 then
      APercent := 1
    else if APercent = 0 then
      Exit;//nothing to draw

    h := Round(CellRect.Height / ASteps) - 1;

    r.Top := CellRect.bottom - h - 1;
    r.Left := ALeft;
    r.Width := AWidth;

    TargetCanvas.Brush.Color := clSkyBlue;

    n := Ceil(APercent * ASteps);//how many rect to draw?

    while n > 0 do begin
      r.Height := h;
      TargetCanvas.FillRect(r);
      Dec(r.Top, 1 + h);
      Dec(n);
    end;
  end;

begin
  drawProgress(10,        1, 7);
  drawProgress(30, Percents, 7, 1 + 10 + 1);
end;
fantaghirocco
  • 4,761
  • 6
  • 38
  • 48
1

First you need to make your cell (row) height bigger. I assume you know how to do that.

Next, as I said in my comments you are using the wrong direction for calculations:

Finally you do not divide your percentage by 100 as you should!

procedure TForm2.VTs1BeforeCellPaint(Sender: TBaseVirtualTree;
  TargetCanvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex;
  CellPaintMode: TVTCellPaintMode; CellRect: TRect; var ContentRect: TRect);
var
  NewRect : TRect;
  xOff, yOff : Integer;
  ProgressBarRect: TRect;
  Percents: Real;
  DrawProgressBar: Boolean;
begin
  //draw progress
  // Percents := 10; // 40% // for testing?
  // progressBar on Column 3
  begin
  // draw progressbar
    ProgressBarRect.Left := 0;
    ProgressBarRect.Top := round((CellRect.Top - CellRect.Bottom) * Percents/100)  + CellRect.Bottom;
    ProgressBarRect.Right := 30;
    ProgressBarRect.Bottom := CellRect.Bottom - 1;
    if (ProgressBarRect.Top - ProgressBarRect.Bottom) > 0 then
    begin
      TargetCanvas.Brush.Color := RGB(179,255,102);
      TargetCanvas.FillRect(ProgressBarRect);
    end;
  // ProgressBarRect
    inc(ProgressBarRect.Left);
    inc(ProgressBarRect.Top);
    dec(ProgressBarRect.Right);
    dec(ProgressBarRect.Bottom);
    if (ProgressBarRect.Top - ProgressBarRect.Bottom) > 0 then
    begin
      TargetCanvas.Brush.Color := RGB(221,255,187);
      TargetCanvas.FillRect(ProgressBarRect);
    end;
  end; 
end;

Edit Note

updated to remove test line that fixes size at 10%

Dsm
  • 5,870
  • 20
  • 24
  • I assumed this was just for test. You need to remove the line Percents := 10. Will edit. – Dsm Aug 02 '16 at 13:22
  • How do you animate? I assume refresh via timer? – Dsm Aug 02 '16 at 13:24
  • invalidate via timer yes same as the answer below if Percents > 1 then ` begin Percents := 0; end else begin Percents := Percents + 0.025; end; Vts1.Invalidate;` – Vlark.Lopin Aug 02 '16 at 13:25
  • For mine I was assuming % 1-100. Did you remove the /100 from the round calculation? – Dsm Aug 02 '16 at 13:28
  • (To use as is you would need percents := percents + 25) – Dsm Aug 02 '16 at 13:29
  • i do it +25 result showing as blink bar not liek level bar its like one clolor that refreshed – Vlark.Lopin Aug 02 '16 at 13:31
  • Your algorithm did not show level bars, and never did (not even horizontally). That would require you to draw several rectangles (you only draw one). But I am sure that now your fundamental problem is solved, you can handle that yourself. If not, then you should ask a separate question. If it is flashing and you don't like that, that is because of how repaint works and is again a separate question. – Dsm Aug 02 '16 at 13:44