diff --git a/MainForm.pas b/MainForm.pas index 3220a30..d1ae0a0 100644 --- a/MainForm.pas +++ b/MainForm.pas @@ -149,6 +149,9 @@ type // RadioController.pas. Поля состояния/движков теперь живут в // FController; обращения идут как FController.FXxx. ---- FController: TRadioController; + // True пока OnControllerState обновляет виджеты — чтобы программное + // изменение слайдера/кнопки не вызывало повторно команду из OnChange. + FSyncingFromController: Boolean; // ---- Network ---- FDevices: array of TDeviceItem; @@ -368,6 +371,7 @@ type // ---- Helpers ---- procedure DoInvoke(M: TThreadMethod); // маршалинг команд контроллера в UI-поток + procedure OnControllerState(Sender: TObject; Field: TRadioField); // контроллер → UI procedure BuildUI; function LeftPanelButtonLeft(PanelWidth, ColCount, ColIndex: Integer): Integer; function LeftPanelButtonWidth(PanelWidth, ColCount, ColIndex: Integer): Integer; @@ -1065,6 +1069,7 @@ begin // освобождаются здесь же (в полях FController) — Фаза 1 миграции. FController := TRadioController.Create; FController.OnInvoke := DoInvoke; + FController.OnStateChanged := OnControllerState; FController.FVfoA := 14200000; FController.FVfoB := 7100000; FController.FActiveVfo := 0; @@ -1410,6 +1415,28 @@ begin TThread.Synchronize(nil, M); end; +// Контроллер изменил состояние — обновляем соответствующие виджеты. +// Выполняется в UI-потоке (команды контроллера вызываются из UI-потока либо +// маршалятся адаптерами). FSyncingFromController защищает от рекурсии, когда +// программное изменение виджета триггерит его OnChange. +procedure TMainForm.OnControllerState(Sender: TObject; Field: TRadioField); +begin + FSyncingFromController := True; + try + case Field of + rfMute: + begin + StyleButton(BtnMute, FController.FMuted); + if FController.FMuted then BtnMute.Caption := 'UNMUTE' + else BtnMute.Caption := 'MUTE'; + BtnMute.Invalidate; + end; + end; + finally + FSyncingFromController := False; + end; +end; + procedure TMainForm.FormClose(Sender: TObject; var CloseAction: TCloseAction); begin // Сохраняем позицию здесь — окно ещё полностью видимо и стабильно. @@ -5830,11 +5857,8 @@ end; procedure TMainForm.BtnMuteClick(Sender: TObject); begin - FController.FMuted := not FController.FMuted; - StyleButton(BtnMute, FController.FMuted); - if FController.FMuted then BtnMute.Caption := 'UNMUTE' - else BtnMute.Caption := 'MUTE'; - if FController.FWDSPReady then FController.FDSPEngine.SetMute(FController.FMuted); + if FSyncingFromController then Exit; + FController.ToggleMute; // логика (движок) + OnControllerState обновит кнопку end; procedure TMainForm.UpdateNRButton; @@ -6430,8 +6454,7 @@ end; procedure TMainForm.SyncWebMute; begin - if FWebSyncBool <> FController.FMuted then - BtnMuteClick(nil); + FController.SetMute(FWebSyncBool); // идемпотентно; OnControllerState обновит UI end; // ── CTUN ────────────────────────────────────────────────────────────────── diff --git a/RadioController.pas b/RadioController.pas index b845b50..4be8cda 100644 --- a/RadioController.pas +++ b/RadioController.pas @@ -397,7 +397,11 @@ procedure TRadioController.VolumeBy(Delta: Integer); begin SetVolume(FVolume + Delta); end; procedure TRadioController.SetMute(On_: Boolean); -begin FMuted := On_; Changed(rfMute); { TODO wiring } end; +begin + FMuted := On_; + if FWDSPReady and Assigned(FDSPEngine) then FDSPEngine.SetMute(FMuted); + Changed(rfMute); +end; procedure TRadioController.ToggleMute; begin SetMute(not FMuted); end;