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.setOnCertificates(() -> new CertificatesManagerDialog(primaryStage, certificateManager).show());
|
||||||
mainAppView.setOnAbout(() -> openAboutDialog(primaryStage));
|
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
|
// Inicializa o servidor local do protocolo CCSP/1 para atender requisições do navegador
|
||||||
initCcspServer(primaryStage);
|
initCcspServer(primaryStage);
|
||||||
|
|
||||||
@@ -140,11 +144,8 @@ public class CyberCoreDesktopApplication extends Application {
|
|||||||
this.systemTrayManager = new SystemTrayManager(primaryStage, this::refreshCertificates);
|
this.systemTrayManager = new SystemTrayManager(primaryStage, this::refreshCertificates);
|
||||||
this.systemTrayManager.setup();
|
this.systemTrayManager.setup();
|
||||||
|
|
||||||
// Carrega os certificados instalados na máquina do usuário
|
// Carrega os certificados instalados na máquina em segundo plano para não congelar a interface
|
||||||
refreshCertificates();
|
CompletableFuture.runAsync(this::refreshCertificates);
|
||||||
|
|
||||||
mainAppView.show();
|
|
||||||
logger.info("Janela principal JavaFX exibida.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void refreshCertificates() {
|
private void refreshCertificates() {
|
||||||
|
|||||||
+35
-2
@@ -15,6 +15,8 @@ import java.awt.PopupMenu;
|
|||||||
import java.awt.RenderingHints;
|
import java.awt.RenderingHints;
|
||||||
import java.awt.SystemTray;
|
import java.awt.SystemTray;
|
||||||
import java.awt.TrayIcon;
|
import java.awt.TrayIcon;
|
||||||
|
import java.awt.event.MouseAdapter;
|
||||||
|
import java.awt.event.MouseEvent;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
@@ -76,7 +78,17 @@ public class SystemTrayManager {
|
|||||||
this.trayIcon = new TrayIcon(iconImage, "CyberCore Signer (CCSP/1 Ativo)", popup);
|
this.trayIcon = new TrayIcon(iconImage, "CyberCore Signer (CCSP/1 Ativo)", popup);
|
||||||
this.trayIcon.setImageAutoSize(true);
|
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));
|
this.trayIcon.addActionListener(e -> Platform.runLater(this::showStage));
|
||||||
|
|
||||||
tray.add(this.trayIcon);
|
tray.add(this.trayIcon);
|
||||||
@@ -104,10 +116,31 @@ public class SystemTrayManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void showStage() {
|
public void showStage() {
|
||||||
if (stage != null) {
|
if (stage == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!stage.isShowing()) {
|
||||||
stage.show();
|
stage.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stage.isIconified()) {
|
||||||
stage.setIconified(false);
|
stage.setIconified(false);
|
||||||
|
}
|
||||||
|
|
||||||
stage.toFront();
|
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() {
|
public void show() {
|
||||||
stage.centerOnScreen();
|
stage.centerOnScreen();
|
||||||
|
if (stage.isIconified()) {
|
||||||
|
stage.setIconified(false);
|
||||||
|
}
|
||||||
stage.show();
|
stage.show();
|
||||||
stage.toFront();
|
stage.toFront();
|
||||||
|
stage.requestFocus();
|
||||||
|
try {
|
||||||
|
stage.setAlwaysOnTop(true);
|
||||||
|
stage.setAlwaysOnTop(false);
|
||||||
|
} catch (Throwable ignored) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void hide() {
|
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