@@ -131,47 +131,26 @@ class CloseButton : public QWidget
131131
132132InfoWindow::InfoWindow (QWidget *parent) : QDialog{parent}
133133{
134- _ui.setupUi (this );
135-
136- setFixedSize (300 , 300 );
137- setAttribute (Qt::WA_DeleteOnClose);
138- setWindowFlags (windowFlags () | Qt::Tool | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);
139- Utils::Qt::SetRoundedCorners (this , 30 );
140-
141- auto palette = this ->palette ();
142- palette.setColor (QPalette::Window, Qt::white);
143- setPalette (palette);
144-
145134 qRegisterMetaType<Core::AirPods::State>(" Core::AirPods::State" );
146135
147136 _closeButton = new CloseButton{this };
148- _leftBattery = new Widget::Battery{this };
149- _rightBattery = new Widget::Battery{this };
150- _caseBattery = new Widget::Battery{this };
151137
152- _leftBattery->hide ();
153- _rightBattery->hide ();
154- _caseBattery->hide ();
155-
156- auto deviceLabelPalette = _ui.deviceLabel ->palette ();
157- deviceLabelPalette.setColor (QPalette::WindowText, QColor{94 , 94 , 94 });
158- _ui.deviceLabel ->setPalette (deviceLabelPalette);
138+ _ui.setupUi (this );
159139
160- InitCommonButton ();
140+ setFixedSize (300 , 300 );
141+ setAttribute (Qt::WA_DeleteOnClose);
142+ setWindowFlags (windowFlags () | Qt::Tool | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);
161143
162- _posAnimation.setDuration (500 );
163- _autoHideTimer->callOnTimeout ([this ] { DoHide (); });
144+ Utils::Qt::SetRoundedCorners (this , 30 );
145+ Utils::Qt::SetRoundedCorners (_ui.pushButton , 6 );
146+ Utils::Qt::SetPaletteColor (this , QPalette::Window, Qt::white);
147+ Utils::Qt::SetPaletteColor (_ui.deviceLabel , QPalette::WindowText, QColor{94 , 94 , 94 });
164148
149+ connect (qApp, &QGuiApplication::applicationStateChanged, this , &InfoWindow::OnAppStateChanged);
150+ connect (_ui.pushButton , &QPushButton::clicked, this , &InfoWindow::OnButtonClicked);
165151 connect (&_posAnimation, &QPropertyAnimation::finished, this , &InfoWindow::OnPosMoveFinished);
166152 connect (_closeButton, &CloseButton::Clicked, this , &InfoWindow::DoHide);
167- connect (qApp, &QGuiApplication::applicationStateChanged, this , &InfoWindow::OnAppStateChanged);
168-
169- // for loop play
170- connect (_mediaPlayer, &QMediaPlayer::stateChanged, [this ](QMediaPlayer::State newState) {
171- if (newState == QMediaPlayer::StoppedState && _isAnimationPlaying) {
172- _mediaPlayer->play ();
173- }
174- });
153+ connect (_mediaPlayer, &QMediaPlayer::stateChanged, this , &InfoWindow::OnPlayerStateChanged);
175154
176155 connect (this , &InfoWindow::UpdateStateSafety, this , &InfoWindow::UpdateState);
177156 connect (this , &InfoWindow::AvailableSafety, this , &InfoWindow::Available);
@@ -182,13 +161,14 @@ InfoWindow::InfoWindow(QWidget *parent) : QDialog{parent}
182161 connect (this , &InfoWindow::ShowSafety, this , &InfoWindow::show);
183162 connect (this , &InfoWindow::HideSafety, this , &InfoWindow::DoHide);
184163
164+ _posAnimation.setDuration (500 );
165+ _autoHideTimer->callOnTimeout ([this ] { DoHide (); });
185166 _mediaPlayer->setMuted (true );
186167 _mediaPlayer->setVideoOutput (_videoWidget);
187168 _videoWidget->setAspectRatioMode (Qt::IgnoreAspectRatio);
188169 _videoWidget->show ();
189170
190171 _ui.layoutAnimation ->addWidget (_videoWidget);
191-
192172 _ui.layoutPods ->addWidget (_leftBattery);
193173 _ui.layoutPods ->addWidget (_rightBattery);
194174 _ui.layoutCase ->addWidget (_caseBattery);
@@ -198,126 +178,6 @@ InfoWindow::InfoWindow(QWidget *parent) : QDialog{parent}
198178 CheckUpdate ();
199179}
200180
201- void InfoWindow::CheckUpdate ()
202- {
203- // SPDLOG_TRACE("CheckUpdate: Poll.");
204-
205- const auto &DispatchNext = []() {
206- Utils::Qt::Dispatch (&CheckUpdate);
207- };
208-
209- using OptReleaseInfo = std::optional<Core::Update::ReleaseInfo>;
210- static std::promise<OptReleaseInfo> pmsRelease;
211- static std::optional<std::future<OptReleaseInfo>> optFuture;
212-
213- if (!optFuture.has_value ()) {
214- SPDLOG_INFO (" CheckUpdate: Prepare promise and future. Fetch release info async." );
215- pmsRelease = decltype (pmsRelease){};
216- optFuture = pmsRelease.get_future ();
217- std::thread{[]() { pmsRelease.set_value (Core::Update::FetchUpdateRelease ()); }}.detach ();
218- DispatchNext ();
219- return ;
220- }
221-
222- if (!Helper::IsFutureReady (optFuture.value ())) {
223- // SPDLOG_TRACE("CheckUpdate: The future is not ready.");
224- DispatchNext ();
225- return ;
226- }
227-
228- auto optRelease = optFuture->get ();
229- optFuture.reset ();
230- SPDLOG_INFO (" CheckUpdate: Fetch release info successfully." );
231-
232- do {
233- if (!optRelease.has_value ()) {
234- break ;
235- }
236-
237- auto releaseInfo = std::move (optRelease.value ());
238- auto releaseVersion = releaseInfo.version .toString ();
239-
240- QString changeLogBlock;
241- if (!releaseInfo.changeLog .isEmpty ()) {
242- changeLogBlock =
243- QString{" %1\n %2\n\n " }.arg (tr (" Change log:" )).arg (releaseInfo.changeLog );
244- }
245-
246- auto button = QMessageBox::question (
247- nullptr , Config::ProgramName,
248- tr (" Hey! I found a new version available!\n "
249- " \n "
250- " Current version: %1\n "
251- " Latest version: %2\n "
252- " \n "
253- " %3"
254- " Click \" Ignore\" to skip this new version.\n "
255- " \n "
256- " Do you want to update it now?" )
257- .arg (Core::Update::GetLocalVersion ().toString ())
258- .arg (releaseVersion)
259- .arg (changeLogBlock),
260- QMessageBox::Yes | QMessageBox::No | QMessageBox::Ignore, QMessageBox::Yes);
261-
262- if (button == QMessageBox::Yes) {
263- SPDLOG_INFO (" CheckUpdate: User clicked Yes." );
264-
265- if (!releaseInfo.CanAutoUpdate ()) {
266- SPDLOG_INFO (" CheckUpdate: Popup latest url and quit." );
267- QDesktopServices::openUrl (QUrl{releaseInfo.url });
268- ApdApplication::QuitSafety ();
269- break ;
270- }
271-
272- Gui::DownloadWindow{std::move (releaseInfo)}.exec ();
273- }
274- else if (button == QMessageBox::Ignore) {
275- SPDLOG_INFO (" CheckUpdate: User clicked Ignore." );
276-
277- Core::Settings::ModifiableAccess ()->skipped_version = releaseVersion;
278- }
279- else {
280- SPDLOG_INFO (" CheckUpdate: User clicked No." );
281- }
282-
283- } while (false );
284-
285- // TODO: Enable this timer after the icon notification is implemented
286- //
287- // QTimer::singleShot(1h, &CheckUpdate);
288- }
289-
290- void InfoWindow::InitCommonButton ()
291- {
292- ChangeButtonAction (ButtonAction::NoButton);
293- Utils::Qt::SetRoundedCorners (_ui.pushButton , 6 );
294- connect (_ui.pushButton , &QPushButton::clicked, this , &InfoWindow::OnButtonClicked);
295-
296- if (Core::Settings::GetCurrent ().device_address == 0 ) {
297- ChangeButtonAction (ButtonAction::Bind);
298- }
299- }
300-
301- void InfoWindow::ChangeButtonAction (ButtonAction action)
302- {
303- switch (action) {
304- case ButtonAction::NoButton:
305- _ui.pushButton ->setText (" " );
306- _ui.pushButton ->hide ();
307- return ;
308-
309- case ButtonAction::Bind:
310- _ui.pushButton ->setText (tr (" Bind to AirPods" ));
311- break ;
312-
313- default :
314- FatalError (std::format (" Unhandled ButtonAction: '{}'" , Helper::ToUnderlying (action)), true );
315- }
316-
317- _buttonAction = action;
318- _ui.pushButton ->show ();
319- }
320-
321181void InfoWindow::UpdateState (const Core::AirPods::State &state)
322182{
323183 SPDLOG_INFO (" InfoWindow::UpdateState" );
@@ -434,6 +294,113 @@ void InfoWindow::Unbind()
434294 ApdApp->GetSysTray ()->Unbind ();
435295}
436296
297+ void InfoWindow::CheckUpdate ()
298+ {
299+ // SPDLOG_TRACE("CheckUpdate: Poll.");
300+
301+ const auto &DispatchNext = []() { Utils::Qt::Dispatch (&CheckUpdate); };
302+
303+ using OptReleaseInfo = std::optional<Core::Update::ReleaseInfo>;
304+ static std::promise<OptReleaseInfo> pmsRelease;
305+ static std::optional<std::future<OptReleaseInfo>> optFuture;
306+
307+ if (!optFuture.has_value ()) {
308+ SPDLOG_INFO (" CheckUpdate: Prepare promise and future. Fetch release info async." );
309+ pmsRelease = decltype (pmsRelease){};
310+ optFuture = pmsRelease.get_future ();
311+ std::thread{[]() { pmsRelease.set_value (Core::Update::FetchUpdateRelease ()); }}.detach ();
312+ DispatchNext ();
313+ return ;
314+ }
315+
316+ if (!Helper::IsFutureReady (optFuture.value ())) {
317+ // SPDLOG_TRACE("CheckUpdate: The future is not ready.");
318+ DispatchNext ();
319+ return ;
320+ }
321+
322+ auto optRelease = optFuture->get ();
323+ optFuture.reset ();
324+ SPDLOG_INFO (" CheckUpdate: Fetch release info successfully." );
325+
326+ do {
327+ if (!optRelease.has_value ()) {
328+ break ;
329+ }
330+
331+ auto releaseInfo = std::move (optRelease.value ());
332+ auto releaseVersion = releaseInfo.version .toString ();
333+
334+ QString changeLogBlock;
335+ if (!releaseInfo.changeLog .isEmpty ()) {
336+ changeLogBlock =
337+ QString{" %1\n %2\n\n " }.arg (tr (" Change log:" )).arg (releaseInfo.changeLog );
338+ }
339+
340+ auto button = QMessageBox::question (
341+ nullptr , Config::ProgramName,
342+ tr (" Hey! I found a new version available!\n "
343+ " \n "
344+ " Current version: %1\n "
345+ " Latest version: %2\n "
346+ " \n "
347+ " %3"
348+ " Click \" Ignore\" to skip this new version.\n "
349+ " \n "
350+ " Do you want to update it now?" )
351+ .arg (Core::Update::GetLocalVersion ().toString ())
352+ .arg (releaseVersion)
353+ .arg (changeLogBlock),
354+ QMessageBox::Yes | QMessageBox::No | QMessageBox::Ignore, QMessageBox::Yes);
355+
356+ if (button == QMessageBox::Yes) {
357+ SPDLOG_INFO (" CheckUpdate: User clicked Yes." );
358+
359+ if (!releaseInfo.CanAutoUpdate ()) {
360+ SPDLOG_INFO (" CheckUpdate: Popup latest url and quit." );
361+ QDesktopServices::openUrl (QUrl{releaseInfo.url });
362+ ApdApplication::QuitSafety ();
363+ break ;
364+ }
365+
366+ Gui::DownloadWindow{std::move (releaseInfo)}.exec ();
367+ }
368+ else if (button == QMessageBox::Ignore) {
369+ SPDLOG_INFO (" CheckUpdate: User clicked Ignore." );
370+
371+ Core::Settings::ModifiableAccess ()->skipped_version = releaseVersion;
372+ }
373+ else {
374+ SPDLOG_INFO (" CheckUpdate: User clicked No." );
375+ }
376+
377+ } while (false );
378+
379+ // TODO: Enable this timer after the icon notification is implemented
380+ //
381+ // QTimer::singleShot(1h, &CheckUpdate);
382+ }
383+
384+ void InfoWindow::ChangeButtonAction (ButtonAction action)
385+ {
386+ switch (action) {
387+ case ButtonAction::NoButton:
388+ _ui.pushButton ->setText (" " );
389+ _ui.pushButton ->hide ();
390+ return ;
391+
392+ case ButtonAction::Bind:
393+ _ui.pushButton ->setText (tr (" Bind to AirPods" ));
394+ break ;
395+
396+ default :
397+ FatalError (std::format (" Unhandled ButtonAction: '{}'" , Helper::ToUnderlying (action)), true );
398+ }
399+
400+ _buttonAction = action;
401+ _ui.pushButton ->show ();
402+ }
403+
437404void InfoWindow::SetAnimation (std::optional<Core::AirPods::Model> model)
438405{
439406 if (model == _cacheModel) {
@@ -481,6 +448,10 @@ void InfoWindow::StopAnimation()
481448
482449void InfoWindow::BindDevice ()
483450{
451+ //
452+ // TODO: Move non-UI code to `Core::AirPods::Manager`
453+ //
454+
484455 SPDLOG_INFO (" BindDevice" );
485456
486457 std::vector<Core::Bluetooth::Device> devices =
@@ -573,6 +544,14 @@ void InfoWindow::OnAppStateChanged(Qt::ApplicationState state)
573544 ControlAutoHideTimer (state != Qt::ApplicationActive);
574545}
575546
547+ void InfoWindow::OnPosMoveFinished ()
548+ {
549+ if (!_isShown) {
550+ hide ();
551+ StopAnimation ();
552+ }
553+ }
554+
576555void InfoWindow::OnButtonClicked ()
577556{
578557 switch (_buttonAction) {
@@ -587,11 +566,11 @@ void InfoWindow::OnButtonClicked()
587566 }
588567}
589568
590- void InfoWindow::OnPosMoveFinished ()
569+ // for loop play
570+ void InfoWindow::OnPlayerStateChanged (QMediaPlayer::State newState)
591571{
592- if (!_isShown) {
593- hide ();
594- StopAnimation ();
572+ if (newState == QMediaPlayer::StoppedState && _isAnimationPlaying) {
573+ _mediaPlayer->play ();
595574 }
596575}
597576
0 commit comments