{ Copyright (C) 2026 - Uladzimir Karpenka, EW8BAK This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. } 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..8] of TPanel; FLabels: array[0..8] 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 SetStatusVisible(Index: Integer; Visible: Boolean); procedure ApplyTheme(const T: TAppTheme); end; implementation const STATUS_H = 26; GAP = 1; PAD_X = 8; SEQ_MIN_W = 90; STATUS_ORDER: array[0..8] of Integer = (2, 0, 1, 3, 7, 8, 4, 5, 6); // поле 7: PLL (openHPSDR) ИЛИ статус QO-100 beacon lock (Pluto) — шире под "BCN LOCK …". // поле 8: динамический статус DMR; скрыт во всех остальных режимах. // поле 3: Supply (HPSDR) ИЛИ две температуры Pluto "Temp 37/53C RSSI -89dB" — шире. BASE_W: array[0..8] of Integer = (280, 160, 170, 190, 190, 180, 130, 200, 280); INITIAL_TEXT: array[0..8] 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]]); FCells[8].Visible := False; 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.Size := 8; end; procedure TMainStatusBar.LayoutCells; var PPI, X, I, Idx, W, H, G, Pad, FixedNatural, FixedAvail: Integer; VisibleCount, FixedCount: 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) * FixedCount 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; FixedCount := 0; VisibleCount := 0; for I := Low(STATUS_ORDER) to High(STATUS_ORDER) do begin Idx := STATUS_ORDER[I]; if (FCells[Idx] = nil) or (not FCells[Idx].Visible) then Continue; Inc(VisibleCount); if Idx <> 6 then begin Inc(FixedNatural, S(BASE_W[Idx])); Inc(FixedCount); end; end; FixedAvail := ClientWidth - S(SEQ_MIN_W) - G * VisibleCount; X := 0; for I := Low(STATUS_ORDER) to High(STATUS_ORDER) do begin Idx := STATUS_ORDER[I]; if (FCells[Idx] = nil) or (not FCells[Idx].Visible) 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.SetStatusVisible(Index: Integer; Visible: Boolean); begin if (Index < Low(FCells)) or (Index > High(FCells)) then Exit; if (FCells[Index] = nil) or (FCells[Index].Visible = Visible) then Exit; FCells[Index].Visible := Visible; LayoutCells; 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.