feat: improve UI startup performance, window focus handling, and system tray interaction with tests
This commit is contained in:
+6
-5
@@ -130,6 +130,10 @@ public class CyberCoreDesktopApplication extends Application {
|
||||
mainAppView.setOnCertificates(() -> new CertificatesManagerDialog(primaryStage, certificateManager).show());
|
||||
mainAppView.setOnAbout(() -> openAboutDialog(primaryStage));
|
||||
|
||||
// Exibe imediatamente a janela principal em primeiro plano para o usuário
|
||||
mainAppView.show();
|
||||
logger.info("Janela principal JavaFX exibida.");
|
||||
|
||||
// Inicializa o servidor local do protocolo CCSP/1 para atender requisições do navegador
|
||||
initCcspServer(primaryStage);
|
||||
|
||||
@@ -140,11 +144,8 @@ public class CyberCoreDesktopApplication extends Application {
|
||||
this.systemTrayManager = new SystemTrayManager(primaryStage, this::refreshCertificates);
|
||||
this.systemTrayManager.setup();
|
||||
|
||||
// Carrega os certificados instalados na máquina do usuário
|
||||
refreshCertificates();
|
||||
|
||||
mainAppView.show();
|
||||
logger.info("Janela principal JavaFX exibida.");
|
||||
// Carrega os certificados instalados na máquina em segundo plano para não congelar a interface
|
||||
CompletableFuture.runAsync(this::refreshCertificates);
|
||||
}
|
||||
|
||||
private void refreshCertificates() {
|
||||
|
||||
+37
-4
@@ -15,6 +15,8 @@ import java.awt.PopupMenu;
|
||||
import java.awt.RenderingHints;
|
||||
import java.awt.SystemTray;
|
||||
import java.awt.TrayIcon;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.util.Objects;
|
||||
|
||||
@@ -76,7 +78,17 @@ public class SystemTrayManager {
|
||||
this.trayIcon = new TrayIcon(iconImage, "CyberCore Signer (CCSP/1 Ativo)", popup);
|
||||
this.trayIcon.setImageAutoSize(true);
|
||||
|
||||
// Clique no ícone restaura a janela
|
||||
// Clique com o botão esquerdo (single-click) restaura a janela imediatamente
|
||||
this.trayIcon.addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
if (e.getButton() == MouseEvent.BUTTON1) {
|
||||
Platform.runLater(SystemTrayManager.this::showStage);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Duplo clique ou acionamento nativo padrão também restaura a janela
|
||||
this.trayIcon.addActionListener(e -> Platform.runLater(this::showStage));
|
||||
|
||||
tray.add(this.trayIcon);
|
||||
@@ -104,10 +116,31 @@ public class SystemTrayManager {
|
||||
}
|
||||
|
||||
public void showStage() {
|
||||
if (stage != null) {
|
||||
stage.show();
|
||||
stage.setIconified(false);
|
||||
if (stage == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (!stage.isShowing()) {
|
||||
stage.show();
|
||||
}
|
||||
|
||||
if (stage.isIconified()) {
|
||||
stage.setIconified(false);
|
||||
}
|
||||
|
||||
stage.toFront();
|
||||
stage.requestFocus();
|
||||
|
||||
// No Windows (Win32 / DWM), chamadas de toFront() partindo da bandeja do sistema
|
||||
// podem ser suprimidas por políticas de foco do SO (Foreground Lock Timeout).
|
||||
// A elevação momentânea via setAlwaysOnTop força o gerenciador de janelas a trazer
|
||||
// o assinador diretamente para o primeiro plano.
|
||||
stage.setAlwaysOnTop(true);
|
||||
stage.setAlwaysOnTop(false);
|
||||
|
||||
} catch (Throwable t) {
|
||||
logger.debug("Falha secundária ao restaurar stage da bandeja: {}", t.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -297,8 +297,17 @@ public final class MainAppView {
|
||||
|
||||
public void show() {
|
||||
stage.centerOnScreen();
|
||||
if (stage.isIconified()) {
|
||||
stage.setIconified(false);
|
||||
}
|
||||
stage.show();
|
||||
stage.toFront();
|
||||
stage.requestFocus();
|
||||
try {
|
||||
stage.setAlwaysOnTop(true);
|
||||
stage.setAlwaysOnTop(false);
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
public void hide() {
|
||||
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package br.com.cybercore.signer.ui.tray;
|
||||
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.awt.SystemTray;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
@DisplayName("Testes do Gerenciador de Bandeja (SystemTrayManager)")
|
||||
class SystemTrayManagerTest {
|
||||
|
||||
@Test
|
||||
@DisplayName("Deve rejeitar Stage nulo no construtor")
|
||||
void shouldRejectNullStage() {
|
||||
assertThatThrownBy(() -> new SystemTrayManager(null, () -> {}))
|
||||
.isInstanceOf(NullPointerException.class)
|
||||
.hasMessageContaining("stage must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Deve reportar suporte do ambiente ou lidar graciosamente sem UI nativa")
|
||||
void shouldReportSupportOrHandleGracefully() {
|
||||
boolean supported = SystemTray.isSupported();
|
||||
// Verifica que chamada não lança exceção fatal
|
||||
assertThat(supported).isIn(true, false);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user