diff --git a/FlatDropDown.pas b/FlatDropDown.pas index 009b109..7826406 100644 --- a/FlatDropDown.pas +++ b/FlatDropDown.pas @@ -32,9 +32,11 @@ type FItemBtns: array of TFlatButton; FItemIndex: Integer; FColumns: Integer; - FItemBtnH: Integer; + FItemBtnH96: Integer; FRightInset96: Integer; FIsOpen: Boolean; + FStyleProc: TButtonStyleProc; + FPanelColor: TColor; FOnSelect: TDropDownSelectEvent; procedure ItemClick(Sender: TObject); procedure PositionPopup; @@ -61,15 +63,17 @@ begin inherited Create; FAnchor := AAnchor; FPopupParent := APopupParent; - FColumns := ACols; - FItemBtnH := ABtnH; + FColumns := Max(1, ACols); + FItemBtnH96 := Max(1, ABtnH); FRightInset96 := Max(0, ARightInset96); FItemIndex := 0; FIsOpen := False; + FStyleProc := nil; + FPanelColor := TColor($00181818); FPopup := TPanel.Create(nil); FPopup.Parent := APopupParent; FPopup.BevelOuter := bvNone; - FPopup.Color := TColor($00181818); + FPopup.Color := FPanelColor; FPopup.Visible := False; end; @@ -81,7 +85,7 @@ end; procedure TFlatDropDown.SetItems(const ANames: array of string); var - I, Count, BtnW, PopW: Integer; + I, Count, BtnW, PopW, ItemBtnH, EdgePad, ItemGap: Integer; B: TFlatButton; begin while FPopup.ControlCount > 0 do @@ -93,17 +97,22 @@ begin // при каждом наполнении: SetItems бывает и до, и после DPI-scale формы. PopW := Max(1, FPopupParent.ClientWidth - FPopupParent.Scale96ToForm(FRightInset96)); - BtnW := (PopW - 4) div FColumns; + ItemBtnH := FPopupParent.Scale96ToForm(FItemBtnH96); + EdgePad := FPopupParent.Scale96ToForm(2); + ItemGap := FPopupParent.Scale96ToForm(2); + BtnW := Max(1, (PopW - 2 * EdgePad) div FColumns); FPopup.SetBounds(0, 0, PopW, - ((Count + FColumns - 1) div FColumns) * FItemBtnH); + ((Count + FColumns - 1) div FColumns) * ItemBtnH); for I := 0 to Count - 1 do begin B := MakeFlatBtn(FPopup, ANames[I], - 2 + (I mod FColumns) * BtnW, - (I div FColumns) * FItemBtnH, - BtnW - 2, FItemBtnH, @ItemClick); + EdgePad + (I mod FColumns) * BtnW, + (I div FColumns) * ItemBtnH, + Max(1, BtnW - ItemGap), ItemBtnH, @ItemClick); B.Tag := I; B.Active := (I = FItemIndex); + if Assigned(FStyleProc) then + FStyleProc(B, B.Active); FItemBtns[I] := B; end; end; @@ -141,9 +150,10 @@ var begin P := FAnchor.ClientToScreen(Point(0, FAnchor.Height)); P := FPopupParent.ScreenToClient(P); - Y0 := P.Y + 2; + Y0 := P.Y + FPopupParent.Scale96ToForm(2); if Y0 + FPopup.Height > FPopupParent.ClientHeight then - Y0 := P.Y - FAnchor.Height - FPopup.Height - 2; + Y0 := P.Y - FAnchor.Height - FPopup.Height - + FPopupParent.Scale96ToForm(2); if Y0 < 0 then Y0 := 0; FPopup.Top := Y0; FPopup.Left := 0; @@ -173,6 +183,8 @@ procedure TFlatDropDown.ApplyStyle(AStyleProc: TButtonStyleProc; APanelColor: TC var I: Integer; begin + FStyleProc := AStyleProc; + FPanelColor := APanelColor; FPopup.Color := APanelColor; for I := 0 to High(FItemBtns) do if FItemBtns[I] <> nil then diff --git a/MainForm.lfm b/MainForm.lfm index 5f1454b..2ae9e07 100644 --- a/MainForm.lfm +++ b/MainForm.lfm @@ -7,7 +7,6 @@ object MainForm: TMainForm Color = clBlack Font.Color = clSilver Font.Height = -13 - Font.Name = 'Courier New' Position = poDesigned LCLVersion = '4.6.0.0' OnClose = FormClose diff --git a/MainForm.pas b/MainForm.pas index e8cd3c0..84ae375 100644 --- a/MainForm.pas +++ b/MainForm.pas @@ -106,6 +106,7 @@ const TOP_SMETER_GAP = 8; TOP_VFO_FONT = 25; LEFT_SCROLL_GUTTER_W = 8; + LEFT_PANEL_CONTENT_W = 232; LEFT_PANEL_BTN_PAD = 3; LEFT_PANEL_BTN_GAP = 2; @@ -825,17 +826,18 @@ const // Высота RX-блока левой панели: с рядом ATT (openHPSDR) и без него (Pluto — // там усиление тракта в блоке RF AGC). Переключает LayoutLeftPanel, он же // двигает панели ниже. - RX_BLOCK_H = 96; - RX_BLOCK_H_ATT = 120; + RX_BLOCK_H = 100; + RX_BLOCK_H_ATT = 124; function TMainForm.LeftPanelButtonWidth(PanelWidth, ColCount, ColIndex: Integer): Integer; var - InnerW, BaseW, Extra: Integer; + InnerW, BaseW, Extra, Pad, Gap: Integer; begin if ColCount <= 0 then Exit(0); - InnerW := PanelWidth - 2 * LEFT_PANEL_BTN_PAD - - (ColCount - 1) * LEFT_PANEL_BTN_GAP; + Pad := MulDiv(LEFT_PANEL_BTN_PAD, PanelWidth, LEFT_PANEL_CONTENT_W); + Gap := MulDiv(LEFT_PANEL_BTN_GAP, PanelWidth, LEFT_PANEL_CONTENT_W); + InnerW := PanelWidth - 2 * Pad - (ColCount - 1) * Gap; BaseW := InnerW div ColCount; Extra := InnerW mod ColCount; Result := BaseW; @@ -845,12 +847,14 @@ end; function TMainForm.LeftPanelButtonLeft(PanelWidth, ColCount, ColIndex: Integer): Integer; var - i: Integer; + i, Pad, Gap: Integer; begin - Result := LEFT_PANEL_BTN_PAD; + Pad := MulDiv(LEFT_PANEL_BTN_PAD, PanelWidth, LEFT_PANEL_CONTENT_W); + Gap := MulDiv(LEFT_PANEL_BTN_GAP, PanelWidth, LEFT_PANEL_CONTENT_W); + Result := Pad; for i := 0 to ColIndex - 1 do Inc(Result, LeftPanelButtonWidth(PanelWidth, ColCount, i) + - LEFT_PANEL_BTN_GAP); + Gap); end; // =========================================================================== @@ -1864,9 +1868,9 @@ procedure TMainForm.BuildUI; const BTN_H = 26; BTN_SM = 24; - LEFT_W = 232; - // Заголовок (16) + MODE_ROWS рядов с шагом 27 + нижний отступ. - MODE_PANEL_H = 16 + (MODE_ROWS - 1) * 27 + BTN_SM + 7; + LEFT_W = LEFT_PANEL_CONTENT_W; + // Заголовок с отступом (20) + MODE_ROWS рядов с шагом 27 + нижний отступ. + MODE_PANEL_H = 20 + (MODE_ROWS - 1) * 27 + BTN_SM + 3; PLUTO_AGC_H = 64; // высота отдельного Pluto RF-AGC блока (под AGC) TOP_VFO_W = 244; TOP_VFO_FREQ_H = 47; @@ -1899,8 +1903,7 @@ var L.Left := ALeft; L.Top := ATop; L.Font.Color := CLR_TEXTDIM; - L.Font.Name := 'Courier New'; - L.Font.Size := 7; + L.Font.Size := 8; end; begin @@ -1981,8 +1984,7 @@ begin PanelVfoA.BevelOuter := bvNone; BtnTopVfoASelect := MakeBtn(PanelVfoA, 'A', 0, 5, 20, 18, BtnTopVfoASelectClick); - BtnTopVfoASelect.Font.Name := 'Courier New'; - BtnTopVfoASelect.Font.Size := 7; + BtnTopVfoASelect.Font.Size := 8; BtnTopVfoASelect.Font.Bold := True; BtnTopVfoASelect.ClrNorm := TColor($00303030); BtnTopVfoASelect.ClrHot := TColor($00404040); @@ -1992,8 +1994,7 @@ begin BtnTopVfoASelect.ClrTextAct := CLR_TEXT; BtnTopVfoATX := MakeBtn(PanelVfoA, 'TX', 0, 25, 20, 18, BtnTopVfoATXClick); - BtnTopVfoATX.Font.Name := 'Courier New'; - BtnTopVfoATX.Font.Size := 7; + BtnTopVfoATX.Font.Size := 8; BtnTopVfoATX.Font.Bold := True; BtnTopVfoATX.ClrNorm := TColor($00303030); BtnTopVfoATX.ClrHot := TColor($00404040); @@ -2006,7 +2007,7 @@ begin LblVfoALabel.Parent := PanelVfoA; LblVfoALabel.Caption := 'VFO-A'; LblVfoALabel.Left := 0; LblVfoALabel.Top := 0; - LblVfoALabel.Font.Name := 'Courier New'; LblVfoALabel.Font.Size := 7; + LblVfoALabel.Font.Size := 8; LblVfoALabel.Font.Color := CLR_TEXTDIM; LblVfoALabel.Visible := False; @@ -2038,8 +2039,7 @@ begin PanelVfoB.BevelOuter := bvNone; BtnTopVfoBSelect := MakeBtn(PanelVfoB, 'B', 0, 5, 20, 18, BtnTopVfoBSelectClick); - BtnTopVfoBSelect.Font.Name := 'Courier New'; - BtnTopVfoBSelect.Font.Size := 7; + BtnTopVfoBSelect.Font.Size := 8; BtnTopVfoBSelect.Font.Bold := True; BtnTopVfoBSelect.ClrNorm := TColor($00303030); BtnTopVfoBSelect.ClrHot := TColor($00404040); @@ -2049,8 +2049,7 @@ begin BtnTopVfoBSelect.ClrTextAct := CLR_TEXT; BtnTopVfoBTX := MakeBtn(PanelVfoB, 'TX', 0, 25, 20, 18, BtnTopVfoBTXClick); - BtnTopVfoBTX.Font.Name := 'Courier New'; - BtnTopVfoBTX.Font.Size := 7; + BtnTopVfoBTX.Font.Size := 8; BtnTopVfoBTX.Font.Bold := True; BtnTopVfoBTX.ClrNorm := TColor($00303030); BtnTopVfoBTX.ClrHot := TColor($00404040); @@ -2063,7 +2062,7 @@ begin LblVfoBLabel.Parent := PanelVfoB; LblVfoBLabel.Caption := 'VFO-B'; LblVfoBLabel.Left := 0; LblVfoBLabel.Top := 0; - LblVfoBLabel.Font.Name := 'Courier New'; LblVfoBLabel.Font.Size := 7; + LblVfoBLabel.Font.Size := 8; LblVfoBLabel.Font.Color := CLR_TEXTDIM; LblVfoBLabel.Visible := False; @@ -2096,9 +2095,9 @@ begin StyleSpanButton(BtnVfoACopyB, False); StyleSpanButton(BtnVfoSwap, False); StyleSpanButton(BtnVfoBCopyA, False); - BtnVfoACopyB.Font.Size := 7; - BtnVfoSwap.Font.Size := 7; - BtnVfoBCopyA.Font.Size := 7; + BtnVfoACopyB.Font.Size := 8; + BtnVfoSwap.Font.Size := 8; + BtnVfoBCopyA.Font.Size := 8; LayoutTopVfoBlock; @@ -2110,23 +2109,23 @@ begin MakeLbl(PanelTX, 'TX', 4, 2); BtnMOX := MakeBtn(PanelTX, 'MOX', - LeftPanelButtonLeft(LEFT_W, 4, 0), 16, + LeftPanelButtonLeft(LEFT_W, 4, 0), 20, LeftPanelButtonWidth(LEFT_W, 4, 0), BTN_SM, BtnMOXClick); BtnTUN := MakeBtn(PanelTX, 'TUN', - LeftPanelButtonLeft(LEFT_W, 4, 1), 16, + LeftPanelButtonLeft(LEFT_W, 4, 1), 20, LeftPanelButtonWidth(LEFT_W, 4, 1), BTN_SM, BtnTUNClick); // PureSignal: ЛКМ = вкл/выкл, ПКМ = поповер настроек/управления. BtnPS := MakeBtn(PanelTX, 'PS', - LeftPanelButtonLeft(LEFT_W, 4, 2), 16, + LeftPanelButtonLeft(LEFT_W, 4, 2), 20, LeftPanelButtonWidth(LEFT_W, 4, 2), BTN_SM, BtnPSClick); BtnPS.OnMouseDown := BtnPSMouseDown; Btn2Ton := MakeBtn(PanelTX, '2TON', - LeftPanelButtonLeft(LEFT_W, 4, 3), 16, + LeftPanelButtonLeft(LEFT_W, 4, 3), 20, LeftPanelButtonWidth(LEFT_W, 4, 3), BTN_SM, Btn2TonClick); UpdateTXControlAvailability; BtnDUP := MakeBtn(PanelTX, 'DUP', - LeftPanelButtonLeft(LEFT_W, 4, 0), 44, + LeftPanelButtonLeft(LEFT_W, 4, 0), 48, LeftPanelButtonWidth(LEFT_W, 4, 0), BTN_SM, BtnDUPClick); BtnDUP.Tag := 0; StyleButton(BtnDUP, FController.FDisplayDuplex); @@ -2135,7 +2134,7 @@ begin // активна только в DUP (см. UpdateRxMuteButton). ON (подсвечена, умолч.) = // RX-аудио глушится на TX; OFF = слышим свой downlink во время передачи. BtnRxMute := MakeBtn(PanelTX, 'RX MUTE', - LeftPanelButtonLeft(LEFT_W, 4, 1), 44, + LeftPanelButtonLeft(LEFT_W, 4, 1), 48, LeftPanelButtonWidth(LEFT_W, 4, 1), BTN_SM, BtnRxMuteClick); StyleButton(BtnRxMute, FController.FRxMuteOnTx); UpdateRxMuteButton; // видимость/активность по DUP @@ -2143,7 +2142,7 @@ begin // TX-профиль — свободные колонки 2..3 того же ряда (панель не растёт по // высоте). Подпись = имя активного профиля, ЛКМ — список, ПКМ — настройки. BtnTXProfile := MakeBtn(PanelTX, 'Default', - LeftPanelButtonLeft(LEFT_W, 4, 2), 44, + LeftPanelButtonLeft(LEFT_W, 4, 2), 48, LeftPanelButtonLeft(LEFT_W, 4, 3) + LeftPanelButtonWidth(LEFT_W, 4, 3) - LeftPanelButtonLeft(LEFT_W, 4, 2), @@ -2159,10 +2158,10 @@ begin LblDrv := TLabel.Create(Self); LblDrv.Parent := PanelTX; LblDrv.Left := 4; LblDrv.Top := 77; LblDrv.Caption := 'DRV'; LblDrv.Font.Color := CLR_TEXTDIM; - LblDrv.Font.Name := 'Courier New'; LblDrv.Font.Size := 7; + LblDrv.Font.Size := 8; TrkDrive := TFlatSlider.Create(Self); - TrkDrive.Parent := PanelTX; TrkDrive.Left := 34; TrkDrive.Top := 74; + TrkDrive.Parent := PanelTX; TrkDrive.Left := 34; TrkDrive.Top := 78; TrkDrive.Width := 120; TrkDrive.Height := 16; TrkDrive.Min := 0; TrkDrive.Max := 100; TrkDrive.Position := 50; TrkDrive.OnChange := TrkDriveChange; @@ -2170,8 +2169,8 @@ begin LblDrvVal := TLabel.Create(Self); LblDrvVal.Parent := PanelTX; LblDrvVal.Left := 158; LblDrvVal.Top := 77; - LblDrvVal.Font.Color := CLR_TEXT; LblDrvVal.Font.Name := 'Courier New'; - LblDrvVal.Font.Size := 7; + LblDrvVal.Font.Color := CLR_TEXT; + LblDrvVal.Font.Size := 8; UpdateDriveLabel; Inc(Y, 100); @@ -2184,14 +2183,14 @@ begin MakeLbl(PanelRXBlock, 'RX', 4, 2); BtnCTun := MakeBtn(PanelRXBlock, 'CTUN', - LeftPanelButtonLeft(LEFT_W, 3, 0), 16, + LeftPanelButtonLeft(LEFT_W, 3, 0), 20, LeftPanelButtonWidth(LEFT_W, 3, 0), BTN_H, BtnCTunClick); BtnCTun.Tag := 0; StyleButton(BtnCTun, FController.FCTun); BtnChannels := MakeBtn(PanelRXBlock, 'Channel', - LeftPanelButtonLeft(LEFT_W, 3, 1), 16, + LeftPanelButtonLeft(LEFT_W, 3, 1), 20, LeftPanelButtonWidth(LEFT_W, 3, 1), BTN_H, BtnChannelsClick); BtnChannels.OnMouseDown := BtnChannelsMouseDown; @@ -2205,7 +2204,7 @@ begin // QO-100 beacon lock — 3-я колонка ряда. Видима только в XVTR-режиме // (см. рендер rfXvtr ниже). Подпись/подсветка обновляются по rfBeaconLock. BtnBeacon := MakeBtn(PanelRXBlock, 'BEACON', - LeftPanelButtonLeft(LEFT_W, 3, 2), 16, + LeftPanelButtonLeft(LEFT_W, 3, 2), 20, LeftPanelButtonWidth(LEFT_W, 3, 2), BTN_H, BtnBeaconClick); BtnBeacon.Visible := False; @@ -2214,26 +2213,26 @@ begin MakeLbl(PanelRXBlock, 'VOL', 4, 50); TrkVolume := TFlatSlider.Create(Self); - TrkVolume.Parent := PanelRXBlock; TrkVolume.Left := 34; TrkVolume.Top := 46; + TrkVolume.Parent := PanelRXBlock; TrkVolume.Left := 34; TrkVolume.Top := 50; TrkVolume.Width := LEFT_W - 38; TrkVolume.Height := 16; TrkVolume.Min := 0; TrkVolume.Max := 100; TrkVolume.Position := FController.FVolume; TrkVolume.OnChange := TrkVolumeChange; BtnNR := MakeBtn(PanelRXBlock, 'NR', - LeftPanelButtonLeft(LEFT_W, 5, 0), 70, + LeftPanelButtonLeft(LEFT_W, 5, 0), 74, LeftPanelButtonWidth(LEFT_W, 5, 0), BTN_SM, BtnNRClick); BtnNB := MakeBtn(PanelRXBlock, 'NB', - LeftPanelButtonLeft(LEFT_W, 5, 1), 70, + LeftPanelButtonLeft(LEFT_W, 5, 1), 74, LeftPanelButtonWidth(LEFT_W, 5, 1), BTN_SM, BtnNBClick); BtnSNB := MakeBtn(PanelRXBlock, 'SNB', - LeftPanelButtonLeft(LEFT_W, 5, 2), 70, + LeftPanelButtonLeft(LEFT_W, 5, 2), 74, LeftPanelButtonWidth(LEFT_W, 5, 2), BTN_SM, BtnSNBClick); BtnANF := MakeBtn(PanelRXBlock, 'ANF', - LeftPanelButtonLeft(LEFT_W, 5, 3), 70, + LeftPanelButtonLeft(LEFT_W, 5, 3), 74, LeftPanelButtonWidth(LEFT_W, 5, 3), BTN_SM, BtnANFClick); BtnMute := MakeBtn(PanelRXBlock, 'MUTE', - LeftPanelButtonLeft(LEFT_W, 5, 4), 70, + LeftPanelButtonLeft(LEFT_W, 5, 4), 74, LeftPanelButtonWidth(LEFT_W, 5, 4), BTN_SM, BtnMuteClick); // Шаговый аттенюатор ADC — плавный 0..31 дБ (железо openHPSDR так и умеет, @@ -2242,10 +2241,10 @@ begin LblAtt := TLabel.Create(Self); LblAtt.Parent := PanelRXBlock; LblAtt.Left := 4; LblAtt.Top := 106; LblAtt.Caption := 'ATT'; LblAtt.Font.Color := CLR_TEXTDIM; - LblAtt.Font.Name := 'Courier New'; LblAtt.Font.Size := 7; + LblAtt.Font.Size := 8; TrkAtt := TFlatSlider.Create(Self); - TrkAtt.Parent := PanelRXBlock; TrkAtt.Left := 34; TrkAtt.Top := 102; + TrkAtt.Parent := PanelRXBlock; TrkAtt.Left := 34; TrkAtt.Top := 106; TrkAtt.Width := LEFT_W - 76; TrkAtt.Height := 16; TrkAtt.Min := 0; TrkAtt.Max := 31; // ADC step attenuator, дБ TrkAtt.Position := FController.FAtten; @@ -2254,8 +2253,8 @@ begin LblAttVal := TLabel.Create(Self); LblAttVal.Parent := PanelRXBlock; LblAttVal.Left := LEFT_W - 38; LblAttVal.Top := 106; - LblAttVal.Font.Color := CLR_TEXT; LblAttVal.Font.Name := 'Courier New'; - LblAttVal.Font.Size := 7; + LblAttVal.Font.Color := CLR_TEXT; + LblAttVal.Font.Size := 8; UpdateAttLabel; Inc(Y, RX_BLOCK_H_ATT + 4); @@ -2269,27 +2268,27 @@ begin // AGC mode — 5 кнопок в ряд MakeLbl(PanelAGC, 'AGC', 4, 2); BtnAGCMode[0] := MakeBtn(PanelAGC, 'FAST', - LeftPanelButtonLeft(LEFT_W, 5, 0), 16, + LeftPanelButtonLeft(LEFT_W, 5, 0), 20, LeftPanelButtonWidth(LEFT_W, 5, 0), BTN_SM, BtnAGCModeClick); BtnAGCMode[0].Tag := 0; BtnAGCMode[1] := MakeBtn(PanelAGC, 'MED', - LeftPanelButtonLeft(LEFT_W, 5, 1), 16, + LeftPanelButtonLeft(LEFT_W, 5, 1), 20, LeftPanelButtonWidth(LEFT_W, 5, 1), BTN_SM, BtnAGCModeClick); BtnAGCMode[1].Tag := 1; BtnAGCMode[2] := MakeBtn(PanelAGC, 'SLOW', - LeftPanelButtonLeft(LEFT_W, 5, 2), 16, + LeftPanelButtonLeft(LEFT_W, 5, 2), 20, LeftPanelButtonWidth(LEFT_W, 5, 2), BTN_SM, BtnAGCModeClick); BtnAGCMode[2].Tag := 2; BtnAGCMode[3] := MakeBtn(PanelAGC, 'LONG', - LeftPanelButtonLeft(LEFT_W, 5, 3), 16, + LeftPanelButtonLeft(LEFT_W, 5, 3), 20, LeftPanelButtonWidth(LEFT_W, 5, 3), BTN_SM, BtnAGCModeClick); BtnAGCMode[3].Tag := 3; BtnAGCMode[4] := MakeBtn(PanelAGC, 'OFF', - LeftPanelButtonLeft(LEFT_W, 5, 4), 16, + LeftPanelButtonLeft(LEFT_W, 5, 4), 20, LeftPanelButtonWidth(LEFT_W, 5, 4), BTN_SM, BtnAGCModeClick); BtnAGCMode[4].Tag := 4; @@ -2298,7 +2297,7 @@ begin // AGC level slider MakeLbl(PanelAGC, 'THRESH', 4, 44); TrkAGC := TFlatSlider.Create(Self); - TrkAGC.Parent := PanelAGC; TrkAGC.Left := 56; TrkAGC.Top := 40; + TrkAGC.Parent := PanelAGC; TrkAGC.Left := 56; TrkAGC.Top := 44; TrkAGC.Width := LEFT_W - 96; TrkAGC.Height := 16; TrkAGC.Min := 20; TrkAGC.Max := 120; // 20..120 → −20..-120 dBm TrkAGC.Position := FController.FAGCTop; @@ -2309,8 +2308,8 @@ begin LblAGCTop.Parent := PanelAGC; LblAGCTop.Left := LEFT_W - 38; LblAGCTop.Top := 44; LblAGCTop.Caption := Format('-%ddB', [FController.FAGCTop]); - LblAGCTop.Font.Color := CLR_TEXT; LblAGCTop.Font.Name := 'Courier New'; - LblAGCTop.Font.Size := 7; + LblAGCTop.Font.Color := CLR_TEXT; + LblAGCTop.Font.Size := 8; Inc(Y, 74); @@ -2324,25 +2323,25 @@ begin MakeLbl(PanelPlutoAGC, 'RF AGC', 4, 2); // Режимы AD936x: FAST/SLOW/HYB/MAN. Tag = индекс режима (1/2/3/0). BtnPlutoGain[0] := MakeBtn(PanelPlutoAGC, 'FAST', - LeftPanelButtonLeft(LEFT_W, 4, 0), 16, LeftPanelButtonWidth(LEFT_W, 4, 0), + LeftPanelButtonLeft(LEFT_W, 4, 0), 20, LeftPanelButtonWidth(LEFT_W, 4, 0), BTN_SM, BtnPlutoGainClick); BtnPlutoGain[0].Tag := 1; BtnPlutoGain[1] := MakeBtn(PanelPlutoAGC, 'SLOW', - LeftPanelButtonLeft(LEFT_W, 4, 1), 16, LeftPanelButtonWidth(LEFT_W, 4, 1), + LeftPanelButtonLeft(LEFT_W, 4, 1), 20, LeftPanelButtonWidth(LEFT_W, 4, 1), BTN_SM, BtnPlutoGainClick); BtnPlutoGain[1].Tag := 2; BtnPlutoGain[2] := MakeBtn(PanelPlutoAGC, 'HYB', - LeftPanelButtonLeft(LEFT_W, 4, 2), 16, LeftPanelButtonWidth(LEFT_W, 4, 2), + LeftPanelButtonLeft(LEFT_W, 4, 2), 20, LeftPanelButtonWidth(LEFT_W, 4, 2), BTN_SM, BtnPlutoGainClick); BtnPlutoGain[2].Tag := 3; BtnPlutoGain[3] := MakeBtn(PanelPlutoAGC, 'MAN', - LeftPanelButtonLeft(LEFT_W, 4, 3), 16, LeftPanelButtonWidth(LEFT_W, 4, 3), + LeftPanelButtonLeft(LEFT_W, 4, 3), 20, LeftPanelButtonWidth(LEFT_W, 4, 3), BTN_SM, BtnPlutoGainClick); BtnPlutoGain[3].Tag := 0; // Manual gain slider. MakeLbl(PanelPlutoAGC, 'GAIN', 4, 44); TrkRxGain := TFlatSlider.Create(Self); - TrkRxGain.Parent := PanelPlutoAGC; TrkRxGain.Left := 56; TrkRxGain.Top := 40; + TrkRxGain.Parent := PanelPlutoAGC; TrkRxGain.Left := 56; TrkRxGain.Top := 44; TrkRxGain.Width := LEFT_W - 96; TrkRxGain.Height := 16; TrkRxGain.Min := 0; TrkRxGain.Max := 73; // AD936x hardwaregain dB TrkRxGain.Position := FController.FRxGainDb; @@ -2351,8 +2350,8 @@ begin LblRxGain.Parent := PanelPlutoAGC; LblRxGain.Left := LEFT_W - 38; LblRxGain.Top := 44; LblRxGain.Caption := Format('%ddB', [FController.FRxGainDb]); - LblRxGain.Font.Color := CLR_TEXT; LblRxGain.Font.Name := 'Courier New'; - LblRxGain.Font.Size := 7; + LblRxGain.Font.Color := CLR_TEXT; + LblRxGain.Font.Size := 8; // НЕ Inc(Y): панель скрыта и наложена на позицию Bands; LayoutLeftPanel сдвинет. // Band selector. Высота резервирует 2 дополнительных ряда под XVTR-кнопки @@ -2367,7 +2366,7 @@ begin begin B := MakeBtn(PanelBands, BAND_NAMES[i], LeftPanelButtonLeft(LEFT_W, 6, i mod 6), - 16 + (i div 6) * 27, + 20 + (i div 6) * 27, LeftPanelButtonWidth(LEFT_W, 6, i mod 6), BTN_SM, BtnBandClick); B.Tag := i; @@ -2391,7 +2390,7 @@ begin begin B := MakeBtn(PanelMode, MODE_NAMES[i], LeftPanelButtonLeft(LEFT_W, 4, i mod 4), - 16 + (i div 4) * 27, + 20 + (i div 4) * 27, LeftPanelButtonWidth(LEFT_W, 4, i mod 4), BTN_SM, BtnModeClick); B.Tag := i; @@ -2412,7 +2411,7 @@ begin begin B := MakeBtn(PanelFilter, '---', LeftPanelButtonLeft(LEFT_W, FILT_GRID_COLS, i mod FILT_GRID_COLS), - 16 + (i div FILT_GRID_COLS) * 27, + 20 + (i div FILT_GRID_COLS) * 27, LeftPanelButtonWidth(LEFT_W, FILT_GRID_COLS, i mod FILT_GRID_COLS), BTN_SM, BtnFilterClick); B.Tag := i; @@ -2448,7 +2447,6 @@ begin LblRawDev.Alignment := taRightJustify; LblRawDev.Caption := Format('%.2f k', [FController.FRawFMDeviation / 1000.0]); LblRawDev.Font.Color := CLR_TEXT; - LblRawDev.Font.Name := 'Courier New'; LblRawDev.Font.Size := 8; // FM Squelch panel (visible only in FM mode, above CTCSS) @@ -2460,15 +2458,15 @@ begin MakeLbl(PanelFMSQ, 'SQL', 4, 2); W := LeftPanelButtonWidth(LEFT_W, 4, 0); BtnFMSQ := MakeBtn(PanelFMSQ, 'SQL', - LeftPanelButtonLeft(LEFT_W, 4, 0), 16, + LeftPanelButtonLeft(LEFT_W, 4, 0), 20, W, BTN_SM, BtnFMSQClick); StyleButton(BtnFMSQ, False); - // Slider: vertically centred with button text (button top=16, h=24 → center=28) + // Slider vertically centred with the button row. // Reserve 28px on the right for the numeric label TrkFMSQ := TFlatSlider.Create(Self); TrkFMSQ.Parent := PanelFMSQ; TrkFMSQ.Left := BtnFMSQ.Left + BtnFMSQ.Width + 3; - TrkFMSQ.Top := 21; // 28 - 14/2 = 21 + TrkFMSQ.Top := 25; TrkFMSQ.Width := LEFT_W - TrkFMSQ.Left - 30 - 4; // leave 30px for label + margins TrkFMSQ.Height := 14; TrkFMSQ.Min := 0; TrkFMSQ.Max := 100; @@ -2478,11 +2476,10 @@ begin LblFMSQ := TLabel.Create(Self); LblFMSQ.Parent := PanelFMSQ; LblFMSQ.Left := TrkFMSQ.Left + TrkFMSQ.Width + 3; - LblFMSQ.Top := 21; + LblFMSQ.Top := 23; LblFMSQ.Width := 26; LblFMSQ.Caption := IntToStr(FController.FFMSQLevel); LblFMSQ.Font.Color := CLR_TEXT; - LblFMSQ.Font.Name := 'Courier New'; LblFMSQ.Font.Size := 8; // FM CTCSS panel (visible only when FM mode is active) @@ -2493,12 +2490,12 @@ begin PanelFMCTCSS.Visible := False; MakeLbl(PanelFMCTCSS, 'CTCSS', 4, 2); BtnFMCTCSS := MakeBtn(PanelFMCTCSS, 'CTCSS', - LeftPanelButtonLeft(LEFT_W, 3, 0), 16, + LeftPanelButtonLeft(LEFT_W, 3, 0), 20, LeftPanelButtonWidth(LEFT_W, 3, 0), BTN_H, BtnFMCTCSSClick); StyleButton(BtnFMCTCSS, False); BtnFMCTCSSTone := MakeBtn(PanelFMCTCSS, CTCSS_NAMES[0], - LeftPanelButtonLeft(LEFT_W, 3, 1), 16, + LeftPanelButtonLeft(LEFT_W, 3, 1), 20, LEFT_W - LEFT_PANEL_BTN_PAD - LeftPanelButtonLeft(LEFT_W, 3, 1), BTN_H, BtnFMCTCSSToneClick); @@ -2518,12 +2515,12 @@ begin PanelFMStep.Visible := False; MakeLbl(PanelFMStep, 'STEP', 4, 2); BtnFMStep := MakeBtn(PanelFMStep, 'STEP', - LeftPanelButtonLeft(LEFT_W, 3, 0), 16, + LeftPanelButtonLeft(LEFT_W, 3, 0), 20, LeftPanelButtonWidth(LEFT_W, 3, 0), BTN_H, BtnFMStepClick); StyleButton(BtnFMStep, FController.FFMStepOn); BtnFMStepSel := MakeBtn(PanelFMStep, FM_STEP_NAMES[FController.FFMStepIdx], - LeftPanelButtonLeft(LEFT_W, 3, 1), 16, + LeftPanelButtonLeft(LEFT_W, 3, 1), 20, LEFT_W - LEFT_PANEL_BTN_PAD - LeftPanelButtonLeft(LEFT_W, 3, 1), BTN_H, BtnFMStepSelClick); @@ -2543,18 +2540,18 @@ begin PanelFMRpt.Visible := False; MakeLbl(PanelFMRpt, 'RPT', 4, 2); BtnFMRptMinus := MakeBtn(PanelFMRpt, '-', - LeftPanelButtonLeft(LEFT_W, 5, 0), 16, + LeftPanelButtonLeft(LEFT_W, 5, 0), 20, LeftPanelButtonWidth(LEFT_W, 5, 0), BTN_H, BtnFMRptMinusClick); StyleButton(BtnFMRptMinus, False); BtnFMRptPlus := MakeBtn(PanelFMRpt, '+', - LeftPanelButtonLeft(LEFT_W, 5, 4), 16, + LeftPanelButtonLeft(LEFT_W, 5, 4), 20, LeftPanelButtonWidth(LEFT_W, 5, 4), BTN_H, BtnFMRptPlusClick); StyleButton(BtnFMRptPlus, False); W := LeftPanelButtonLeft(LEFT_W, 5, 1); EdFMRptOffset := TFlatEdit.Create(Self); EdFMRptOffset.Parent := PanelFMRpt; EdFMRptOffset.Left := W; - EdFMRptOffset.Top := 18; + EdFMRptOffset.Top := 22; EdFMRptOffset.Width := LeftPanelButtonLeft(LEFT_W, 5, 4) - W - LEFT_PANEL_BTN_GAP; EdFMRptOffset.Height := BTN_H - 4; EdFMRptOffset.Text := RptFormatOffset(FController.FFMRptOffsetHz); @@ -3159,6 +3156,15 @@ procedure TMainForm.ApplyDarkTheme; var T: TAppTheme; i: Integer; procedure DP(P: TPanel); begin P.Color := T.Panel; P.Font.Color := T.Text; end; + procedure DPL(P: TPanel); + var + J: Integer; + begin + if P = nil then Exit; + for J := 0 to P.ControlCount - 1 do + if P.Controls[J] is TLabel then + TLabel(P.Controls[J]).Font.Color := T.TextDim; + end; procedure DL(L: TLabel; Dim: Boolean = True); begin if Dim then L.Font.Color := T.TextDim else L.Font.Color := T.Text; end; procedure DS(S: TFlatSlider); @@ -3177,6 +3183,7 @@ begin DP(PanelTopVfoGroup); DP(PanelTopVfoA); DP(PanelTopVfoB); DP(PanelVfoA); DP(PanelVfoB); DP(PanelVfoButtons); DP(PanelBands); DP(PanelMode); DP(PanelFilter); + if PanelPlutoAGC <> nil then DP(PanelPlutoAGC); if PanelFMSQ <> nil then DP(PanelFMSQ); if PanelRawDev <> nil then DP(PanelRawDev); if PanelFMCTCSS <> nil then DP(PanelFMCTCSS); @@ -3186,17 +3193,30 @@ begin DP(PanelRXBlock); DP(PanelAGC); DP(PanelTX); DP(PanelRight); - if PbSpectrum is TPaintBox then - TPaintBox(PbSpectrum).Color := T.BG; - if PbWaterfall is TPaintBox then - TPaintBox(PbWaterfall).Color := T.BG; if PanelSMeterRight <> nil then PanelSMeterRight.Color := T.Panel; if PbSMeterRight <> nil then PbSMeterRight.Color := T.Panel; ApplyStatusTheme(T); - // Метки + // Заголовки групп и подписи строк. + DPL(PanelTX); + DPL(PanelRXBlock); + DPL(PanelAGC); + DPL(PanelPlutoAGC); + DPL(PanelBands); + DPL(PanelMode); + DPL(PanelFilter); + DPL(PanelRawDev); + DPL(PanelFMStep); + DPL(PanelFMSQ); + DPL(PanelFMCTCSS); + DPL(PanelFMRpt); + // Значения справа от ползунков ярче обычных подписей. DL(LblAGCTop, False); DL(LblDrv); + DL(LblDrvVal, False); + DL(LblAttVal, False); + DL(LblRxGain, False); + DL(LblRawDev, False); if LblFMSQ <> nil then DL(LblFMSQ, False); // Кнопки тулбара @@ -3208,23 +3228,39 @@ begin BtnDiscover.ClrTextAct := T.TbDiscoverText; BtnDiscover.Invalidate; BtnStartStop.ClrNorm := T.TbStartNorm; + BtnStartStop.ClrActive := T.TbStartNorm; + BtnStartStop.ClrHot := T.TbStartNorm; BtnStartStop.ClrBorder := T.TbStartBorder; BtnStartStop.ClrText := T.TbStartText; + BtnStartStop.ClrTextAct:= T.TbStartText; BtnStartStop.Invalidate; + StyleButton(BtnSettings, False); // Обычные кнопки — все массивы for i := 0 to BAND_COUNT-1 do StyleButton(BtnBand[i], BtnBand[i].Active); + for i := 0 to CFG_XVTR_COUNT - 1 do + if BtnXvtrBand[i] <> nil then + StyleButton(BtnXvtrBand[i], BtnXvtrBand[i].Active); for i := 0 to MODE_COUNT-1 do StyleButton(BtnMode[i], BtnMode[i].Active); for i := 0 to FILT_SLOTS-1 do StyleButton(BtnFilter[i], BtnFilter[i].Active); for i := 0 to 4 do StyleButton(BtnAGCMode[i],BtnAGCMode[i].Active); StyleButton(BtnCTun, BtnCTun.Active); StyleButton(BtnDUP, BtnDUP.Active); + StyleButton(BtnPS, BtnPS.Active); + StyleButton(Btn2Ton, Btn2Ton.Active); + StyleButton(BtnRxMute, BtnRxMute.Active); + StyleButton(BtnBeacon, BtnBeacon.Active); if BtnFMSQ <> nil then StyleButton(BtnFMSQ, BtnFMSQ.Active); if TrkFMSQ <> nil then DS(TrkFMSQ); if BtnFMCTCSS <> nil then StyleButton(BtnFMCTCSS, BtnFMCTCSS.Active); if BtnFMCTCSSTone <> nil then StyleButton(BtnFMCTCSSTone, False); + if BtnFMStep <> nil then StyleButton(BtnFMStep, BtnFMStep.Active); + if BtnFMStepSel <> nil then StyleButton(BtnFMStepSel, False); if BtnFMRptMinus <> nil then StyleButton(BtnFMRptMinus, BtnFMRptMinus.Active); if BtnFMRptPlus <> nil then StyleButton(BtnFMRptPlus, BtnFMRptPlus.Active); + for i := 0 to 3 do + if BtnPlutoGain[i] <> nil then + StyleButton(BtnPlutoGain[i], BtnPlutoGain[i].Active); if FCTCSSDropDown <> nil then FCTCSSDropDown.ApplyStyle(StyleButton, T.Panel); if FStepDropDown <> nil then FStepDropDown.ApplyStyle(StyleButton, T.Panel); if BtnZoomIn <> nil then StyleButton(BtnZoomIn, False); @@ -3234,6 +3270,15 @@ begin for i := 0 to MAX_PANS - 1 do if FPans[i] <> nil then begin + FPans[i].View.SetTheme(T); + if FPans[i].PbSpectrum is TPaintBox then + TPaintBox(FPans[i].PbSpectrum).Color := T.BG; + if FPans[i].PbWaterfall is TPaintBox then + TPaintBox(FPans[i].PbWaterfall).Color := T.BG; + if FPans[i].Splitter <> nil then FPans[i].Splitter.Color := T.Border; + if FPans[i].BtnZoomIn <> nil then StyleButton(FPans[i].BtnZoomIn, False); + if FPans[i].BtnZoomDef <> nil then StyleButton(FPans[i].BtnZoomDef, False); + if FPans[i].BtnZoomOut <> nil then StyleButton(FPans[i].BtnZoomOut, False); if FPans[i].BtnAddPan <> nil then StyleButton(FPans[i].BtnAddPan, False); if FPans[i].BtnGrid <> nil then StyleButton(FPans[i].BtnGrid, False); if FPans[i].BtnPopPan <> nil then StyleButton(FPans[i].BtnPopPan, False); @@ -3250,6 +3295,8 @@ begin if FPans[i].ZoomBar <> nil then FPans[i].ZoomBar.SetTheme(T); if FPans[i].PbPanZoom <> nil then FPans[i].PbPanZoom.Color := T.SliderBG; end; + // Тема инвалидирует кэши и GL-текстуры сама; историю водопада сохраняем. + MarkPanDisplayDirty(FPans[i], False); end; if FPanZoomBar <> nil then begin @@ -3275,9 +3322,9 @@ begin // Ползунки DS(TrkAGC); DS(TrkVolume); DS(TrkDrive); + DS(TrkAtt); DS(TrkRxGain); DS(TrkRawDev); - // Спектр - FSpecView.SetTheme(T); + // Wideband не входит в стек обычных панов. if Assigned(FWidebandView) then FWidebandView.SetTheme(T); // Флаги слайсов (оверлеи) — цвета кнопок из темы, как левая панель. @@ -3325,11 +3372,17 @@ begin end; procedure TMainForm.SetLightTheme(V: Boolean); +var + T: TAppTheme; begin if FLightTheme = V then Exit; FLightTheme := V; FController.FLoadedGlobal.LightTheme := V; // зеркало в persist-буфер контроллера ApplyDarkTheme; + T := CurrentAppTheme; + if FDeviceDialog <> nil then FDeviceDialog.SetTheme(T); + if FChannelsForm <> nil then TChannelsForm(FChannelsForm).ApplyTheme(T); + if FBeaconScopeForm <> nil then FBeaconScopeForm.ApplyTheme(T); FController.FSettings.SaveTheme(V); FController.FSettings.Save; if (FSettingsForm <> nil) and TSettingsForm(FSettingsForm).Visible then @@ -3348,7 +3401,6 @@ begin else B.ClrBorder := T.BtnBorderNorm; B.ClrText := T.BtnText; B.ClrTextAct := T.BtnTextActive; - B.Font.Name := 'Courier New'; B.Font.Size := 8; end; @@ -3363,8 +3415,7 @@ begin B.ClrBorder := T.SpanBorder; B.ClrText := T.SpanText; B.ClrTextAct := T.SpanText; - B.Font.Name := 'Courier New'; - B.Font.Size := 7; + B.Font.Size := 8; end; procedure TMainForm.PositionSampleRateOverlay; @@ -4568,11 +4619,15 @@ end; procedure TMainForm.LayoutFilterButtons(PresetCount: Integer); var i, Total, Cols, Rows, RowOf, ColOf, InRow: Integer; + HeaderH, RowStep, BottomPad: Integer; begin Total := PresetCount + 2; // + VAR1/VAR2 if Total <= FILT_GRID_COLS then Cols := Total // FM/WFM: всё в один ряд else Cols := FILT_GRID_COLS; Rows := (Total + Cols - 1) div Cols; + HeaderH := PanelFilter.Scale96ToForm(20); + RowStep := PanelFilter.Scale96ToForm(27); + BottomPad := PanelFilter.Scale96ToForm(4); for i := 0 to FILT_SLOTS - 1 do begin if i < PresetCount then InRow := i // пресет на своём месте @@ -4583,12 +4638,12 @@ begin ColOf := InRow mod Cols; BtnFilter[i].Left := LeftPanelButtonLeft(PanelFilter.Width, Cols, ColOf); BtnFilter[i].Width := LeftPanelButtonWidth(PanelFilter.Width, Cols, ColOf); - BtnFilter[i].Top := 16 + RowOf * 27; + BtnFilter[i].Top := HeaderH + RowOf * RowStep; BtnFilter[i].Caption := FController.FilterSlot(FController.FMode, i).Name; BtnFilter[i].Visible := True; StyleButton(BtnFilter[i], i = FController.FFilter); end; - PanelFilter.Height := 16 + Rows * 27 + 4; + PanelFilter.Height := HeaderH + Rows * RowStep + BottomPad; end; procedure TMainForm.UpdateFilterButtons; @@ -4634,7 +4689,7 @@ begin // Non-FM: restore full 2-row height and 5-column layout, hide FM panels. // DMR reuses only the tuning STEP panel; SQL/CTCSS/repeater stay FM-only. - PanelFilter.Height := 74; + PanelFilter.Height := PanelFilter.Scale96ToForm(74); if PanelFMSQ <> nil then PanelFMSQ.Visible := False; if PanelFMCTCSS <> nil then PanelFMCTCSS.Visible := False; if PanelFMStep <> nil then @@ -4691,7 +4746,7 @@ begin // Прочие режимы: смена режима возвращает заводской индекс фильтра (как было // до таблицы), кромки и подписи приезжают из таблицы контроллера. - PanelFilter.Height := 74; + PanelFilter.Height := PanelFilter.Scale96ToForm(74); DefIdx := FilterDefIdx(FController.FMode); FController.SelectFilterSlotState(DefIdx); LayoutFilterButtons(FILT_COUNT); @@ -4740,7 +4795,7 @@ begin begin if FBeaconScopeForm = nil then FBeaconScopeForm := TBeaconScopeForm.CreateWith(Self, FController); - FBeaconScopeForm.ApplyTheme(DarkTheme); + FBeaconScopeForm.ApplyTheme(CurrentAppTheme); FBeaconScopeForm.Show; end; UpdateBeaconButton; @@ -4755,7 +4810,7 @@ begin if not FController.BeaconLockEnabled then Exit; if FBeaconScopeForm = nil then FBeaconScopeForm := TBeaconScopeForm.CreateWith(Self, FController); - FBeaconScopeForm.ApplyTheme(DarkTheme); + FBeaconScopeForm.ApplyTheme(CurrentAppTheme); if FBeaconScopeForm.Visible then FBeaconScopeForm.Hide else FBeaconScopeForm.Show; end; @@ -5712,7 +5767,7 @@ begin Def.Power := 100; Def.Name := ''; TChannelsForm(FChannelsForm).SetCurrentDefaults(Def); - TChannelsForm(FChannelsForm).ApplyTheme(DarkTheme); + TChannelsForm(FChannelsForm).ApplyTheme(CurrentAppTheme); TChannelsForm(FChannelsForm).Show; end; @@ -8239,7 +8294,7 @@ end; procedure TMainForm.RebuildXvtrButtons; var i, Pos, XvtrRows: Integer; - BtnH, RowH, MaxBottom: Integer; + BtnH, RowH, MaxBottom, RowGap, TopGap, BottomPad: Integer; B: TFlatButton; Cap: string; begin @@ -8264,7 +8319,10 @@ begin BtnH := BtnBand[i].Height; end; if BtnH = 0 then BtnH := DpiScale(24); - RowH := BtnH + 3; // шаг ряда: высота кнопки + 3px зазор (как 27=24+3 при 1x) + RowGap := PanelBands.Scale96ToForm(3); + TopGap := PanelBands.Scale96ToForm(4); + BottomPad := PanelBands.Scale96ToForm(6); + RowH := BtnH + RowGap; Pos := 0; // позиция среди enabled слотов for i := 0 to CFG_XVTR_COUNT - 1 do @@ -8277,7 +8335,7 @@ begin B.Parent := PanelBands; B.Caption := Cap; B.Left := LeftPanelButtonLeft(PanelBands.Width, 6, Pos mod 6); - B.Top := MaxBottom + 4 + (Pos div 6) * RowH; + B.Top := MaxBottom + TopGap + (Pos div 6) * RowH; B.Width := LeftPanelButtonWidth(PanelBands.Width, 6, Pos mod 6); B.Height := BtnH; B.OnClick := BtnXvtrBandClick; @@ -8291,9 +8349,9 @@ begin else if Pos <= 6 then XvtrRows := 1 else XvtrRows := 2; if XvtrRows = 0 then - PanelBands.Height := MaxBottom + 6 + PanelBands.Height := MaxBottom + BottomPad else - PanelBands.Height := MaxBottom + 4 + XvtrRows * RowH + 6; + PanelBands.Height := MaxBottom + TopGap + XvtrRows * RowH + BottomPad; RelayoutBelowBands; end;