From 3bf8c1c162a7f58039731687a50e8068df25c0bb Mon Sep 17 00:00:00 2001 From: Uladzimir Karpenka Date: Thu, 21 May 2026 21:27:43 +0300 Subject: [PATCH] Extract main status bar component --- MainForm.pas | 76 ++------------------ StatusBar.pas | 187 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 193 insertions(+), 70 deletions(-) create mode 100644 StatusBar.pas diff --git a/MainForm.pas b/MainForm.pas index 99e71d3..c507119 100644 --- a/MainForm.pas +++ b/MainForm.pas @@ -31,6 +31,7 @@ uses WebServer, CATEngine, CATSerial, CATTcp, SpectrumView, + StatusBar, WinFirewall; const @@ -435,9 +436,7 @@ type PbWaterfall: TPaintBox; // ---- Status bar ---- - StatusPanel: TPanel; - StatusCells: array[0..7] of TPanel; - StatusLabels: array[0..7] of TLabel; + StatusPanel: TMainStatusBar; // ---- Helpers ---- procedure BuildUI; @@ -1529,30 +1528,6 @@ var L.Font.Size := 7; end; - procedure MakeStatusCell(Index, CellWidth: Integer; const Cap: string); - begin - StatusCells[Index] := TPanel.Create(Self); - StatusCells[Index].Parent := StatusPanel; - StatusCells[Index].BevelOuter := bvNone; - StatusCells[Index].Align := alLeft; - StatusCells[Index].Width := CellWidth; - StatusCells[Index].BorderSpacing.Left := 1; - StatusCells[Index].BorderSpacing.Top := 1; - StatusCells[Index].BorderSpacing.Bottom := 1; - - StatusLabels[Index] := TLabel.Create(Self); - StatusLabels[Index].Parent := StatusCells[Index]; - StatusLabels[Index].AutoSize := False; - StatusLabels[Index].Align := alClient; - StatusLabels[Index].BorderSpacing.Left := 8; - StatusLabels[Index].BorderSpacing.Right := 8; - StatusLabels[Index].Alignment := taLeftJustify; - StatusLabels[Index].Layout := tlCenter; - StatusLabels[Index].Caption := Cap; - StatusLabels[Index].Font.Name := 'Courier New'; - StatusLabels[Index].Font.Size := 8; - end; - begin // ---- Toolbar ---- PanelToolbar := TPanel.Create(Self); @@ -1583,23 +1558,8 @@ begin // ---- Separator line ---- // ---- Status bar ---- - StatusPanel := TPanel.Create(Self); + StatusPanel := TMainStatusBar.Create(Self); StatusPanel.Parent := Self; - StatusPanel.Align := alBottom; - StatusPanel.Height := 26; - StatusPanel.BevelOuter := bvNone; - - // alLeft controls are arranged by Z-order in LCL; create them in reverse - // visual order so the final bar reads left-to-right as intended. - MakeStatusCell(6, 130, 'SEQ --'); - MakeStatusCell(5, 180, 'TX idle'); - MakeStatusCell(4, 190, 'RX idle'); - MakeStatusCell(7, 90, 'PLL --'); - MakeStatusCell(3, 150, 'Supply --'); - MakeStatusCell(1, 120, 'IP --'); - MakeStatusCell(0, 220, 'Board --'); - MakeStatusCell(2, 170, 'Disconnected'); - StatusCells[6].Align := alClient; // ---- Left panel ---- PanelLeft := TPanel.Create(Self); @@ -2608,10 +2568,8 @@ end; procedure TMainForm.SetStatusText(Index: Integer; const Text: string); begin - if (Index < Low(StatusLabels)) or (Index > High(StatusLabels)) then Exit; - if StatusLabels[Index] = nil then Exit; - if StatusLabels[Index].Caption <> Text then - StatusLabels[Index].Caption := Text; + if StatusPanel = nil then Exit; + StatusPanel.SetStatusText(Index, Text); end; procedure TMainForm.SetRadioOfflineStatus(const ConnText: string; ClearDevice: Boolean); @@ -2639,31 +2597,9 @@ begin end; procedure TMainForm.ApplyStatusTheme(const T: TAppTheme); -var - I: Integer; begin if StatusPanel = nil then Exit; - StatusPanel.Color := T.Border; - for I := Low(StatusCells) to High(StatusCells) do - begin - if StatusCells[I] <> nil then - begin - StatusCells[I].Color := T.Panel; - StatusCells[I].Font.Color := T.Text; - end; - if StatusLabels[I] <> nil then - begin - if I in [4, 5] then - StatusLabels[I].Font.Color := T.TextDim - else if I = 6 then - StatusLabels[I].Font.Color := T.Amber - else if I = 2 then - StatusLabels[I].Font.Color := T.TbStartText - else - StatusLabels[I].Font.Color := T.Text; - StatusLabels[I].Color := T.Panel; - end; - end; + StatusPanel.ApplyTheme(T); end; procedure TMainForm.SetLightTheme(V: Boolean); diff --git a/StatusBar.pas b/StatusBar.pas new file mode 100644 index 0000000..2a6c2cf --- /dev/null +++ b/StatusBar.pas @@ -0,0 +1,187 @@ +unit StatusBar; + +{$IFDEF FPC} + {$MODE Delphi} +{$ENDIF} + +interface + +uses + Classes, SysUtils, Forms, Controls, Graphics, StdCtrls, ExtCtrls, Math, + AppTheme; + +type + TMainStatusBar = class(TPanel) + private + FCells: array[0..7] of TPanel; + FLabels: array[0..7] of TLabel; + procedure CreateStatusCell(Index: Integer; const Cap: string); + procedure LayoutCells; + protected + procedure Resize; override; + public + constructor Create(AOwner: TComponent); override; + procedure SetStatusText(Index: Integer; const Text: string); + procedure ApplyTheme(const T: TAppTheme); + end; + +implementation + +const + STATUS_H = 26; + GAP = 1; + PAD_X = 8; + SEQ_MIN_W = 90; + STATUS_ORDER: array[0..7] of Integer = (2, 0, 1, 3, 7, 4, 5, 6); + BASE_W: array[0..7] of Integer = (280, 160, 170, 150, 190, 180, 130, 90); + INITIAL_TEXT: array[0..7] of string = ( + 'Board --', + 'IP --', + 'Disconnected', + 'Supply --', + 'RX idle', + 'TX idle', + 'SEQ --', + 'PLL --' + ); + +constructor TMainStatusBar.Create(AOwner: TComponent); +var + I: Integer; +begin + inherited Create(AOwner); + Align := alBottom; + Height := STATUS_H; + BevelOuter := bvNone; + + for I := Low(STATUS_ORDER) to High(STATUS_ORDER) do + CreateStatusCell(STATUS_ORDER[I], INITIAL_TEXT[STATUS_ORDER[I]]); + LayoutCells; +end; + +procedure TMainStatusBar.CreateStatusCell(Index: Integer; const Cap: string); +begin + FCells[Index] := TPanel.Create(Self); + FCells[Index].Parent := Self; + FCells[Index].BevelOuter := bvNone; + FCells[Index].Align := alNone; + FCells[Index].Width := BASE_W[Index]; + FCells[Index].Top := 1; + FCells[Index].Height := Height - 2; + + FLabels[Index] := TLabel.Create(Self); + FLabels[Index].Parent := FCells[Index]; + FLabels[Index].AutoSize := False; + FLabels[Index].Align := alClient; + FLabels[Index].BorderSpacing.Left := PAD_X; + FLabels[Index].BorderSpacing.Right := PAD_X; + FLabels[Index].Alignment := taLeftJustify; + FLabels[Index].Layout := tlCenter; + FLabels[Index].Caption := Cap; + FLabels[Index].Font.Name := 'Courier New'; + FLabels[Index].Font.Size := 8; +end; + +procedure TMainStatusBar.LayoutCells; +var + PPI, X, I, Idx, W, H, G, Pad, FixedNatural, FixedAvail: Integer; + + function S(Value: Integer): Integer; + begin + Result := (Int64(Value) * PPI + 48) div 96; + if (Value > 0) and (Result < 1) then + Result := 1; + end; + + function FixedCellWidth(Index: Integer): Integer; + begin + Result := S(BASE_W[Index]); + if FixedAvail <= 0 then + Result := 0 + else if FixedAvail < FixedNatural then + begin + Result := (Int64(Result) * FixedAvail + FixedNatural div 2) div FixedNatural; + if FixedAvail >= S(48) * 7 then + Result := Max(S(48), Result); + end; + end; + +begin + PPI := Screen.PixelsPerInch; + if PPI <= 0 then PPI := 96; + + G := S(GAP); + Pad := S(PAD_X); + if Height <> S(STATUS_H) then + Height := S(STATUS_H); + H := Max(1, ClientHeight - G * 2); + + FixedNatural := 0; + for I := Low(STATUS_ORDER) to High(STATUS_ORDER) - 1 do + Inc(FixedNatural, S(BASE_W[STATUS_ORDER[I]])); + FixedAvail := ClientWidth - S(SEQ_MIN_W) - G * 8; + + X := 0; + for I := Low(STATUS_ORDER) to High(STATUS_ORDER) do + begin + Idx := STATUS_ORDER[I]; + if FCells[Idx] = nil then Continue; + + Inc(X, G); + if Idx = 6 then + W := Max(0, ClientWidth - X) + else + W := FixedCellWidth(Idx); + + FCells[Idx].SetBounds(X, G, W, H); + if FLabels[Idx] <> nil then + begin + FLabels[Idx].BorderSpacing.Left := Pad; + FLabels[Idx].BorderSpacing.Right := Pad; + end; + Inc(X, W); + end; +end; + +procedure TMainStatusBar.Resize; +begin + inherited Resize; + LayoutCells; +end; + +procedure TMainStatusBar.SetStatusText(Index: Integer; const Text: string); +begin + if (Index < Low(FLabels)) or (Index > High(FLabels)) then Exit; + if FLabels[Index] = nil then Exit; + if FLabels[Index].Caption <> Text then + FLabels[Index].Caption := Text; +end; + +procedure TMainStatusBar.ApplyTheme(const T: TAppTheme); +var + I: Integer; +begin + Color := T.Border; + for I := Low(FCells) to High(FCells) do + begin + if FCells[I] <> nil then + begin + FCells[I].Color := T.Panel; + FCells[I].Font.Color := T.Text; + end; + if FLabels[I] <> nil then + begin + if I in [4, 5] then + FLabels[I].Font.Color := T.TextDim + else if I = 6 then + FLabels[I].Font.Color := T.Amber + else if I = 2 then + FLabels[I].Font.Color := T.TbStartText + else + FLabels[I].Font.Color := T.Text; + FLabels[I].Color := T.Panel; + end; + end; +end; + +end.