mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 19:45:09 +00:00
feat(overlay): flat themed buttons + full theme support for slice flags
Share one button paint routine: extract TFlatButton.Paint into PaintFlatButton(canvas, rect, colors) and call it from both TFlatButton and VfoOverlay.DrawButton — flag buttons now render identically to the left panel (flat fill + border, no gloss), using theme Btn* colors. Theme the whole flag card: the structural CLR_* palette (card bg, border, text, dim, accents, freq, meter bg) is now derived from the active TAppTheme via ApplyThemeColors, so flags follow light/dark like the rest of the UI. Status accents (TX/SPLIT) stay fixed on purpose and use a fixed dark text over their bright badges. Theme is pushed to flags from ApplyDarkTheme and on creation via WireOverlayEvents. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+55
-68
@@ -17,7 +17,7 @@ unit VfoOverlay;
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, Controls, Graphics, Types, Math;
|
||||
Classes, SysUtils, Controls, Graphics, Types, Math, FlatButton, AppTheme;
|
||||
|
||||
const
|
||||
VFO_OVERLAY_ALPHA = 210;
|
||||
@@ -88,6 +88,12 @@ type
|
||||
FOnTxSelect: TNotifyEvent;
|
||||
FOnInvalidate: TNotifyEvent;
|
||||
|
||||
FTheme: TAppTheme; // тема приложения (цвета кнопок и т.п.)
|
||||
// Структурные цвета карточки, выведенные из FTheme (ApplyThemeColors).
|
||||
CLR_CARD_BG, CLR_BORDER, CLR_SEP, CLR_TEXT, CLR_DIM,
|
||||
CLR_ACCENT, CLR_ACCENT_D, CLR_BTN_NORM, CLR_BTN_HOT,
|
||||
CLR_FREQ, CLR_SVAL, CLR_BAR_BG: TColor;
|
||||
|
||||
FSliceId: Integer; // логический id слайса (владелец — MainForm/контроллер)
|
||||
FIsMain: Boolean; // главный приёмник (слайс A) — без кнопки закрытия
|
||||
FOnSliceSelect: TNotifyEvent; // клик по бейджу буквы — сделать слайс активным
|
||||
@@ -116,6 +122,7 @@ type
|
||||
procedure DrawDspPanel(C: TCanvas; X, Y, RowW: Integer);
|
||||
procedure DrawAudPanel(C: TCanvas; X, Y, RowW: Integer);
|
||||
procedure RegisterHit(const HR: TRect; HV: Integer);
|
||||
procedure ApplyThemeColors; // раскладывает FTheme → структурные CLR_*
|
||||
function FormatFreq(Hz: Double): string;
|
||||
function GetFilterBWForMode(ModeIdx, FilterIdx: Integer): Integer;
|
||||
function GetFilterLblForMode(ModeIdx, FilterIdx: Integer): string;
|
||||
@@ -157,6 +164,8 @@ type
|
||||
const CurName: string);
|
||||
procedure UpdateSMeter(DB: Double);
|
||||
procedure UpdateVfo(AVfoHz: Double);
|
||||
// Тема приложения (цвета кнопок берутся отсюда — как левая панель).
|
||||
procedure SetTheme(const T: TAppTheme);
|
||||
// Привязка к слайсу: id, буква флага, признак главного (слайс A).
|
||||
procedure SetSliceInfo(ASliceId: Integer; ALetter: Char; AIsMain: Boolean);
|
||||
property SliceId: Integer read FSliceId;
|
||||
@@ -237,22 +246,14 @@ const
|
||||
AUD_MAX_VIS = 6; // максимум видимых устройств (дальше — прокрутка)
|
||||
OVL_BASE_H = PAD + HDR_H + MET_H + FRQ_H + BAR_H + PAD; // = 96
|
||||
|
||||
// Палитра
|
||||
CLR_CARD_BG = TColor($00191919);
|
||||
CLR_BORDER = TColor($00484848);
|
||||
CLR_SEP = TColor($00303030);
|
||||
CLR_TEXT = TColor($00E4E4E4);
|
||||
CLR_DIM = TColor($00707070);
|
||||
CLR_ACCENT = TColor($0040FF80); // зелёный акцент
|
||||
CLR_ACCENT_D = TColor($00103018); // тёмный акцент (фон активной)
|
||||
CLR_BTN_NORM = TColor($00262626);
|
||||
CLR_BTN_HOT = TColor($00383838);
|
||||
CLR_FREQ = TColor($0050FF80);
|
||||
CLR_SVAL = TColor($0030C8FF);
|
||||
CLR_BAR_BG = TColor($00101010);
|
||||
CLR_TX_ON = TColor($000030E0); // красный TX идёт (BGR)
|
||||
CLR_TX_SEL = TColor($0010A8F0); // янтарный: слайс выбран для передачи
|
||||
CLR_SPLIT_ON = TColor($0020C0FF); // оранжевый SPLIT
|
||||
// Палитра. Структурные цвета (фон/текст/акценты) берутся из темы —
|
||||
// см. поля CLR_* класса + ApplyThemeColors. Здесь — только статус-акценты,
|
||||
// которые намеренно одинаковы в обеих темах (яркие бейджи-статусы), и
|
||||
// тёмный текст поверх них.
|
||||
CLR_TX_ON = TColor($000030E0); // красный TX идёт (BGR)
|
||||
CLR_TX_SEL = TColor($0010A8F0); // янтарный: слайс выбран для передачи
|
||||
CLR_SPLIT_ON = TColor($0020C0FF); // оранжевый SPLIT
|
||||
CLR_BADGE_TXT = TColor($00181818); // тёмный текст поверх ярких бейджей
|
||||
|
||||
OVL_MODE_COUNT = 8;
|
||||
OVL_MODE_NAMES: array[0..7] of string = (
|
||||
@@ -407,11 +408,38 @@ begin
|
||||
FCacheDirty := True;
|
||||
FMeterDirty := False;
|
||||
FLastSTextRect := Rect(0, 0, 0, 0);
|
||||
FTheme := DarkTheme; // по умолчанию тёмная; MainForm выставит актуальную
|
||||
ApplyThemeColors;
|
||||
Width := OVL_W;
|
||||
Height := OVL_BASE_H;
|
||||
Cursor := crHandPoint;
|
||||
end;
|
||||
|
||||
// Раскладывает тему в структурные цвета карточки. Статус-акценты (TX/SPLIT)
|
||||
// намеренно НЕ тематизируются — они одинаковы в обеих темах.
|
||||
procedure TVfoOverlay.ApplyThemeColors;
|
||||
begin
|
||||
CLR_CARD_BG := FTheme.Panel;
|
||||
CLR_BORDER := FTheme.Border;
|
||||
CLR_SEP := FTheme.Border;
|
||||
CLR_TEXT := FTheme.Text;
|
||||
CLR_DIM := FTheme.TextDim;
|
||||
CLR_ACCENT := FTheme.BtnTextActive; // зелёный акцент (яркий/тёмный по теме)
|
||||
CLR_ACCENT_D := FTheme.BtnActive; // фон активной строки
|
||||
CLR_BTN_NORM := FTheme.BtnNorm;
|
||||
CLR_BTN_HOT := FTheme.BtnHot;
|
||||
CLR_FREQ := FTheme.FreqActive;
|
||||
CLR_SVAL := FTheme.Amber; // числовое значение S-метра
|
||||
CLR_BAR_BG := FTheme.BG; // фон полоски S-метра
|
||||
end;
|
||||
|
||||
procedure TVfoOverlay.SetTheme(const T: TAppTheme);
|
||||
begin
|
||||
FTheme := T;
|
||||
ApplyThemeColors;
|
||||
RequestInvalidate; // перерисовать хром флага с новыми цветами
|
||||
end;
|
||||
|
||||
destructor TVfoOverlay.Destroy;
|
||||
begin
|
||||
FDevNames.Free;
|
||||
@@ -522,61 +550,20 @@ end;
|
||||
|
||||
{ ---- Примитивы отрисовки ---- }
|
||||
|
||||
// Масштаб яркости цвета (для стеклянного градиента/фаски).
|
||||
function GlossColor(Base: TColor; F: Double): TColor;
|
||||
var
|
||||
R, G, B: Byte;
|
||||
begin
|
||||
RedGreenBlue(ColorToRGB(Base), R, G, B);
|
||||
Result := RGBToColor(EnsureRange(Round(R * F), 0, 255),
|
||||
EnsureRange(Round(G * F), 0, 255),
|
||||
EnsureRange(Round(B * F), 0, 255));
|
||||
end;
|
||||
|
||||
procedure TVfoOverlay.DrawButton(C: TCanvas; const R: TRect; const Lbl: string;
|
||||
Active, Hot: Boolean; FontSz: Integer);
|
||||
var
|
||||
Base: TColor;
|
||||
Y, H: Integer;
|
||||
RowT, Gloss: Double;
|
||||
Brd: TColor;
|
||||
begin
|
||||
if Active then Base := CLR_ACCENT
|
||||
else if Hot then Base := CLR_BTN_HOT
|
||||
else Base := CLR_BTN_NORM;
|
||||
|
||||
// Скруглённая тёмная база = форма + рамка-фаска.
|
||||
C.Brush.Color := GlossColor(Base, 0.58);
|
||||
C.Brush.Style := bsSolid;
|
||||
C.Pen.Color := GlossColor(Base, 0.42);
|
||||
C.Pen.Style := psSolid;
|
||||
C.RoundRect(R.Left, R.Top, R.Right, R.Bottom, 5, 5);
|
||||
|
||||
// Стеклянный вертикальный градиент: ярче вверху, темнее внизу (как ползунок зума).
|
||||
H := R.Bottom - R.Top;
|
||||
C.Pen.Style := psClear;
|
||||
for Y := R.Top + 1 to R.Bottom - 2 do
|
||||
begin
|
||||
if H > 3 then RowT := (Y - (R.Top + 1)) / (H - 3) else RowT := 0.0;
|
||||
Gloss := 1.30 - 0.60 * RowT;
|
||||
C.Brush.Color := GlossColor(Base, Gloss);
|
||||
C.FillRect(Rect(R.Left + 2, Y, R.Right - 2, Y + 1));
|
||||
end;
|
||||
|
||||
// Блик сверху + тень снизу (объём).
|
||||
C.Pen.Style := psSolid;
|
||||
C.Pen.Color := GlossColor(Base, 1.55);
|
||||
C.Line(R.Left + 2, R.Top + 1, R.Right - 2, R.Top + 1);
|
||||
C.Pen.Color := GlossColor(Base, 0.40);
|
||||
C.Line(R.Left + 2, R.Bottom - 2, R.Right - 2, R.Bottom - 2);
|
||||
|
||||
if Active then C.Font.Color := CLR_CARD_BG
|
||||
else C.Font.Color := CLR_TEXT;
|
||||
// Цвета берём из темы (как StyleButton на левой панели): рамка — по состоянию.
|
||||
if Active then Brd := FTheme.BtnBorderActive else Brd := FTheme.BtnBorderNorm;
|
||||
C.Font.Name := 'Courier New';
|
||||
C.Font.Size := FontSz;
|
||||
C.Font.Style := [];
|
||||
C.Brush.Style := bsClear;
|
||||
C.TextOut(R.Left + (R.Right - R.Left - C.TextWidth(Lbl)) div 2,
|
||||
R.Top + (R.Bottom - R.Top - C.TextHeight('A')) div 2, Lbl);
|
||||
// Общая отрисовка (FlatButton) — вид как у кнопок по всей программе.
|
||||
PaintFlatButton(C, R, Lbl, Active, Hot,
|
||||
FTheme.BtnNorm, FTheme.BtnActive, FTheme.BtnHot, Brd,
|
||||
FTheme.BtnText, FTheme.BtnTextActive);
|
||||
end;
|
||||
|
||||
procedure TVfoOverlay.DrawIndicator(C: TCanvas; X, Y: Integer; const Lbl: string;
|
||||
@@ -917,7 +904,7 @@ begin
|
||||
CurY := PAD;
|
||||
|
||||
{ --- Шапка: [A] BW ... [×] SPLIT [TX] --- }
|
||||
DrawBadge(C, Rect(X, CurY, X+16, CurY+HDR_BADGE_H), FSlice, SliceColor(FSlice), CLR_CARD_BG);
|
||||
DrawBadge(C, Rect(X, CurY, X+16, CurY+HDR_BADGE_H), FSlice, SliceColor(FSlice), CLR_BADGE_TXT);
|
||||
RegisterHit(Rect(X, CurY, X+16, CurY+HDR_BADGE_H), HIT_SLICE_SEL); // бейдж = выбор слайса
|
||||
// Кнопка закрытия × — только у неглавных слайсов (главный приёмник не закрыть).
|
||||
if not FIsMain then
|
||||
@@ -936,12 +923,12 @@ begin
|
||||
// передача идёт → красный; слайс выбран для TX → янтарный; иначе → нейтральный.
|
||||
R := Rect(W - PAD - 26, CurY, W - PAD, CurY + HDR_BADGE_H);
|
||||
if FTx then DrawBadge(C, R, 'TX', CLR_TX_ON, clWhite)
|
||||
else if FTxSel then DrawBadge(C, R, 'TX', CLR_TX_SEL, CLR_CARD_BG)
|
||||
else if FTxSel then DrawBadge(C, R, 'TX', CLR_TX_SEL, CLR_BADGE_TXT)
|
||||
else DrawBadge(C, R, 'TX', CLR_BTN_NORM, CLR_DIM);
|
||||
RegisterHit(R, HIT_TX);
|
||||
// SPLIT (левее TX) — кликабельно
|
||||
R := Rect(W - PAD - 26 - 44, CurY, W - PAD - 26 - 4, CurY + HDR_BADGE_H);
|
||||
if FSplit then DrawBadge(C, R, 'SPLIT', CLR_SPLIT_ON, CLR_CARD_BG)
|
||||
if FSplit then DrawBadge(C, R, 'SPLIT', CLR_SPLIT_ON, CLR_BADGE_TXT)
|
||||
else DrawBadge(C, R, 'SPLIT', CLR_BTN_NORM, CLR_DIM);
|
||||
RegisterHit(R, HIT_SPLIT);
|
||||
Inc(CurY, HDR_H);
|
||||
|
||||
Reference in New Issue
Block a user