Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
36178df130 | ||
|
|
be281d1d04 | ||
|
|
ed69db4d59 | ||
|
|
cfea07a179 | ||
|
|
9f1bc289ee | ||
|
|
e20a52d3b2 | ||
|
|
3d3ec9d47c | ||
|
|
0abbbf5abd | ||
|
|
9598172174 | ||
|
|
68943221c3 | ||
|
|
d27c0f9567 | ||
|
|
0658f7007e | ||
|
|
14b342c3a2 |
@@ -126,8 +126,7 @@ Para detalhes completos sobre arquitetura, protocolos e manuais técnicos, acess
|
|||||||
* 🩺 **[Diagnóstico e Troubleshooting](docs/diagnostics.md):** Auto-teste e guia de resolução de problemas.
|
* 🩺 **[Diagnóstico e Troubleshooting](docs/diagnostics.md):** Auto-teste e guia de resolução de problemas.
|
||||||
* 💿 **[Instalação e Empacotamento Nativo](docs/installation.md):** Distribuição via `jpackage` sem JRE pré-instalado.
|
* 💿 **[Instalação e Empacotamento Nativo](docs/installation.md):** Distribuição via `jpackage` sem JRE pré-instalado.
|
||||||
* ⚖️ **[Limitações Técnicas](docs/limitations.md):** Declaração de dependências conforme a Regra 23.
|
* ⚖️ **[Limitações Técnicas](docs/limitations.md):** Declaração de dependências conforme a Regra 23.
|
||||||
* 🛠️ **[Modo de Desenvolvimento](docs/development-mode.md):** Parâmetro `--dev` e certificados de teste.
|
* 🎨 **[Layout e Assinatura em Lote](docs/ui-layout.md):** Design da UI padronizada e operações em lote.
|
||||||
* 🎨 **[Layout Dual-Theme e Assinatura em Lote](docs/serpro-layout-and-batch.md):** Design da UI e operações em lote.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -25,8 +25,6 @@ assets/
|
|||||||
│ ├── tutorial.svg # Ícone Tutorial e Ajuda (monitor com vídeo/play)
|
│ ├── tutorial.svg # Ícone Tutorial e Ajuda (monitor com vídeo/play)
|
||||||
│ ├── cert.svg # Ícone Certificados Digitais (pasta com cadeado seguro)
|
│ ├── cert.svg # Ícone Certificados Digitais (pasta com cadeado seguro)
|
||||||
│ ├── about.svg # Ícone Sobre o Sistema (círculo azul com 'i')
|
│ ├── about.svg # Ícone Sobre o Sistema (círculo azul com 'i')
|
||||||
│ ├── sun.svg # Ícone Tema Claro (sol dourado com raios vetoriais)
|
|
||||||
│ ├── moon.svg # Ícone Tema Escuro (lua crescente azul/índigo)
|
|
||||||
│ ├── shield.svg # Ícone Brasão de Segurança (CyberShield com chave interna)
|
│ ├── shield.svg # Ícone Brasão de Segurança (CyberShield com chave interna)
|
||||||
│ ├── seal.svg # Ícone Designer e Gerenciador de Selos Notariais
|
│ ├── seal.svg # Ícone Designer e Gerenciador de Selos Notariais
|
||||||
│ ├── check.svg # Ícone Checkmark de sucesso e validação
|
│ ├── check.svg # Ícone Checkmark de sucesso e validação
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
||||||
<rect x="3" y="4" width="18" height="16" rx="2.5" fill="none" stroke="currentColor" stroke-width="2"/>
|
<rect x="3.5" y="2.5" width="17" height="19" rx="2.5" fill="none" stroke="currentColor" stroke-width="2"/>
|
||||||
<circle cx="12" cy="10" r="2.5" fill="none" stroke="#0288d1" stroke-width="1.8"/>
|
<line x1="7" y1="6.5" x2="13" y2="6.5" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/>
|
||||||
<path d="M 10.5 12.2 L 9.5 17 L 12 15.5 L 14.5 17 L 13.5 12.2" fill="none" stroke="#0288d1" stroke-width="1.8" stroke-linejoin="round"/>
|
<line x1="7" y1="9.5" x2="11" y2="9.5" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" opacity="0.6"/>
|
||||||
<line x1="6" y1="7" x2="8" y2="7" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/>
|
<circle cx="14.5" cy="14.5" r="3.5" fill="none" stroke="#0288d1" stroke-width="1.8"/>
|
||||||
<line x1="16" y1="7" x2="18" y2="7" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/>
|
<path d="M 13 14.5 L 14.2 15.7 L 16.5 13.2" fill="none" stroke="#0288d1" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 625 B After Width: | Height: | Size: 659 B |
@@ -1,3 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
|
||||||
<path d="M 12 3 A 9 9 0 1 0 21 12 A 7.5 7.5 0 0 1 12 3 Z" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 251 B |
@@ -1,5 +1,7 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
||||||
<circle cx="12" cy="10" r="7.5" fill="none" stroke="#f59e0b" stroke-width="2"/>
|
<rect x="2.5" y="4.5" width="19" height="15" rx="3" fill="none" stroke="currentColor" stroke-width="2"/>
|
||||||
<circle cx="12" cy="10" r="4.5" fill="none" stroke="#f59e0b" stroke-width="1.2" stroke-dasharray="2 1.5"/>
|
<path d="M 6 13.5 C 7.5 10 9 10 10.5 13 C 11.5 15 13 11 15 12" fill="none" stroke="#0288d1" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
<path d="M 9.5 15.5 L 7 21.5 L 12 19 L 17 21.5 L 14.5 15.5" fill="none" stroke="#d97706" stroke-width="1.8" stroke-linejoin="round"/>
|
<circle cx="17.5" cy="8.5" r="1.5" fill="#f59e0b"/>
|
||||||
|
<line x1="6" y1="8.5" x2="13" y2="8.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" opacity="0.5"/>
|
||||||
|
<line x1="6" y1="16" x2="18" y2="16" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" opacity="0.4"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 418 B After Width: | Height: | Size: 654 B |
@@ -1,11 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
|
||||||
<circle cx="12" cy="12" r="4.5" fill="none" stroke="currentColor" stroke-width="2"/>
|
|
||||||
<line x1="12" y1="2" x2="12" y2="4.5" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
|
||||||
<line x1="12" y1="19.5" x2="12" y2="22" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
|
||||||
<line x1="2" y1="12" x2="4.5" y2="12" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
|
||||||
<line x1="19.5" y1="12" x2="22" y2="12" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
|
||||||
<line x1="4.9" y1="4.9" x2="6.7" y2="6.7" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
|
||||||
<line x1="17.3" y1="17.3" x2="19.1" y2="19.1" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
|
||||||
<line x1="4.9" y1="19.1" x2="6.7" y2="17.3" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
|
||||||
<line x1="17.3" y1="6.7" x2="19.1" y2="4.9" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.0 KiB |
@@ -27,7 +27,7 @@ Se você atua no desenvolvimento, manutenção ou auditoria do código-fonte do
|
|||||||
* **[Certificados A1, A3 e PKCS#11](a1-a3-certificates.md):** Funcionamento dos provedores `Pkcs12CertificateProvider`, `WindowsCertificateStoreProvider` (SunMSCAPI/CNG) e `Pkcs11CertificateProvider` (SunPKCS11), monitoramento dinâmico PC/SC (`SmartCardWatcher`) e extração de OIDs ICP-Brasil.
|
* **[Certificados A1, A3 e PKCS#11](a1-a3-certificates.md):** Funcionamento dos provedores `Pkcs12CertificateProvider`, `WindowsCertificateStoreProvider` (SunMSCAPI/CNG) e `Pkcs11CertificateProvider` (SunPKCS11), monitoramento dinâmico PC/SC (`SmartCardWatcher`) e extração de OIDs ICP-Brasil.
|
||||||
* **[Assinaturas PDF (PAdES) e Selos Visuais SVG](pades.md):** Motor de assinatura PDFBox + Bouncy Castle, salvamento incremental, renderização de alta resolução (300 DPI Java2D) e gerenciamento de selos digitais.
|
* **[Assinaturas PDF (PAdES) e Selos Visuais SVG](pades.md):** Motor de assinatura PDFBox + Bouncy Castle, salvamento incremental, renderização de alta resolução (300 DPI Java2D) e gerenciamento de selos digitais.
|
||||||
* **[Assinaturas XML (XAdES-BES e XMLDSig)](xml-xmldsig.md):** Conformidade com o padrão ETSI TS 101 903 / DOC-ICP-15.03 e suporte a notas fiscais eletrônicas (NF-e, CT-e, MDF-e, eSocial).
|
* **[Assinaturas XML (XAdES-BES e XMLDSig)](xml-xmldsig.md):** Conformidade com o padrão ETSI TS 101 903 / DOC-ICP-15.03 e suporte a notas fiscais eletrônicas (NF-e, CT-e, MDF-e, eSocial).
|
||||||
* **[Layout Dual-Theme e Assinatura em Lote](serpro-layout-and-batch.md):** Especificação de interface gráfica moderna, alternância dinâmica de tema (Claro/Escuro) e fila de processamento em lote.
|
* **[Layout de Interface e Assinatura em Lote](ui-layout.md):** Especificação de interface gráfica padronizada, acessibilidade WCAG AA e processamento em lote.
|
||||||
* **[Modo de Desenvolvimento](development-mode.md):** Guia de inicialização em modo `--dev`, políticas de não utilização de mocks em produção e geração de certificados A1 de teste locais.
|
* **[Modo de Desenvolvimento](development-mode.md):** Guia de inicialização em modo `--dev`, políticas de não utilização de mocks em produção e geração de certificados A1 de teste locais.
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -66,7 +66,7 @@ Se você precisa implantar, homologar ou validar os aspectos de conformidade e s
|
|||||||
| [`browser-compatibility.md`](browser-compatibility.md) | Compatibilidade de navegadores, HTTPS para loopback e Private Network Access (PNA) | Integradores Web & Segurança |
|
| [`browser-compatibility.md`](browser-compatibility.md) | Compatibilidade de navegadores, HTTPS para loopback e Private Network Access (PNA) | Integradores Web & Segurança |
|
||||||
| [`ccsp-v1.schema.json`](ccsp-v1.schema.json) | JSON Schema formal e versionado do protocolo CCSP/1 | Integradores & QA |
|
| [`ccsp-v1.schema.json`](ccsp-v1.schema.json) | JSON Schema formal e versionado do protocolo CCSP/1 | Integradores & QA |
|
||||||
| [`sdk-release-process.md`](sdk-release-process.md) | Processo de publicação e release do SDK no registry npm privado | DevOps & Engenharia |
|
| [`sdk-release-process.md`](sdk-release-process.md) | Processo de publicação e release do SDK no registry npm privado | DevOps & Engenharia |
|
||||||
| [`serpro-layout-and-batch.md`](serpro-layout-and-batch.md) | Design de interface e processamento em lote | UI/UX & Desenvolvedores |
|
| [`ui-layout.md`](ui-layout.md) | Design de interface e processamento em lote | UI/UX & Desenvolvedores |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 69 KiB |
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 41 KiB |
|
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 41 KiB |
|
Before Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 48 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 43 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 68 KiB |
|
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 68 KiB |
|
Before Width: | Height: | Size: 68 KiB |
|
After Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 47 KiB |
@@ -1,32 +1,32 @@
|
|||||||
# Layout CyberCore Signer — Design Dual-Theme & Assinatura em Lote
|
# Layout e Interface Gráfica — CyberCore Signer
|
||||||
|
|
||||||
O **CyberCore Signer** adota uma interface gráfica que une a ergonomia de hub consolidada no mercado nacional com uma identidade visual moderna, autoral e com suporte dinâmico a **Tema Claro (Light)** e **Tema Escuro (Dark)**.
|
O **CyberCore Signer** adota uma interface gráfica profissional e moderna, com foco em usabilidade, segurança visual e conformidade rigorosa com os padrões de acessibilidade WCAG 2.1 (AA).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 1. Identidade Visual e Filosofia de Design
|
## 1. Identidade Visual e Filosofia de Design
|
||||||
|
|
||||||
Em conformidade com os requisitos do projeto, a interface **não é uma cópia datada**, mas sim uma evolução:
|
A interface foi estruturada com foco na clareza operacional e consistência ergonômica:
|
||||||
1. **Identidade CyberCore:** Logotipo central em cristal tridimensional e escudo criptográfico vetorial (`SvgIcons`), subtítulo "Agente Criptográfico Local • ICP-Brasil" e indicador de segurança em tempo real.
|
1. **Identidade CyberCore:** Logotipo central em cristal e escudo criptográfico vetorial (`SvgIcons`), subtítulo "Agente Criptográfico Local • ICP-Brasil" e indicador de segurança em tempo real.
|
||||||
2. **Badge de Status Ativo:** Exibe o status da porta local (`● Agente Ativo • 127.0.0.1:21325`) com verde esmeralda no modo claro e verde neon no modo escuro.
|
2. **Badge de Status Ativo:** Exibe o status do agente e da porta local (`● Agente Ativo • 127.0.0.1:21325`) com indicação em verde esmeralda.
|
||||||
3. **Alternador Dinâmico de Temas:** Botão pílula localizado no cabeçalho superior que permite ao usuário alternar instantaneamente entre **Modo Claro** e **Modo Escuro**:
|
3. **Tema Único Padronizado:** Sistema visual unificado baseado em tokens CSS declarados em `.root`, garantindo contraste nítido, tipografia escalável e ausência de inconsistências de renderização.
|
||||||
- Ícone do Sol ☀️ para comutar para o tema Claro.
|
|
||||||
- Ícone da Lua 🌙 para comutar para o tema Escuro.
|
|
||||||
- Classe gerenciadora centralizada: [`ThemeManager.java`](file:///var/www/cybercore/cybercore-signer/modules/cybercore-ui/src/main/java/br/com/cybercore/signer/ui/theme/ThemeManager.java), que replica a escolha de tema para todos os diálogos secundários.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 2. Paleta de Cores dos Temas
|
## 2. Paleta de Cores e Tokens Semânticos
|
||||||
|
|
||||||
| Componente | Tema Claro (Light) | Tema Escuro (Dark) |
|
| Componente / Token | Valor Canônico | Descrição |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| **Fundo da Janela** | `#f1f5f9` (Slate suave) | `#0b0f17` (Dark profundo) |
|
| **Fundo da Janela** (`-fx-color-background`) | `#f1f5f9` | Tom slate neutro e suave para leitura prolongada |
|
||||||
| **Header Superior** | `#ffffff`, borda `#e2e8f0` | `#121824`, borda `#1e293b` |
|
| **Superfície de Cartões** (`-fx-color-surface`) | `#ffffff` | Branco puro para cartões, caixas e painéis |
|
||||||
| **Cartões de Ação** | `#ffffff`, borda `#e2e8f0`, hover `#0284c7` | `#131c2e`, borda `#1e293b`, hover `#38bdf8` |
|
| **Bordas Principais** (`-fx-color-border`) | `#cbd5e1` | Linhas de divisão estrutural com contraste seguro |
|
||||||
| **Texto dos Botões** | `#1e293b` (Cinza escuro de alto contraste) | `#f8fafc` (Branco gelo nítido) |
|
| **Bordas Suaves** (`-fx-color-border-subtle`) | `#e2e8f0` | Divisórias e contornos secundários |
|
||||||
| **Painel Lateral** | `#ffffff`, borda `#e2e8f0` | `#121824`, borda `#1e293b` |
|
| **Texto Primário** (`-fx-color-text`) | `#0f172a` | Texto de alto contraste (WCAG ratio ~ 15.8:1) |
|
||||||
| **Badge de Status** | Fundo `#ecfdf5`, texto `#047857`, borda `#10b981` | Fundo `rgba(16, 185, 129, 0.15)`, texto `#34d399` |
|
| **Texto Atenuado** (`-fx-color-text-muted`) | `#475569` | Rótulos secundários e metadados (WCAG ratio ~ 6.8:1) |
|
||||||
| **Alternador de Tema** | Fundo `#ffffff`, borda `#cbd5e1`, hover `#0284c7` | Fundo `#1e293b`, borda `#334155`, hover `#38bdf8` |
|
| **Cor Primária** (`-fx-color-primary`) | `#0284c7` | Azul característico da marca CyberCore para ações padrão |
|
||||||
|
| **Sucesso** (`-fx-color-success`) | `#16a34a` | Indicadores de assinaturas válidas e status operacional |
|
||||||
|
| **Aviso** (`-fx-color-warning`) | `#d97706` | Alertas de atenção e certificados próximos ao vencimento |
|
||||||
|
| **Erro** (`-fx-color-error`) | `#dc2626` | Notificações de erro ou falha criptográfica |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ A interface principal organiza as operações em 6 cartões modernos com cantos
|
|||||||
|
|
||||||
| Posição | Operação | Descrição |
|
| Posição | Operação | Descrição |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| (0, 0) | **Assinar PDF** | Assinatura digital de documentos PDF com cálculo de hash SHA-256 e gravação de assinatura. |
|
| (0, 0) | **Assinar PDF** | Assinatura digital de documentos PDF (PAdES) com visualização e posicionamento interativo de selos. |
|
||||||
| (1, 0) | **Assinar XML** | Assinatura de documentos XML (estruturas fiscais, NF-e, XMLDSig). |
|
| (1, 0) | **Assinar XML** | Assinatura de documentos XML (estruturas fiscais, NF-e, XMLDSig). |
|
||||||
| (2, 0) | **Assinatura Atachada** | Assinatura embutida/anexada ao documento original (CAdES enveloped). |
|
| (2, 0) | **Assinatura Atachada** | Assinatura embutida/anexada ao documento original (CAdES enveloped). |
|
||||||
| (0, 1) | **Gerenciar Selos** | Abertura do Designer e Gerenciador de Selos Digitais visuais para PAdES. |
|
| (0, 1) | **Gerenciar Selos** | Abertura do Designer e Gerenciador de Selos Digitais visuais para PAdES. |
|
||||||
@@ -52,7 +52,7 @@ Na coluna lateral direita, o usuário dispõe de atalhos rápidos para operaçõ
|
|||||||
1. **`</> Logs`:** Visualizador de auditoria em tempo real, monitorando requisições locais CCSP/1 e eventos criptográficos.
|
1. **`</> Logs`:** Visualizador de auditoria em tempo real, monitorando requisições locais CCSP/1 e eventos criptográficos.
|
||||||
2. **`⚙ Configurações`:** Gerenciador de origens web autorizadas, revogação de acessos e opções de PIN.
|
2. **`⚙ Configurações`:** Gerenciador de origens web autorizadas, revogação de acessos e opções de PIN.
|
||||||
3. **`💻 Tutorial`:** Guia passo a passo interativo de uso do assinador e integração web.
|
3. **`💻 Tutorial`:** Guia passo a passo interativo de uso do assinador e integração web.
|
||||||
4. **`🗂 Certificados`:** Lista de certificados ICP-Brasil instalados (Windows-MY, A1 e A3) e ferramenta para importação de certificados A1 (`.pfx` / `.p12`).
|
4. **`🗂 Certificados`:** Lista de certificados ICP-Brasil disponíveis (Windows Certificate Store, A1 e A3) e ferramenta para importação de certificados A1 (`.pfx` / `.p12`).
|
||||||
5. **`ℹ Sobre`:** Informações de versão, engine JCA/JCE e garantias de privacidade local.
|
5. **`ℹ Sobre`:** Informações de versão, engine JCA/JCE e garantias de privacidade local.
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -65,7 +65,6 @@ A funcionalidade de assinatura em lote permite:
|
|||||||
- Seleção do certificado digital ICP-Brasil desejado (com exibição de titular, CPF/CNPJ, tipo A1/A3 e validade).
|
- Seleção do certificado digital ICP-Brasil desejado (com exibição de titular, CPF/CNPJ, tipo A1/A3 e validade).
|
||||||
- Escolha da pasta de destino para os arquivos assinados (salvando arquivos `.p7s` correspondentes).
|
- Escolha da pasta de destino para os arquivos assinados (salvando arquivos `.p7s` correspondentes).
|
||||||
- Execução assíncrona em thread isolada para manter a interface responsiva durante todo o lote.
|
- Execução assíncrona em thread isolada para manter a interface responsiva durante todo o lote.
|
||||||
- Compatibilidade automática com o tema ativo (Claro ou Escuro).
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -1,79 +1,95 @@
|
|||||||
# UI Troubleshooting & Resolução de Estilos (JavaFX CSS)
|
# UI Troubleshooting & Resolução de Estilos (JavaFX CSS)
|
||||||
|
|
||||||
Este documento registra a investigação da causa raiz de problemas visuais na interface gráfica do CyberCore Signer, em especial a falha de renderização de textos escuros sobre fundo escuro no tema escuro.
|
Este documento registra a investigação da causa raiz de problemas visuais na interface gráfica do CyberCore Signer, em especial a falha de renderização de textos com cor preta (`Color.BLACK`) via `setStyle()`, a eliminação definitiva de estilos inline de cor e a transição para o tema claro unificado.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 1. O Problema: Texto Preto sobre Fundo Escuro (Contraste ~1.1:1)
|
## 1. O Problema: Texto Renderizado em Preto (`#000000`) via `setStyle`
|
||||||
|
|
||||||
### Sintoma
|
### Sintoma
|
||||||
No tema escuro (`ThemeManager.AppTheme.DARK` / `.theme-dark`), determinados textos nos diálogos `ExitConfirmationDialog` (texto explicativo da ação), `PdfSignDialog` (nome e metadados do documento PDF selecionado, dica de arraste do selo visual) e `PdfCertificateSignDialog` (opção de memorização segura de PIN) eram renderizados com a cor preta (`#000000`) sobre um fundo escuro (`#090d16` ou `#111827`).
|
Em diálogos modais como `ExitConfirmationDialog`, `PdfSignDialog` e `PdfCertificateSignDialog`, chamadas como:
|
||||||
|
```java
|
||||||
O contraste medido entre o texto e o fundo situava-se em aproximadamente **1.1:1 a 1.2:1**, violando completamente as diretrizes de acessibilidade WCAG AA (mínimo de 4.5:1 para texto normal) e tornando o texto praticamente invisível.
|
subtitle.setStyle("-fx-text-fill: -fx-text-secondary;");
|
||||||
|
lblDocName.setStyle("-fx-text-fill: -fx-text-primary;");
|
||||||
|
dragTip.setStyle("-fx-text-fill: -fx-text-secondary;");
|
||||||
|
```
|
||||||
|
resultavam em texto renderizado com a cor preta opaca (`#000000`), mesmo quando o token `-fx-text-secondary` já se encontrava definido no arquivo `theme.css`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 2. Investigação Técnica e Causa Raiz
|
## 2. Investigação Técnica e Causa Raiz
|
||||||
|
|
||||||
### 2.1 Lookups Inexistentes no CSS
|
### 2.1 Por que `setStyle("-fx-text-fill: -fx-text-secondary")` renderiza PRETO?
|
||||||
Ao inspecionar o código-fonte Java dessas telas, verificou-se o uso de estilos inline via `setStyle(...)`:
|
No motor de estilização do JavaFX (`com.sun.javafx.css.StyleManager` / `CssStyleHelper`):
|
||||||
```java
|
|
||||||
subtitle.setStyle("-fx-text-fill: -fx-text-secondary; -fx-font-size: 12px;");
|
1. **Momento da Avaliação (Timing) de Nós Fora da Árvore da Scene:**
|
||||||
lblDocName.setStyle("-fx-font-weight: bold; -fx-font-size: 12.5px; -fx-text-fill: -fx-text-primary;");
|
Quando um nó (como um `Label` ou `Text`) é instanciado no código Java dentro do construtor de um diálogo e recebe `.setStyle("-fx-text-fill: -fx-text-secondary;")`, ele ainda **não foi anexado ao grafo de cena** (`Scene`).
|
||||||
lblDocMeta.setStyle("-fx-font-size: 11px; -fx-text-fill: -fx-text-secondary;");
|
2. **Escopo de Resolução de Lookups Ausente:**
|
||||||
previewBox.setStyle("-fx-background-color: -fx-bg-primary; -fx-border-color: -fx-border-light; ...");
|
Como `-fx-text-secondary` é uma cor pesquisada (*looked-up color*) definida no seletor `.root` da folha de estilo da aplicação, a engine de CSS do JavaFX tenta resolver essa variável subindo a cadeia de nós pais (`parent chain`) até alcançar `.root`. Como o nó está solto na memória (sem parent e sem Scene vinculada no instante da chamada), o lookup não encontra nenhuma definição válida.
|
||||||
chkRememberPin.setStyle("-fx-font-size: 11.5px; -fx-text-fill: -fx-text-primary;");
|
3. **Mecanismo de Fallback Silencioso do Modena (`Color.BLACK`):**
|
||||||
|
O analisador do JavaFX não lança exceção ao falhar na resolução de uma cor de lookup em estilo inline. Em vez disso, seu conversor (`PaintConverter` / `ColorConverter`) assume o valor padrão de salvaguarda da especificação Modena: **`Color.BLACK` (`rgba(0, 0, 0, 1.0)`)**.
|
||||||
|
4. **Imutabilidade e Prioridade do Estilo Inline:**
|
||||||
|
O estilo inline atribuído via `setStyle(...)` possui a especificidade mais alta na hierarquia CSS do JavaFX. O valor pré-resolvido como `Color.BLACK` fica gravado diretamente no cache de estilo do nó. Quando a Scene finalmente executa a passagem formal de CSS (`applyCss()` / `layout()`), a engine prioriza o estilo inline e não reavalia o lookup que falhou previamente.
|
||||||
|
|
||||||
|
### 2.2 Por que o problema não ocorre com Classes CSS?
|
||||||
|
Quando utilizamos classes semânticas declaradas em folha de estilo (ex.: `lbl.getStyleClass().add("text-muted");`):
|
||||||
|
```css
|
||||||
|
.text-muted {
|
||||||
|
-fx-text-fill: -fx-text-secondary;
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
A resolução da regra é postergada até que o nó pertença à hierarquia da Scene e a passagem de CSS do JavaFX seja executada. Nesse momento, o nó tem acesso direto às definições de `.root`, resolvendo perfeitamente o token para a cor correta da paleta (`#475569`).
|
||||||
Os tokens `-fx-text-primary`, `-fx-text-secondary`, `-fx-bg-primary`, `-fx-border-light` e `-fx-accent` **não estavam declarados** no arquivo de folha de estilos `theme.css`.
|
|
||||||
|
|
||||||
### 2.2 Por que o JavaFX rebaixa para preto (`#000000`)?
|
|
||||||
No motor de estilização do JavaFX (`com.sun.javafx.css` / Modena):
|
|
||||||
1. Quando uma declaração inline em `setStyle(...)` utiliza uma cor de lookup (`-fx-text-fill: <variavel>`), o analisador CSS do JavaFX tenta resolver essa variável percorrendo os ancestrais do nó até o `.root` da cena.
|
|
||||||
2. Caso a variável **não seja encontrada**, o `StyleConverter` do JavaFX não lança exceção em tempo de execução. Em vez disso, seu mecanismo de tolerância a falhas adota o valor fallback padrão da especificação base de controles: **`Color.BLACK` (`rgba(0, 0, 0, 1.0)`)**.
|
|
||||||
3. Em um tema claro, o fundo é branco ou cinza claro (`#ffffff` / `#f1f5f9`), de modo que o texto preto parece ter funcionado por pura coincidência de luminância.
|
|
||||||
4. No tema escuro, no entanto, o fundo da aplicação é escuro (`#090d16` a `#111827`), fazendo com que o fallback para `#000000` destrua o contraste da interface.
|
|
||||||
|
|
||||||
### 2.3 Problemas Adicionais com `setStyle(...)`
|
|
||||||
- **Prioridade Forçada:** `setStyle(...)` tem especificidade de estilo inline (prioridade máxima), sobrescrevendo quaisquer regras de temas `.theme-light` e `.theme-dark` que tentem ajustar o elemento.
|
|
||||||
- **Acoplamento:** Espalha lógica visual e regras de cor pelo código Java, dificultando testes de regressão automatizados e auditorias de contraste.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 3. Diretrizes e Solução Adotada
|
## 3. Diretriz Arquitetural: Proibição de Cores em `setStyle()`
|
||||||
|
|
||||||
### 3.1 Definição Completa de Tokens e Aliases no `theme.css`
|
Fica estabelecido como regra obrigatória de desenvolvimento no `cybercore-ui`:
|
||||||
Todos os tokens semânticos e seus aliases de compatibilidade são declarados formalmente nos blocos canônicos de tokens:
|
|
||||||
```css
|
|
||||||
.theme-light {
|
|
||||||
-fx-text-primary: -fx-color-text;
|
|
||||||
-fx-text-secondary: -fx-color-text-muted;
|
|
||||||
-fx-bg-primary: -fx-color-surface;
|
|
||||||
-fx-border-light: -fx-color-border-subtle;
|
|
||||||
-fx-accent: -fx-color-primary;
|
|
||||||
}
|
|
||||||
|
|
||||||
.theme-dark {
|
1. **`setStyle()` restrito exclusivamente à Geometria:**
|
||||||
-fx-text-primary: -fx-color-text;
|
- Permitido: dimensões dinâmicas, larguras mínimas, alturas, alinhamentos e paddings específicos de cálculo em tempo de execução (`-fx-min-width`, `-fx-pref-width`, `-fx-padding`, `-fx-alignment`).
|
||||||
-fx-text-secondary: -fx-color-text-muted;
|
- **Terminantemente proibido em `setStyle()`:**
|
||||||
-fx-bg-primary: -fx-color-surface;
|
- `-fx-text-fill`
|
||||||
-fx-border-light: -fx-color-border;
|
- `-fx-background-color`
|
||||||
-fx-accent: -fx-color-primary;
|
- `-fx-border-color`
|
||||||
}
|
- `-fx-fill` / `-fx-stroke`
|
||||||
```
|
- Códigos hexadecimais (`#...`) ou funções cromáticas (`rgb(...)`, `rgba(...)`)
|
||||||
|
- Nomes de lookups de cor (`-fx-accent`, `-fx-color-*`, etc.)
|
||||||
|
|
||||||
### 3.2 Eliminação de Cores Inline
|
2. **Uso Obrigatório de Classes CSS Semânticas:**
|
||||||
É terminantemente proibido utilizar `setStyle(...)` com propriedades que afetem cores:
|
| Classe CSS | Finalidade | Propriedade Aplicada |
|
||||||
- Proibidos em `setStyle`: `-fx-text-fill`, `-fx-background-color`, `-fx-border-color`, `-fx-fill`, `-fx-stroke` ou códigos `#hex`.
|
|---|---|---|
|
||||||
- Permitidos em `setStyle` exclusivamente quando estritamente necessário: geometria e layout (`-fx-min-width`, `-fx-pref-width`, `-fx-padding`).
|
| `.text-primary` | Texto principal | `-fx-text-fill: -fx-text-primary;` |
|
||||||
|
| `.text-secondary`, `.text-muted` | Texto auxiliar / metadados | `-fx-text-fill: -fx-text-secondary;` |
|
||||||
|
| `.text-accent` | Destaque primário | `-fx-text-fill: -fx-accent;` |
|
||||||
|
| `.text-bold` | Tipografia em negrito | `-fx-font-weight: bold;` |
|
||||||
|
| `.text-small` | Texto compacto | `-fx-font-size: 11px;` |
|
||||||
|
| `.text-mono` | Fonte monoespaçada para hashes | `-fx-font-family: monospace;` |
|
||||||
|
| `.surface-box` | Container de superfície branca com borda | `-fx-background-color: -fx-color-surface;` |
|
||||||
|
| `.toolbar-box` | Barra de ferramentas compacta | `-fx-background-color: -fx-color-surface;` |
|
||||||
|
| `.scroll-transparent` | ScrollPane sem fundo cinza | `-fx-background-color: transparent;` |
|
||||||
|
| `.status-ok` / `.status-warn` / `.status-error` | Indicadores de status | Cores semânticas da paleta |
|
||||||
|
|
||||||
Toda estilização cromática deve ser feita atribuindo classes semânticas:
|
---
|
||||||
- `.text-primary`: Texto principal com alta luminância no escuro e baixa no claro.
|
|
||||||
- `.text-secondary`, `.text-muted`: Texto secundário / auxiliar com contraste garantido >= 4.5:1.
|
## 4. Salvaguardas Automatizadas
|
||||||
- `.text-accent`: Destaque com a cor primária da marca.
|
|
||||||
- `.surface-box`, `.toolbar-box`: Caixas de superfície neutra e barras de ferramentas.
|
O teste de arquitetura [`DesignTokenGuardrailTest.java`](file:///var/www/cybercore/cybercore-signer/modules/cybercore-ui/src/test/java/br/com/cybercore/signer/ui/theme/DesignTokenGuardrailTest.java) atua como barreira de compilação contínua:
|
||||||
|
- Varre todos os arquivos `.java` no módulo `cybercore-ui` e reprova automaticamente o build se qualquer chamada `setStyle()` contiver propriedades ou valores de cor.
|
||||||
|
- Valida matematicamente a relação de contraste WCAG AA (mínimo de 4.5:1 para texto normal) em todas as combinações de tokens de texto sobre superfície/fundo.
|
||||||
|
- Assegura ausência de seletores ou classes residuais de temas escuros (`.theme-dark`, `AppTheme`, `isDark()`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Comportamento da Barra de Título Nativa no Windows (DWM em Modo Escuro)
|
||||||
|
|
||||||
|
### Diagnóstico Operacional
|
||||||
|
Quando o sistema operacional Windows 10/11 está configurado no "Modo Escuro do Sistema" (*Personalização > Cores > Escolher o modo: Escuro*), o gerenciador de janelas do sistema operacional (**Desktop Window Manager — DWM**) aplica automaticamente o atributo `DWMWA_USE_IMMERSIVE_DARK_MODE` na moldura não-cliente da janela nativa (`HWND`).
|
||||||
|
|
||||||
|
Como o CyberCore Signer utiliza decoração nativa padrão (`StageStyle.DECORATED`) para preservar o comportamento esperado pelo usuário empresarial — incluindo cantos arredondados, atalhos de teclado nativos, suporte a múltiplos monitores com DPIs distintos e os *Snap Layouts* do Windows 11 — a barra de título nativa é desenhada pelo Windows em tom escuro, enquanto o conteúdo da janela exibe o tema claro unificado da aplicação.
|
||||||
|
|
||||||
|
### Diretriz de Arquitetura (Não Forçar)
|
||||||
|
Em estrita conformidade com as diretrizes do projeto (Regras 2 e 23):
|
||||||
|
1. **Não forçar a barra de título:** Evita-se o uso de hacks de baixo nível via JNA/JNI para sobrescrever o DWM do Windows ou janelas não decoradas (`UNDECORATED`), o que causaria quebra de acessibilidade e perda de recursos nativos do sistema.
|
||||||
|
2. **Harmonia Visual:** A moldura do Windows atua como moldura de sistema e o conteúdo claro do CyberCore Signer permanece legível, íntegro e em total conformidade com a especificação de interface.
|
||||||
|
|
||||||
### 3.3 Testes Automatizados de Guarda-Corpo
|
|
||||||
O projeto mantém testes unitários automatizados em `DesignTokenGuardrailTest` e `CssColorAndContrastGuardrailTest` que:
|
|
||||||
1. Fazem varredura estática de todo o código Java da UI para reprovar qualquer `setStyle` que declare cor ou hexadecimal.
|
|
||||||
2. Validam matematicamente a fórmula de luminância relativa WCAG 2.1 para cada par texto/fundo nos dois temas, garantindo conformidade rigorosa.
|
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ import java.util.HashMap;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import br.com.cybercore.signer.ui.CyberCoreDesktopApplication;
|
import br.com.cybercore.signer.ui.CyberCoreDesktopApplication;
|
||||||
import br.com.cybercore.signer.ui.theme.ThemeManager;
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
@@ -422,8 +421,7 @@ public final class AssetManager {
|
|||||||
Map<String, Paint> defsMap = new HashMap<>();
|
Map<String, Paint> defsMap = new HashMap<>();
|
||||||
parseDefs(svg, defsMap);
|
parseDefs(svg, defsMap);
|
||||||
|
|
||||||
Color effectiveCurrentColor = (tint != null) ? tint :
|
Color effectiveCurrentColor = (tint != null) ? tint : Color.web("#1E293B");
|
||||||
(ThemeManager.isDark() ? Color.web("#F1F5F9") : Color.web("#1E293B"));
|
|
||||||
List<Shape> dynamicStrokeShapes = new ArrayList<>();
|
List<Shape> dynamicStrokeShapes = new ArrayList<>();
|
||||||
List<Shape> dynamicFillShapes = new ArrayList<>();
|
List<Shape> dynamicFillShapes = new ArrayList<>();
|
||||||
RenderContext ctx = new RenderContext(defsMap, effectiveCurrentColor, dynamicStrokeShapes, dynamicFillShapes);
|
RenderContext ctx = new RenderContext(defsMap, effectiveCurrentColor, dynamicStrokeShapes, dynamicFillShapes);
|
||||||
@@ -455,19 +453,6 @@ public final class AssetManager {
|
|||||||
container.setMaxSize(targetWidth, targetHeight);
|
container.setMaxSize(targetWidth, targetHeight);
|
||||||
container.setMinSize(targetWidth, targetHeight);
|
container.setMinSize(targetWidth, targetHeight);
|
||||||
|
|
||||||
// Se for dinâmico (sem tint fixo) e contiver elementos currentColor, atualiza com a mudança de tema
|
|
||||||
if (tint == null && (!dynamicStrokeShapes.isEmpty() || !dynamicFillShapes.isEmpty())) {
|
|
||||||
ThemeManager.addListener(theme -> {
|
|
||||||
Color c = (theme == ThemeManager.AppTheme.DARK) ? Color.web("#F1F5F9") : Color.web("#1E293B");
|
|
||||||
for (Shape s : dynamicStrokeShapes) {
|
|
||||||
s.setStroke(c);
|
|
||||||
}
|
|
||||||
for (Shape s : dynamicFillShapes) {
|
|
||||||
s.setFill(c);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return container;
|
return container;
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -801,7 +786,7 @@ public final class AssetManager {
|
|||||||
if (ctx != null && ctx.currentColor() != null) {
|
if (ctx != null && ctx.currentColor() != null) {
|
||||||
return ctx.currentColor();
|
return ctx.currentColor();
|
||||||
}
|
}
|
||||||
return ThemeManager.isDark() ? Color.web("#F1F5F9") : Color.web("#1E293B");
|
return Color.web("#1E293B");
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
return Color.web(trimmed);
|
return Color.web(trimmed);
|
||||||
|
|||||||
@@ -119,12 +119,6 @@ public final class Icons {
|
|||||||
public static Node refresh() { return refresh(SIZE_16); }
|
public static Node refresh() { return refresh(SIZE_16); }
|
||||||
public static Node refresh(int size) { return of("refresh", size); }
|
public static Node refresh(int size) { return of("refresh", size); }
|
||||||
|
|
||||||
public static Node sun() { return sun(SIZE_16); }
|
|
||||||
public static Node sun(int size) { return of("sun", size); }
|
|
||||||
|
|
||||||
public static Node moon() { return moon(SIZE_16); }
|
|
||||||
public static Node moon(int size) { return of("moon", size); }
|
|
||||||
|
|
||||||
public static Node arrowRight() { return arrowRight(SIZE_16); }
|
public static Node arrowRight() { return arrowRight(SIZE_16); }
|
||||||
public static Node arrowRight(int size) { return of("arrow-right", size); }
|
public static Node arrowRight(int size) { return of("arrow-right", size); }
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ import javafx.scene.shape.Circle;
|
|||||||
import javafx.scene.shape.Polygon;
|
import javafx.scene.shape.Polygon;
|
||||||
import javafx.scene.shape.Rectangle;
|
import javafx.scene.shape.Rectangle;
|
||||||
import javafx.scene.shape.SVGPath;
|
import javafx.scene.shape.SVGPath;
|
||||||
|
import javafx.scene.shape.StrokeLineCap;
|
||||||
|
import javafx.scene.shape.StrokeLineJoin;
|
||||||
import javafx.scene.text.Font;
|
import javafx.scene.text.Font;
|
||||||
import javafx.scene.text.FontWeight;
|
import javafx.scene.text.FontWeight;
|
||||||
import javafx.scene.text.Text;
|
import javafx.scene.text.Text;
|
||||||
@@ -477,11 +479,21 @@ public final class SvgIcons {
|
|||||||
StackPane box = new StackPane();
|
StackPane box = new StackPane();
|
||||||
box.setPrefSize(24, 24);
|
box.setPrefSize(24, 24);
|
||||||
|
|
||||||
Text code = new Text("</>");
|
SVGPath frame = new SVGPath();
|
||||||
code.setFont(Font.font("Segoe UI", FontWeight.BOLD, 12));
|
frame.setContent("M 3 5 C 3 3.9 3.9 3 5 3 L 19 3 C 20.1 3 21 3.9 21 5 L 21 19 C 21 20.1 20.1 21 19 21 L 5 21 C 3.9 21 3 20.1 3 19 Z");
|
||||||
code.setFill(Color.web("#0288d1"));
|
frame.setFill(Color.TRANSPARENT);
|
||||||
|
frame.setStroke(Color.web("#1e293b"));
|
||||||
|
frame.setStrokeWidth(1.8);
|
||||||
|
|
||||||
box.getChildren().add(code);
|
SVGPath prompt = new SVGPath();
|
||||||
|
prompt.setContent("M 7 9 L 10 12 L 7 15 M 12 15 L 16 15");
|
||||||
|
prompt.setFill(Color.TRANSPARENT);
|
||||||
|
prompt.setStroke(Color.web("#0288d1"));
|
||||||
|
prompt.setStrokeWidth(1.8);
|
||||||
|
prompt.setStrokeLineCap(StrokeLineCap.ROUND);
|
||||||
|
prompt.setStrokeLineJoin(StrokeLineJoin.ROUND);
|
||||||
|
|
||||||
|
box.getChildren().addAll(frame, prompt);
|
||||||
return box;
|
return box;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -538,20 +550,21 @@ public final class SvgIcons {
|
|||||||
StackPane box = new StackPane();
|
StackPane box = new StackPane();
|
||||||
box.setPrefSize(24, 24);
|
box.setPrefSize(24, 24);
|
||||||
|
|
||||||
SVGPath folder = new SVGPath();
|
SVGPath certFrame = new SVGPath();
|
||||||
folder.setContent("M 2 5 L 8 5 L 10 7 L 22 7 L 22 20 L 2 20 Z");
|
certFrame.setContent("M 4 4 C 4 3 5 2 6 2 L 18 2 C 19 2 20 3 20 4 L 20 20 C 20 21 19 22 18 22 L 6 22 C 5 22 4 21 4 20 Z M 7 6 L 14 6 M 7 9 L 12 9");
|
||||||
folder.setFill(Color.web("#e1f5fe"));
|
certFrame.setFill(Color.TRANSPARENT);
|
||||||
folder.setStroke(Color.web("#0288d1"));
|
certFrame.setStroke(Color.web("#1e293b"));
|
||||||
folder.setStrokeWidth(1.5);
|
certFrame.setStrokeWidth(1.8);
|
||||||
|
|
||||||
Rectangle lock = new Rectangle(7, 6);
|
SVGPath badge = new SVGPath();
|
||||||
lock.setArcWidth(2);
|
badge.setContent("M 15 13 A 3.2 3.2 0 1 1 15 13.01 M 13.5 15 L 14.8 16.2 L 17 14");
|
||||||
lock.setArcHeight(2);
|
badge.setFill(Color.TRANSPARENT);
|
||||||
lock.setFill(Color.web("#0288d1"));
|
badge.setStroke(Color.web("#0288d1"));
|
||||||
lock.setTranslateX(3);
|
badge.setStrokeWidth(1.6);
|
||||||
lock.setTranslateY(2);
|
badge.setStrokeLineCap(StrokeLineCap.ROUND);
|
||||||
|
badge.setStrokeLineJoin(StrokeLineJoin.ROUND);
|
||||||
|
|
||||||
box.getChildren().addAll(folder, lock);
|
box.getChildren().addAll(certFrame, badge);
|
||||||
return box;
|
return box;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -577,63 +590,6 @@ public final class SvgIcons {
|
|||||||
return box;
|
return box;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Ícone de Sol (Light Mode).
|
|
||||||
*/
|
|
||||||
public static Node sunIcon(double size) {
|
|
||||||
return AssetManager.getIconOrDefault("sun", size, () -> buildSunIcon(size));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Node buildSunIcon(double size) {
|
|
||||||
StackPane box = new StackPane();
|
|
||||||
box.setPrefSize(size, size);
|
|
||||||
box.setMaxSize(size, size);
|
|
||||||
|
|
||||||
Circle sunCore = new Circle(size * 0.5, size * 0.5, size * 0.24);
|
|
||||||
sunCore.setFill(new LinearGradient(
|
|
||||||
0, 0, 1, 1, true, CycleMethod.NO_CYCLE,
|
|
||||||
new Stop(0, Color.web("#f59e0b")),
|
|
||||||
new Stop(1, Color.web("#d97706"))
|
|
||||||
));
|
|
||||||
|
|
||||||
SVGPath rays = new SVGPath();
|
|
||||||
rays.setContent("M 12 2 L 12 5 M 12 19 L 12 22 M 2 12 L 5 12 M 19 12 L 22 12 " +
|
|
||||||
"M 4.93 4.93 L 7.05 7.05 M 16.95 16.95 L 19.07 19.07 " +
|
|
||||||
"M 4.93 19.07 L 7.05 16.95 M 16.95 7.05 L 19.07 4.93");
|
|
||||||
rays.setStroke(Color.web("#f59e0b"));
|
|
||||||
rays.setStrokeWidth(2.0);
|
|
||||||
rays.setScaleX(size / 24.0);
|
|
||||||
rays.setScaleY(size / 24.0);
|
|
||||||
|
|
||||||
box.getChildren().addAll(rays, sunCore);
|
|
||||||
return box;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ícone de Lua (Dark Mode).
|
|
||||||
*/
|
|
||||||
public static Node moonIcon(double size) {
|
|
||||||
return AssetManager.getIconOrDefault("moon", size, () -> buildMoonIcon(size));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Node buildMoonIcon(double size) {
|
|
||||||
StackPane box = new StackPane();
|
|
||||||
box.setPrefSize(size, size);
|
|
||||||
box.setMaxSize(size, size);
|
|
||||||
|
|
||||||
SVGPath moon = new SVGPath();
|
|
||||||
moon.setContent("M 21 12.79 A 9 9 0 1 1 11.21 3 A 7 7 0 0 0 21 12.79 Z");
|
|
||||||
moon.setFill(new LinearGradient(
|
|
||||||
0, 0, 1, 1, true, CycleMethod.NO_CYCLE,
|
|
||||||
new Stop(0, Color.web("#38bdf8")),
|
|
||||||
new Stop(1, Color.web("#818cf8"))
|
|
||||||
));
|
|
||||||
moon.setScaleX(size / 24.0);
|
|
||||||
moon.setScaleY(size / 24.0);
|
|
||||||
|
|
||||||
box.getChildren().add(moon);
|
|
||||||
return box;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Logotipo vetorial CyberShield.
|
* Logotipo vetorial CyberShield.
|
||||||
@@ -681,23 +637,31 @@ public final class SvgIcons {
|
|||||||
StackPane box = new StackPane();
|
StackPane box = new StackPane();
|
||||||
box.setPrefSize(38, 38);
|
box.setPrefSize(38, 38);
|
||||||
|
|
||||||
SVGPath ribbon = new SVGPath();
|
SVGPath frame = new SVGPath();
|
||||||
ribbon.setContent("M 14 26 L 19 36 L 24 26 Z M 9 26 L 12 34 L 15 26 Z");
|
frame.setContent("M 6 8 C 4.9 8 4 8.9 4 10 L 4 28 C 4 29.1 4.9 30 6 30 L 32 30 C 33.1 30 34 29.1 34 28 L 34 10 C 34 8.9 33.1 8 32 8 Z");
|
||||||
ribbon.setFill(Color.web("#f59e0b"));
|
frame.setFill(Color.TRANSPARENT);
|
||||||
|
frame.setStroke(Color.web("#1e293b"));
|
||||||
|
frame.setStrokeWidth(1.8);
|
||||||
|
|
||||||
Circle sealCircle = new Circle(13);
|
SVGPath lines = new SVGPath();
|
||||||
sealCircle.setFill(Color.web("#d97706"));
|
lines.setContent("M 8 13 L 20 13 M 8 25 L 30 25");
|
||||||
sealCircle.setStroke(Color.web("#fbbf24"));
|
lines.setFill(Color.TRANSPARENT);
|
||||||
sealCircle.setStrokeWidth(1.6);
|
lines.setStroke(Color.web("#64748b"));
|
||||||
sealCircle.setTranslateY(-4);
|
lines.setStrokeWidth(1.4);
|
||||||
|
lines.setStrokeLineCap(StrokeLineCap.ROUND);
|
||||||
|
|
||||||
SVGPath star = new SVGPath();
|
Circle dot = new Circle(28, 13, 2);
|
||||||
star.setContent("M 19 8 L 21 13 L 26 13 L 22 16 L 24 21 L 19 18 L 14 21 L 16 16 L 12 13 L 17 13 Z");
|
dot.setFill(Color.web("#f59e0b"));
|
||||||
star.setFill(Color.web("#ffffff"));
|
|
||||||
star.setTranslateX(-9);
|
|
||||||
star.setTranslateY(-14);
|
|
||||||
|
|
||||||
box.getChildren().addAll(ribbon, sealCircle, star);
|
SVGPath signature = new SVGPath();
|
||||||
|
signature.setContent("M 8 20 C 11 16 13 16 15 20 C 17 23 20 17 23 18");
|
||||||
|
signature.setFill(Color.TRANSPARENT);
|
||||||
|
signature.setStroke(Color.web("#0288d1"));
|
||||||
|
signature.setStrokeWidth(2.0);
|
||||||
|
signature.setStrokeLineCap(StrokeLineCap.ROUND);
|
||||||
|
signature.setStrokeLineJoin(StrokeLineJoin.ROUND);
|
||||||
|
|
||||||
|
box.getChildren().addAll(frame, lines, dot, signature);
|
||||||
return box;
|
return box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,70 +4,19 @@ import br.com.cybercore.signer.ui.CyberCoreUiConstants;
|
|||||||
import javafx.scene.Parent;
|
import javafx.scene.Parent;
|
||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gerenciador centralizado de temas visuais do CyberCore Signer.
|
* Utilitário para aplicação da folha de estilo global do CyberCore Signer à Scene.
|
||||||
* Permite alternar dinamicamente entre Tema Claro (Light) e Tema Escuro (Dark)
|
* Gerencia a folha de estilo padrão única do aplicativo.
|
||||||
* aplicando as classes CSS e folhas de estilo correspondentes em qualquer Scene.
|
|
||||||
*/
|
*/
|
||||||
public final class ThemeManager {
|
public final class ThemeManager {
|
||||||
|
|
||||||
public enum AppTheme {
|
|
||||||
LIGHT("theme-light", "Modo Claro"),
|
|
||||||
DARK("theme-dark", "Modo Escuro");
|
|
||||||
|
|
||||||
private final String cssClass;
|
|
||||||
private final String displayName;
|
|
||||||
|
|
||||||
AppTheme(String cssClass, String displayName) {
|
|
||||||
this.cssClass = cssClass;
|
|
||||||
this.displayName = displayName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getCssClass() {
|
|
||||||
return cssClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getDisplayName() {
|
|
||||||
return displayName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static AppTheme currentTheme = AppTheme.LIGHT;
|
|
||||||
private static final List<Consumer<AppTheme>> listeners = new ArrayList<>();
|
|
||||||
|
|
||||||
private ThemeManager() {
|
private ThemeManager() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AppTheme getCurrentTheme() {
|
|
||||||
return currentTheme;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isDark() {
|
|
||||||
return currentTheme == AppTheme.DARK;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void setCurrentTheme(AppTheme theme) {
|
|
||||||
if (theme == null || theme == currentTheme) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
currentTheme = theme;
|
|
||||||
notifyListeners();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static AppTheme toggleTheme() {
|
|
||||||
currentTheme = (currentTheme == AppTheme.LIGHT) ? AppTheme.DARK : AppTheme.LIGHT;
|
|
||||||
notifyListeners();
|
|
||||||
return currentTheme;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Aplica o tema atualmente ativo a uma Scene, adicionando a folha de estilo global
|
* Aplica a folha de estilo padrão única a uma Scene.
|
||||||
* e ajustando a classe CSS raiz (theme-light / theme-dark).
|
|
||||||
*/
|
*/
|
||||||
public static void applyTheme(Scene scene) {
|
public static void applyTheme(Scene scene) {
|
||||||
if (scene == null) {
|
if (scene == null) {
|
||||||
@@ -81,40 +30,12 @@ public final class ThemeManager {
|
|||||||
if (!scene.getStylesheets().contains(cssUrl)) {
|
if (!scene.getStylesheets().contains(cssUrl)) {
|
||||||
scene.getStylesheets().add(cssUrl);
|
scene.getStylesheets().add(cssUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
Parent root = scene.getRoot();
|
|
||||||
if (root != null) {
|
|
||||||
applyThemeToNode(root);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Aplica a classe de estilo do tema ativo no nó raiz fornecido.
|
* Mantido para compatibilidade com chamadas de estilização de nós raiz.
|
||||||
*/
|
*/
|
||||||
public static void applyThemeToNode(Parent node) {
|
public static void applyThemeToNode(Parent node) {
|
||||||
if (node == null) {
|
// No tema padrão único definido em .root, a vinculação da folha na Scene é suficiente.
|
||||||
return;
|
|
||||||
}
|
|
||||||
node.getStyleClass().removeAll(AppTheme.LIGHT.getCssClass(), AppTheme.DARK.getCssClass());
|
|
||||||
node.getStyleClass().add(currentTheme.getCssClass());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void addListener(Consumer<AppTheme> listener) {
|
|
||||||
if (listener != null && !listeners.contains(listener)) {
|
|
||||||
listeners.add(listener);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void removeListener(Consumer<AppTheme> listener) {
|
|
||||||
listeners.remove(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void notifyListeners() {
|
|
||||||
for (Consumer<AppTheme> listener : new ArrayList<>(listeners)) {
|
|
||||||
try {
|
|
||||||
listener.accept(currentTheme);
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ public class A1PasswordDialog {
|
|||||||
VBox root = new VBox(16);
|
VBox root = new VBox(16);
|
||||||
root.getStyleClass().add("dialog-container");
|
root.getStyleClass().add("dialog-container");
|
||||||
root.setPadding(new Insets(24));
|
root.setPadding(new Insets(24));
|
||||||
root.setPrefWidth(440);
|
|
||||||
|
|
||||||
Label header = new Label("IMPORTAR CERTIFICADO A1");
|
Label header = new Label("IMPORTAR CERTIFICADO A1");
|
||||||
header.getStyleClass().add("dialog-header");
|
header.getStyleClass().add("dialog-header");
|
||||||
@@ -70,14 +69,19 @@ public class A1PasswordDialog {
|
|||||||
actionsRow.setAlignment(Pos.CENTER_RIGHT);
|
actionsRow.setAlignment(Pos.CENTER_RIGHT);
|
||||||
|
|
||||||
Button btnCancel = new Button("Cancelar");
|
Button btnCancel = new Button("Cancelar");
|
||||||
btnCancel.getStyleClass().add("btn-secondary");
|
btnCancel.getStyleClass().addAll("btn-ghost", "btn-dialog");
|
||||||
|
btnCancel.setMinWidth(javafx.scene.layout.Region.USE_PREF_SIZE);
|
||||||
btnCancel.setOnAction(e -> {
|
btnCancel.setOnAction(e -> {
|
||||||
passwordFuture.complete(null);
|
passwordFuture.complete(null);
|
||||||
stage.close();
|
stage.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
HBox spacer = new HBox();
|
||||||
|
HBox.setHgrow(spacer, Priority.ALWAYS);
|
||||||
|
|
||||||
Button btnConfirm = new Button("Importar");
|
Button btnConfirm = new Button("Importar");
|
||||||
btnConfirm.getStyleClass().add("btn-primary");
|
btnConfirm.getStyleClass().addAll("btn-primary", "btn-dialog");
|
||||||
|
btnConfirm.setMinWidth(javafx.scene.layout.Region.USE_PREF_SIZE);
|
||||||
btnConfirm.setDefaultButton(true);
|
btnConfirm.setDefaultButton(true);
|
||||||
btnConfirm.setOnAction(e -> {
|
btnConfirm.setOnAction(e -> {
|
||||||
String text = passwordField.getText();
|
String text = passwordField.getText();
|
||||||
@@ -85,7 +89,7 @@ public class A1PasswordDialog {
|
|||||||
stage.close();
|
stage.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
actionsRow.getChildren().addAll(btnCancel, btnConfirm);
|
actionsRow.getChildren().addAll(btnCancel, spacer, btnConfirm);
|
||||||
|
|
||||||
root.getChildren().addAll(header, desc, fileBox, inputGroup, actionsRow);
|
root.getChildren().addAll(header, desc, fileBox, inputGroup, actionsRow);
|
||||||
|
|
||||||
@@ -93,6 +97,7 @@ public class A1PasswordDialog {
|
|||||||
ThemeManager.applyTheme(scene);
|
ThemeManager.applyTheme(scene);
|
||||||
|
|
||||||
stage.setScene(scene);
|
stage.setScene(scene);
|
||||||
|
stage.sizeToScene();
|
||||||
stage.setOnCloseRequest(e -> passwordFuture.complete(null));
|
stage.setOnCloseRequest(e -> passwordFuture.complete(null));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,4 +105,8 @@ public class A1PasswordDialog {
|
|||||||
stage.show();
|
stage.show();
|
||||||
return passwordFuture;
|
return passwordFuture;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Stage getStage() {
|
||||||
|
return this.stage;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,20 +95,25 @@ public class ApplicationApprovalDialog {
|
|||||||
|
|
||||||
Button btnReject = new Button("Recusar Acesso");
|
Button btnReject = new Button("Recusar Acesso");
|
||||||
btnReject.getStyleClass().addAll("btn-danger-outline", "btn-dialog");
|
btnReject.getStyleClass().addAll("btn-danger-outline", "btn-dialog");
|
||||||
|
btnReject.setMinWidth(javafx.scene.layout.Region.USE_PREF_SIZE);
|
||||||
btnReject.setOnAction(e -> {
|
btnReject.setOnAction(e -> {
|
||||||
approvalFuture.complete(false);
|
approvalFuture.complete(false);
|
||||||
stage.close();
|
stage.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
HBox spacer = new HBox();
|
||||||
|
HBox.setHgrow(spacer, Priority.ALWAYS);
|
||||||
|
|
||||||
Button btnAuthorize = new Button("Autorizar Conexão");
|
Button btnAuthorize = new Button("Autorizar Conexão");
|
||||||
btnAuthorize.getStyleClass().addAll("btn-primary", "btn-dialog");
|
btnAuthorize.getStyleClass().addAll("btn-primary", "btn-dialog");
|
||||||
|
btnAuthorize.setMinWidth(javafx.scene.layout.Region.USE_PREF_SIZE);
|
||||||
btnAuthorize.setDefaultButton(true);
|
btnAuthorize.setDefaultButton(true);
|
||||||
btnAuthorize.setOnAction(e -> {
|
btnAuthorize.setOnAction(e -> {
|
||||||
approvalFuture.complete(true);
|
approvalFuture.complete(true);
|
||||||
stage.close();
|
stage.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
actionsRow.getChildren().addAll(btnReject, btnAuthorize);
|
actionsRow.getChildren().addAll(btnReject, spacer, btnAuthorize);
|
||||||
|
|
||||||
root.getChildren().addAll(header, detailsBox, secWarning, actionsRow);
|
root.getChildren().addAll(header, detailsBox, secWarning, actionsRow);
|
||||||
|
|
||||||
@@ -116,6 +121,7 @@ public class ApplicationApprovalDialog {
|
|||||||
ThemeManager.applyTheme(scene);
|
ThemeManager.applyTheme(scene);
|
||||||
|
|
||||||
stage.setScene(scene);
|
stage.setScene(scene);
|
||||||
|
stage.sizeToScene();
|
||||||
stage.setOnCloseRequest(e -> approvalFuture.complete(false));
|
stage.setOnCloseRequest(e -> approvalFuture.complete(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,4 +130,8 @@ public class ApplicationApprovalDialog {
|
|||||||
stage.toFront();
|
stage.toFront();
|
||||||
return approvalFuture;
|
return approvalFuture;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Stage getStage() {
|
||||||
|
return this.stage;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -245,6 +245,7 @@ public final class BatchSigningDialog {
|
|||||||
|
|
||||||
btnChooseOutputDir = new Button("Selecionar...");
|
btnChooseOutputDir = new Button("Selecionar...");
|
||||||
btnChooseOutputDir.getStyleClass().add("btn-secondary");
|
btnChooseOutputDir.getStyleClass().add("btn-secondary");
|
||||||
|
btnChooseOutputDir.setMinWidth(javafx.scene.layout.Region.USE_PREF_SIZE);
|
||||||
btnChooseOutputDir.setOnAction(e -> handleChooseOutputDir());
|
btnChooseOutputDir.setOnAction(e -> handleChooseOutputDir());
|
||||||
|
|
||||||
outputRow.getChildren().addAll(lblOutput, txtOutputDir, btnChooseOutputDir);
|
outputRow.getChildren().addAll(lblOutput, txtOutputDir, btnChooseOutputDir);
|
||||||
@@ -266,16 +267,21 @@ public final class BatchSigningDialog {
|
|||||||
footer.setAlignment(Pos.CENTER_RIGHT);
|
footer.setAlignment(Pos.CENTER_RIGHT);
|
||||||
|
|
||||||
Button btnClose = new Button("Fechar");
|
Button btnClose = new Button("Fechar");
|
||||||
btnClose.getStyleClass().add("btn-secondary");
|
btnClose.getStyleClass().addAll("btn-ghost", "btn-dialog");
|
||||||
|
btnClose.setMinWidth(javafx.scene.layout.Region.USE_PREF_SIZE);
|
||||||
btnClose.setOnAction(e -> {
|
btnClose.setOnAction(e -> {
|
||||||
if (!isRunning) stage.close();
|
if (!isRunning) stage.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
HBox spacer = new HBox();
|
||||||
|
HBox.setHgrow(spacer, Priority.ALWAYS);
|
||||||
|
|
||||||
btnStart = new Button("Iniciar Assinatura em Lote");
|
btnStart = new Button("Iniciar Assinatura em Lote");
|
||||||
btnStart.getStyleClass().add("btn-primary");
|
btnStart.getStyleClass().addAll("btn-primary", "btn-dialog");
|
||||||
|
btnStart.setMinWidth(javafx.scene.layout.Region.USE_PREF_SIZE);
|
||||||
btnStart.setOnAction(e -> startBatchSigning());
|
btnStart.setOnAction(e -> startBatchSigning());
|
||||||
|
|
||||||
footer.getChildren().addAll(btnClose, btnStart);
|
footer.getChildren().addAll(btnClose, spacer, btnStart);
|
||||||
|
|
||||||
root.getChildren().addAll(header, fileActions, tableView, configBox, progressBox, footer);
|
root.getChildren().addAll(header, fileActions, tableView, configBox, progressBox, footer);
|
||||||
|
|
||||||
@@ -283,6 +289,7 @@ public final class BatchSigningDialog {
|
|||||||
ThemeManager.applyTheme(scene);
|
ThemeManager.applyTheme(scene);
|
||||||
|
|
||||||
stage.setScene(scene);
|
stage.setScene(scene);
|
||||||
|
stage.sizeToScene();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleAddFiles() {
|
private void handleAddFiles() {
|
||||||
|
|||||||
@@ -129,8 +129,7 @@ public class CertificateDetailsDialog {
|
|||||||
|
|
||||||
Button btnClose = new Button(" Fechar", SvgIcons.closeIcon(13));
|
Button btnClose = new Button(" Fechar", SvgIcons.closeIcon(13));
|
||||||
btnClose.getStyleClass().addAll("btn-ghost", "btn-dialog");
|
btnClose.getStyleClass().addAll("btn-ghost", "btn-dialog");
|
||||||
btnClose.setPrefWidth(110);
|
btnClose.setMinWidth(javafx.scene.layout.Region.USE_PREF_SIZE);
|
||||||
btnClose.setMinWidth(110);
|
|
||||||
btnClose.setOnAction(e -> stage.close());
|
btnClose.setOnAction(e -> stage.close());
|
||||||
|
|
||||||
actionsRow.getChildren().add(btnClose);
|
actionsRow.getChildren().add(btnClose);
|
||||||
@@ -141,12 +140,17 @@ public class CertificateDetailsDialog {
|
|||||||
ThemeManager.applyTheme(scene);
|
ThemeManager.applyTheme(scene);
|
||||||
|
|
||||||
stage.setScene(scene);
|
stage.setScene(scene);
|
||||||
|
stage.sizeToScene();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void show() {
|
public void show() {
|
||||||
stage.show();
|
stage.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Stage getStage() {
|
||||||
|
return this.stage;
|
||||||
|
}
|
||||||
|
|
||||||
private void addField(GridPane grid, int row, String label, String value) {
|
private void addField(GridPane grid, int row, String label, String value) {
|
||||||
Label lbl = new Label(label);
|
Label lbl = new Label(label);
|
||||||
lbl.getStyleClass().add("dialog-field-title");
|
lbl.getStyleClass().add("dialog-field-title");
|
||||||
|
|||||||
@@ -75,10 +75,12 @@ public final class CertificatesManagerDialog {
|
|||||||
|
|
||||||
Button btnAddA1 = new Button("Importar A1 (.pfx / .p12)...");
|
Button btnAddA1 = new Button("Importar A1 (.pfx / .p12)...");
|
||||||
btnAddA1.getStyleClass().add("btn-primary");
|
btnAddA1.getStyleClass().add("btn-primary");
|
||||||
|
btnAddA1.setMinWidth(javafx.scene.layout.Region.USE_PREF_SIZE);
|
||||||
btnAddA1.setOnAction(e -> handleImportA1());
|
btnAddA1.setOnAction(e -> handleImportA1());
|
||||||
|
|
||||||
Button btnRefresh = new Button(" Atualizar Lista", SvgIcons.refreshIcon(14));
|
Button btnRefresh = new Button(" Atualizar Lista", SvgIcons.refreshIcon(14));
|
||||||
btnRefresh.getStyleClass().add("btn-secondary");
|
btnRefresh.getStyleClass().add("btn-secondary");
|
||||||
|
btnRefresh.setMinWidth(javafx.scene.layout.Region.USE_PREF_SIZE);
|
||||||
btnRefresh.setOnAction(e -> refreshList());
|
btnRefresh.setOnAction(e -> refreshList());
|
||||||
|
|
||||||
actions.getChildren().addAll(btnAddA1, btnRefresh);
|
actions.getChildren().addAll(btnAddA1, btnRefresh);
|
||||||
@@ -95,6 +97,7 @@ public final class CertificatesManagerDialog {
|
|||||||
footer.setAlignment(Pos.CENTER_RIGHT);
|
footer.setAlignment(Pos.CENTER_RIGHT);
|
||||||
Button btnClose = new Button(" Fechar", SvgIcons.closeIcon(13));
|
Button btnClose = new Button(" Fechar", SvgIcons.closeIcon(13));
|
||||||
btnClose.getStyleClass().add("btn-secondary");
|
btnClose.getStyleClass().add("btn-secondary");
|
||||||
|
btnClose.setMinWidth(javafx.scene.layout.Region.USE_PREF_SIZE);
|
||||||
btnClose.setOnAction(e -> stage.close());
|
btnClose.setOnAction(e -> stage.close());
|
||||||
footer.getChildren().add(btnClose);
|
footer.getChildren().add(btnClose);
|
||||||
|
|
||||||
@@ -104,6 +107,7 @@ public final class CertificatesManagerDialog {
|
|||||||
ThemeManager.applyTheme(scene);
|
ThemeManager.applyTheme(scene);
|
||||||
|
|
||||||
stage.setScene(scene);
|
stage.setScene(scene);
|
||||||
|
stage.sizeToScene();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void refreshList() {
|
private void refreshList() {
|
||||||
@@ -189,4 +193,8 @@ public final class CertificatesManagerDialog {
|
|||||||
public void show() {
|
public void show() {
|
||||||
stage.show();
|
stage.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Stage getStage() {
|
||||||
|
return this.stage;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,8 +37,6 @@ public final class ExitConfirmationDialog {
|
|||||||
}
|
}
|
||||||
this.stage.setTitle("CyberCore Signer — Encerrar ou Manter na Bandeja");
|
this.stage.setTitle("CyberCore Signer — Encerrar ou Manter na Bandeja");
|
||||||
this.stage.setResizable(false);
|
this.stage.setResizable(false);
|
||||||
this.stage.setMinWidth(520);
|
|
||||||
this.stage.setMinHeight(190);
|
|
||||||
|
|
||||||
initLayout();
|
initLayout();
|
||||||
}
|
}
|
||||||
@@ -71,6 +69,7 @@ public final class ExitConfirmationDialog {
|
|||||||
|
|
||||||
Button btnCancel = new Button("Cancelar");
|
Button btnCancel = new Button("Cancelar");
|
||||||
btnCancel.getStyleClass().addAll("btn-ghost", "btn-dialog");
|
btnCancel.getStyleClass().addAll("btn-ghost", "btn-dialog");
|
||||||
|
btnCancel.setMinWidth(javafx.scene.layout.Region.USE_PREF_SIZE);
|
||||||
btnCancel.setOnAction(e -> {
|
btnCancel.setOnAction(e -> {
|
||||||
chosenAction = ExitChoice.CANCEL;
|
chosenAction = ExitChoice.CANCEL;
|
||||||
stage.close();
|
stage.close();
|
||||||
@@ -81,6 +80,7 @@ public final class ExitConfirmationDialog {
|
|||||||
|
|
||||||
Button btnExit = new Button("Encerrar Definitivamente");
|
Button btnExit = new Button("Encerrar Definitivamente");
|
||||||
btnExit.getStyleClass().addAll("btn-danger-outline", "btn-dialog");
|
btnExit.getStyleClass().addAll("btn-danger-outline", "btn-dialog");
|
||||||
|
btnExit.setMinWidth(javafx.scene.layout.Region.USE_PREF_SIZE);
|
||||||
btnExit.setOnAction(e -> {
|
btnExit.setOnAction(e -> {
|
||||||
chosenAction = ExitChoice.EXIT_COMPLETELY;
|
chosenAction = ExitChoice.EXIT_COMPLETELY;
|
||||||
stage.close();
|
stage.close();
|
||||||
@@ -88,6 +88,7 @@ public final class ExitConfirmationDialog {
|
|||||||
|
|
||||||
Button btnMinimize = new Button("Minimizar para a Bandeja");
|
Button btnMinimize = new Button("Minimizar para a Bandeja");
|
||||||
btnMinimize.getStyleClass().addAll("btn-primary", "btn-dialog");
|
btnMinimize.getStyleClass().addAll("btn-primary", "btn-dialog");
|
||||||
|
btnMinimize.setMinWidth(javafx.scene.layout.Region.USE_PREF_SIZE);
|
||||||
btnMinimize.setDefaultButton(true);
|
btnMinimize.setDefaultButton(true);
|
||||||
btnMinimize.setOnAction(e -> {
|
btnMinimize.setOnAction(e -> {
|
||||||
chosenAction = ExitChoice.MINIMIZE_TO_TRAY;
|
chosenAction = ExitChoice.MINIMIZE_TO_TRAY;
|
||||||
@@ -101,6 +102,7 @@ public final class ExitConfirmationDialog {
|
|||||||
Scene scene = new Scene(root);
|
Scene scene = new Scene(root);
|
||||||
ThemeManager.applyTheme(scene);
|
ThemeManager.applyTheme(scene);
|
||||||
stage.setScene(scene);
|
stage.setScene(scene);
|
||||||
|
stage.sizeToScene();
|
||||||
stage.setOnCloseRequest(e -> chosenAction = ExitChoice.CANCEL);
|
stage.setOnCloseRequest(e -> chosenAction = ExitChoice.CANCEL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -112,6 +112,7 @@ public final class FileSignDialog {
|
|||||||
|
|
||||||
Button btnBrowse = new Button("Procurar...");
|
Button btnBrowse = new Button("Procurar...");
|
||||||
btnBrowse.getStyleClass().add("btn-secondary");
|
btnBrowse.getStyleClass().add("btn-secondary");
|
||||||
|
btnBrowse.setMinWidth(javafx.scene.layout.Region.USE_PREF_SIZE);
|
||||||
btnBrowse.setOnAction(e -> handleBrowseFile());
|
btnBrowse.setOnAction(e -> handleBrowseFile());
|
||||||
|
|
||||||
fileRow.getChildren().addAll(txtFilePath, btnBrowse);
|
fileRow.getChildren().addAll(txtFilePath, btnBrowse);
|
||||||
@@ -159,15 +160,20 @@ public final class FileSignDialog {
|
|||||||
footer.setAlignment(Pos.CENTER_RIGHT);
|
footer.setAlignment(Pos.CENTER_RIGHT);
|
||||||
|
|
||||||
Button btnCancel = new Button("Cancelar");
|
Button btnCancel = new Button("Cancelar");
|
||||||
btnCancel.getStyleClass().add("btn-secondary");
|
btnCancel.getStyleClass().addAll("btn-ghost", "btn-dialog");
|
||||||
|
btnCancel.setMinWidth(javafx.scene.layout.Region.USE_PREF_SIZE);
|
||||||
btnCancel.setOnAction(e -> stage.close());
|
btnCancel.setOnAction(e -> stage.close());
|
||||||
|
|
||||||
|
HBox spacer = new HBox();
|
||||||
|
HBox.setHgrow(spacer, Priority.ALWAYS);
|
||||||
|
|
||||||
Button btnSign = new Button("Assinar Documento");
|
Button btnSign = new Button("Assinar Documento");
|
||||||
btnSign.getStyleClass().add("btn-primary");
|
btnSign.getStyleClass().addAll("btn-primary", "btn-dialog");
|
||||||
|
btnSign.setMinWidth(javafx.scene.layout.Region.USE_PREF_SIZE);
|
||||||
btnSign.setDefaultButton(true);
|
btnSign.setDefaultButton(true);
|
||||||
btnSign.setOnAction(e -> executeSignature());
|
btnSign.setOnAction(e -> executeSignature());
|
||||||
|
|
||||||
footer.getChildren().addAll(btnCancel, btnSign);
|
footer.getChildren().addAll(btnCancel, spacer, btnSign);
|
||||||
|
|
||||||
root.getChildren().addAll(header, fileBox, certBox, footer);
|
root.getChildren().addAll(header, fileBox, certBox, footer);
|
||||||
|
|
||||||
@@ -175,6 +181,7 @@ public final class FileSignDialog {
|
|||||||
ThemeManager.applyTheme(scene);
|
ThemeManager.applyTheme(scene);
|
||||||
|
|
||||||
stage.setScene(scene);
|
stage.setScene(scene);
|
||||||
|
stage.sizeToScene();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleBrowseFile() {
|
private void handleBrowseFile() {
|
||||||
@@ -257,4 +264,8 @@ public final class FileSignDialog {
|
|||||||
public void show() {
|
public void show() {
|
||||||
stage.show();
|
stage.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Stage getStage() {
|
||||||
|
return this.stage;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ public final class LogsViewerDialog {
|
|||||||
|
|
||||||
Button btnCopy = new Button("Copiar");
|
Button btnCopy = new Button("Copiar");
|
||||||
btnCopy.getStyleClass().addAll("btn-secondary", "btn-dialog");
|
btnCopy.getStyleClass().addAll("btn-secondary", "btn-dialog");
|
||||||
btnCopy.setPrefWidth(90);
|
btnCopy.setMinWidth(javafx.scene.layout.Region.USE_PREF_SIZE);
|
||||||
btnCopy.setOnAction(e -> {
|
btnCopy.setOnAction(e -> {
|
||||||
javafx.scene.input.ClipboardContent content = new javafx.scene.input.ClipboardContent();
|
javafx.scene.input.ClipboardContent content = new javafx.scene.input.ClipboardContent();
|
||||||
content.putString(txtLogs.getText());
|
content.putString(txtLogs.getText());
|
||||||
@@ -77,7 +77,7 @@ public final class LogsViewerDialog {
|
|||||||
|
|
||||||
Button btnClear = new Button("Limpar");
|
Button btnClear = new Button("Limpar");
|
||||||
btnClear.getStyleClass().addAll("btn-secondary", "btn-dialog");
|
btnClear.getStyleClass().addAll("btn-secondary", "btn-dialog");
|
||||||
btnClear.setPrefWidth(90);
|
btnClear.setMinWidth(javafx.scene.layout.Region.USE_PREF_SIZE);
|
||||||
btnClear.setOnAction(e -> {
|
btnClear.setOnAction(e -> {
|
||||||
br.com.cybercore.signer.core.log.AppLogBuffer.clear();
|
br.com.cybercore.signer.core.log.AppLogBuffer.clear();
|
||||||
loadLogs();
|
loadLogs();
|
||||||
@@ -85,15 +85,18 @@ public final class LogsViewerDialog {
|
|||||||
|
|
||||||
Button btnRefresh = new Button("Atualizar");
|
Button btnRefresh = new Button("Atualizar");
|
||||||
btnRefresh.getStyleClass().addAll("btn-secondary", "btn-dialog");
|
btnRefresh.getStyleClass().addAll("btn-secondary", "btn-dialog");
|
||||||
btnRefresh.setPrefWidth(100);
|
btnRefresh.setMinWidth(javafx.scene.layout.Region.USE_PREF_SIZE);
|
||||||
btnRefresh.setOnAction(e -> loadLogs());
|
btnRefresh.setOnAction(e -> loadLogs());
|
||||||
|
|
||||||
|
HBox spacer = new HBox();
|
||||||
|
HBox.setHgrow(spacer, Priority.ALWAYS);
|
||||||
|
|
||||||
Button btnClose = new Button(" Fechar", SvgIcons.closeIcon(13));
|
Button btnClose = new Button(" Fechar", SvgIcons.closeIcon(13));
|
||||||
btnClose.getStyleClass().addAll("btn-secondary", "btn-dialog");
|
btnClose.getStyleClass().addAll("btn-ghost", "btn-dialog");
|
||||||
btnClose.setPrefWidth(100);
|
btnClose.setMinWidth(javafx.scene.layout.Region.USE_PREF_SIZE);
|
||||||
btnClose.setOnAction(e -> stage.close());
|
btnClose.setOnAction(e -> stage.close());
|
||||||
|
|
||||||
footer.getChildren().addAll(btnCopy, btnClear, btnRefresh, btnClose);
|
footer.getChildren().addAll(btnCopy, btnClear, btnRefresh, spacer, btnClose);
|
||||||
|
|
||||||
root.getChildren().addAll(header, txtLogs, footer);
|
root.getChildren().addAll(header, txtLogs, footer);
|
||||||
|
|
||||||
@@ -101,6 +104,7 @@ public final class LogsViewerDialog {
|
|||||||
ThemeManager.applyTheme(scene);
|
ThemeManager.applyTheme(scene);
|
||||||
|
|
||||||
stage.setScene(scene);
|
stage.setScene(scene);
|
||||||
|
stage.sizeToScene();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadLogs() {
|
private void loadLogs() {
|
||||||
@@ -127,4 +131,8 @@ public final class LogsViewerDialog {
|
|||||||
public void show() {
|
public void show() {
|
||||||
stage.show();
|
stage.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Stage getStage() {
|
||||||
|
return this.stage;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ public final class MainAppView {
|
|||||||
|
|
||||||
private final Stage stage;
|
private final Stage stage;
|
||||||
private final VBox root = new VBox(16);
|
private final VBox root = new VBox(16);
|
||||||
private final Button themeToggleBtn = new Button();
|
|
||||||
|
|
||||||
private Runnable onSignPdf;
|
private Runnable onSignPdf;
|
||||||
private Runnable onSignXml;
|
private Runnable onSignXml;
|
||||||
@@ -82,9 +81,19 @@ public final class MainAppView {
|
|||||||
ColumnConstraints col = new ColumnConstraints();
|
ColumnConstraints col = new ColumnConstraints();
|
||||||
col.setHgrow(Priority.ALWAYS);
|
col.setHgrow(Priority.ALWAYS);
|
||||||
col.setPercentWidth(33.33);
|
col.setPercentWidth(33.33);
|
||||||
|
col.setFillWidth(true);
|
||||||
actionGrid.getColumnConstraints().add(col);
|
actionGrid.getColumnConstraints().add(col);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 2; i++) {
|
||||||
|
javafx.scene.layout.RowConstraints row = new javafx.scene.layout.RowConstraints();
|
||||||
|
row.setVgrow(Priority.NEVER);
|
||||||
|
row.setMinHeight(68);
|
||||||
|
row.setPrefHeight(Region.USE_COMPUTED_SIZE);
|
||||||
|
row.setFillHeight(true);
|
||||||
|
actionGrid.getRowConstraints().add(row);
|
||||||
|
}
|
||||||
|
|
||||||
// Linha 0: Operações de Assinatura
|
// Linha 0: Operações de Assinatura
|
||||||
actionGrid.add(createActionButton(Icons.pdf(Icons.SIZE_40), "Assinar\nPDF", () -> trigger(onSignPdf)), 0, 0);
|
actionGrid.add(createActionButton(Icons.pdf(Icons.SIZE_40), "Assinar\nPDF", () -> trigger(onSignPdf)), 0, 0);
|
||||||
actionGrid.add(createActionButton(Icons.xml(Icons.SIZE_40), "Assinar\nXML", () -> trigger(onSignXml)), 1, 0);
|
actionGrid.add(createActionButton(Icons.xml(Icons.SIZE_40), "Assinar\nXML", () -> trigger(onSignXml)), 1, 0);
|
||||||
@@ -148,11 +157,8 @@ public final class MainAppView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configuração e inicialização do Tema
|
// Configuração e inicialização da folha de estilos global
|
||||||
ThemeManager.applyTheme(scene);
|
ThemeManager.applyTheme(scene);
|
||||||
updateThemeToggleButton();
|
|
||||||
|
|
||||||
ThemeManager.addListener(theme -> updateThemeToggleButton());
|
|
||||||
|
|
||||||
stage.setTitle(CyberCoreUiConstants.APP_NAME);
|
stage.setTitle(CyberCoreUiConstants.APP_NAME);
|
||||||
stage.getIcons().addAll(br.com.cybercore.signer.ui.assets.AssetManager.getApplicationIcons());
|
stage.getIcons().addAll(br.com.cybercore.signer.ui.assets.AssetManager.getApplicationIcons());
|
||||||
@@ -207,44 +213,16 @@ public final class MainAppView {
|
|||||||
Label statusBadge = new Label("● Agente Ativo • 127.0.0.1:21325");
|
Label statusBadge = new Label("● Agente Ativo • 127.0.0.1:21325");
|
||||||
statusBadge.getStyleClass().add("cyber-status-badge");
|
statusBadge.getStyleClass().add("cyber-status-badge");
|
||||||
|
|
||||||
// Botão Alternador de Tema (Claro / Escuro)
|
headerBar.getChildren().addAll(brandLogo, titleBox, leftSpacer, statusBadge);
|
||||||
themeToggleBtn.getStyleClass().add("theme-toggle-btn");
|
|
||||||
themeToggleBtn.setOnAction(e -> {
|
|
||||||
ThemeManager.toggleTheme();
|
|
||||||
if (stage.getScene() != null) {
|
|
||||||
ThemeManager.applyTheme(stage.getScene());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
headerBar.getChildren().addAll(brandLogo, titleBox, leftSpacer, statusBadge, themeToggleBtn);
|
|
||||||
return headerBar;
|
return headerBar;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateThemeToggleButton() {
|
|
||||||
boolean dark = ThemeManager.isDark();
|
|
||||||
HBox content = new HBox(8);
|
|
||||||
content.setAlignment(Pos.CENTER);
|
|
||||||
|
|
||||||
Node icon = dark ? Icons.sun(16) : Icons.moon(16);
|
|
||||||
StackPane iconBox = new StackPane(icon);
|
|
||||||
iconBox.setPrefSize(18, 18);
|
|
||||||
iconBox.setMinSize(18, 18);
|
|
||||||
iconBox.setMaxSize(18, 18);
|
|
||||||
iconBox.setAlignment(Pos.CENTER);
|
|
||||||
|
|
||||||
Label label = new Label(dark ? "Modo Claro" : "Modo Escuro");
|
|
||||||
label.getStyleClass().add("text-bold");
|
|
||||||
|
|
||||||
content.getChildren().addAll(iconBox, label);
|
|
||||||
themeToggleBtn.setGraphic(content);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Button createActionButton(Node icon, String text, Runnable onAction) {
|
private Button createActionButton(Node icon, String text, Runnable onAction) {
|
||||||
Button btn = new Button();
|
Button btn = new Button();
|
||||||
btn.getStyleClass().add("cyber-action-card");
|
btn.getStyleClass().add("cyber-action-card");
|
||||||
btn.setMaxWidth(Double.MAX_VALUE);
|
btn.setMaxWidth(Double.MAX_VALUE);
|
||||||
btn.setPrefHeight(72);
|
btn.setPrefHeight(68);
|
||||||
btn.setMinHeight(68);
|
btn.setMinHeight(Region.USE_PREF_SIZE);
|
||||||
|
|
||||||
HBox content = new HBox(12);
|
HBox content = new HBox(12);
|
||||||
content.setAlignment(Pos.CENTER_LEFT);
|
content.setAlignment(Pos.CENTER_LEFT);
|
||||||
@@ -277,7 +255,7 @@ public final class MainAppView {
|
|||||||
Button btn = new Button();
|
Button btn = new Button();
|
||||||
btn.getStyleClass().add("cyber-side-card");
|
btn.getStyleClass().add("cyber-side-card");
|
||||||
btn.setPrefHeight(42);
|
btn.setPrefHeight(42);
|
||||||
btn.setMinHeight(42);
|
btn.setMinHeight(40);
|
||||||
btn.setMaxWidth(Double.MAX_VALUE);
|
btn.setMaxWidth(Double.MAX_VALUE);
|
||||||
|
|
||||||
HBox content = new HBox(12);
|
HBox content = new HBox(12);
|
||||||
|
|||||||
@@ -304,13 +304,12 @@ public final class PdfCertificateSignDialog {
|
|||||||
|
|
||||||
btnCancel = new Button("Cancelar");
|
btnCancel = new Button("Cancelar");
|
||||||
btnCancel.getStyleClass().addAll("btn-ghost", "btn-dialog");
|
btnCancel.getStyleClass().addAll("btn-ghost", "btn-dialog");
|
||||||
btnCancel.setMinWidth(110);
|
btnCancel.setMinWidth(javafx.scene.layout.Region.USE_PREF_SIZE);
|
||||||
btnCancel.setOnAction(e -> stage.close());
|
btnCancel.setOnAction(e -> stage.close());
|
||||||
|
|
||||||
btnSign = new Button("Assinar Documento PDF");
|
btnSign = new Button("Assinar Documento PDF");
|
||||||
btnSign.getStyleClass().addAll("btn-primary", "btn-dialog-wide");
|
btnSign.getStyleClass().addAll("btn-primary", "btn-dialog-wide");
|
||||||
btnSign.setMinWidth(220);
|
btnSign.setMinWidth(javafx.scene.layout.Region.USE_PREF_SIZE);
|
||||||
btnSign.setPrefWidth(Region.USE_COMPUTED_SIZE);
|
|
||||||
btnSign.setDefaultButton(true);
|
btnSign.setDefaultButton(true);
|
||||||
btnSign.setOnAction(e -> handleSign());
|
btnSign.setOnAction(e -> handleSign());
|
||||||
|
|
||||||
@@ -318,9 +317,10 @@ public final class PdfCertificateSignDialog {
|
|||||||
|
|
||||||
root.getChildren().addAll(header, summaryBox, certSection, footer);
|
root.getChildren().addAll(header, summaryBox, certSection, footer);
|
||||||
|
|
||||||
Scene scene = new Scene(root, 720, 500);
|
Scene scene = new Scene(root);
|
||||||
ThemeManager.applyTheme(scene);
|
ThemeManager.applyTheme(scene);
|
||||||
stage.setScene(scene);
|
stage.setScene(scene);
|
||||||
|
stage.sizeToScene();
|
||||||
|
|
||||||
updatePinFieldVisibility();
|
updatePinFieldVisibility();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -270,20 +270,13 @@ public final class PdfSignDialog {
|
|||||||
|
|
||||||
btnCancel = new Button("Cancelar");
|
btnCancel = new Button("Cancelar");
|
||||||
btnCancel.getStyleClass().addAll("btn-ghost", "btn-dialog");
|
btnCancel.getStyleClass().addAll("btn-ghost", "btn-dialog");
|
||||||
btnCancel.setMinWidth(110);
|
btnCancel.setMinWidth(Region.USE_PREF_SIZE);
|
||||||
btnCancel.setOnAction(e -> stage.close());
|
btnCancel.setOnAction(e -> stage.close());
|
||||||
|
|
||||||
HBox btnNextContent = new HBox(8);
|
btnNextToCert = new Button("Avançar", SvgIcons.arrowRightIcon(12));
|
||||||
btnNextContent.setAlignment(Pos.CENTER);
|
btnNextToCert.setContentDisplay(javafx.scene.control.ContentDisplay.RIGHT);
|
||||||
Label lblBtnNextText = new Label("Avançar");
|
|
||||||
lblBtnNextText.getStyleClass().add("text-bold");
|
|
||||||
btnNextContent.getChildren().addAll(lblBtnNextText, SvgIcons.arrowRightIcon(12));
|
|
||||||
|
|
||||||
btnNextToCert = new Button();
|
|
||||||
btnNextToCert.setGraphic(btnNextContent);
|
|
||||||
btnNextToCert.getStyleClass().addAll("btn-primary", "btn-dialog-wide");
|
btnNextToCert.getStyleClass().addAll("btn-primary", "btn-dialog-wide");
|
||||||
btnNextToCert.setMinWidth(240);
|
btnNextToCert.setMinWidth(Region.USE_PREF_SIZE);
|
||||||
btnNextToCert.setPrefWidth(250);
|
|
||||||
btnNextToCert.setDefaultButton(true);
|
btnNextToCert.setDefaultButton(true);
|
||||||
btnNextToCert.setOnAction(e -> handleAdvanceToCertificate());
|
btnNextToCert.setOnAction(e -> handleAdvanceToCertificate());
|
||||||
|
|
||||||
@@ -297,6 +290,7 @@ public final class PdfSignDialog {
|
|||||||
Scene scene = new Scene(root, 1020, 680);
|
Scene scene = new Scene(root, 1020, 680);
|
||||||
ThemeManager.applyTheme(scene);
|
ThemeManager.applyTheme(scene);
|
||||||
stage.setScene(scene);
|
stage.setScene(scene);
|
||||||
|
stage.sizeToScene();
|
||||||
|
|
||||||
updateSealPreview();
|
updateSealPreview();
|
||||||
}
|
}
|
||||||
@@ -435,6 +429,7 @@ public final class PdfSignDialog {
|
|||||||
|
|
||||||
btnPrevPage = new Button("◀");
|
btnPrevPage = new Button("◀");
|
||||||
btnPrevPage.getStyleClass().addAll("btn-secondary", "btn-compact");
|
btnPrevPage.getStyleClass().addAll("btn-secondary", "btn-compact");
|
||||||
|
btnPrevPage.setMinWidth(Region.USE_PREF_SIZE);
|
||||||
btnPrevPage.setTooltip(new Tooltip("Página Anterior"));
|
btnPrevPage.setTooltip(new Tooltip("Página Anterior"));
|
||||||
btnPrevPage.setDisable(true);
|
btnPrevPage.setDisable(true);
|
||||||
btnPrevPage.setOnAction(e -> navigatePage(-1));
|
btnPrevPage.setOnAction(e -> navigatePage(-1));
|
||||||
@@ -444,6 +439,7 @@ public final class PdfSignDialog {
|
|||||||
|
|
||||||
btnNextPage = new Button("▶");
|
btnNextPage = new Button("▶");
|
||||||
btnNextPage.getStyleClass().addAll("btn-secondary", "btn-compact");
|
btnNextPage.getStyleClass().addAll("btn-secondary", "btn-compact");
|
||||||
|
btnNextPage.setMinWidth(Region.USE_PREF_SIZE);
|
||||||
btnNextPage.setTooltip(new Tooltip("Próxima Página"));
|
btnNextPage.setTooltip(new Tooltip("Próxima Página"));
|
||||||
btnNextPage.setDisable(true);
|
btnNextPage.setDisable(true);
|
||||||
btnNextPage.setOnAction(e -> navigatePage(1));
|
btnNextPage.setOnAction(e -> navigatePage(1));
|
||||||
@@ -462,6 +458,7 @@ public final class PdfSignDialog {
|
|||||||
btnZoomOut = new Button();
|
btnZoomOut = new Button();
|
||||||
btnZoomOut.setGraphic(SvgIcons.zoomOutIcon(13));
|
btnZoomOut.setGraphic(SvgIcons.zoomOutIcon(13));
|
||||||
btnZoomOut.getStyleClass().addAll("btn-secondary", "btn-compact");
|
btnZoomOut.getStyleClass().addAll("btn-secondary", "btn-compact");
|
||||||
|
btnZoomOut.setMinWidth(Region.USE_PREF_SIZE);
|
||||||
btnZoomOut.setTooltip(new Tooltip("Diminuir Zoom"));
|
btnZoomOut.setTooltip(new Tooltip("Diminuir Zoom"));
|
||||||
btnZoomOut.setOnAction(e -> adjustZoom(-ZOOM_STEP));
|
btnZoomOut.setOnAction(e -> adjustZoom(-ZOOM_STEP));
|
||||||
|
|
||||||
@@ -471,12 +468,14 @@ public final class PdfSignDialog {
|
|||||||
btnZoomIn = new Button();
|
btnZoomIn = new Button();
|
||||||
btnZoomIn.setGraphic(SvgIcons.zoomInIcon(13));
|
btnZoomIn.setGraphic(SvgIcons.zoomInIcon(13));
|
||||||
btnZoomIn.getStyleClass().addAll("btn-secondary", "btn-compact");
|
btnZoomIn.getStyleClass().addAll("btn-secondary", "btn-compact");
|
||||||
|
btnZoomIn.setMinWidth(Region.USE_PREF_SIZE);
|
||||||
btnZoomIn.setTooltip(new Tooltip("Aumentar Zoom"));
|
btnZoomIn.setTooltip(new Tooltip("Aumentar Zoom"));
|
||||||
btnZoomIn.setOnAction(e -> adjustZoom(ZOOM_STEP));
|
btnZoomIn.setOnAction(e -> adjustZoom(ZOOM_STEP));
|
||||||
|
|
||||||
btnZoomFit = new Button("Ajustar");
|
btnZoomFit = new Button("Ajustar");
|
||||||
btnZoomFit.setGraphic(SvgIcons.zoomFitIcon(12));
|
btnZoomFit.setGraphic(SvgIcons.zoomFitIcon(12));
|
||||||
btnZoomFit.getStyleClass().addAll("btn-secondary", "btn-compact");
|
btnZoomFit.getStyleClass().addAll("btn-secondary", "btn-compact");
|
||||||
|
btnZoomFit.setMinWidth(Region.USE_PREF_SIZE);
|
||||||
btnZoomFit.setTooltip(new Tooltip("Ajustar à janela"));
|
btnZoomFit.setTooltip(new Tooltip("Ajustar à janela"));
|
||||||
btnZoomFit.setOnAction(e -> setZoom(1.0));
|
btnZoomFit.setOnAction(e -> setZoom(1.0));
|
||||||
|
|
||||||
@@ -489,6 +488,7 @@ public final class PdfSignDialog {
|
|||||||
|
|
||||||
btnToggleResize = new Button(" Redimensionar", SvgIcons.zoomFitIcon(13));
|
btnToggleResize = new Button(" Redimensionar", SvgIcons.zoomFitIcon(13));
|
||||||
btnToggleResize.getStyleClass().addAll("btn-secondary", "btn-compact");
|
btnToggleResize.getStyleClass().addAll("btn-secondary", "btn-compact");
|
||||||
|
btnToggleResize.setMinWidth(Region.USE_PREF_SIZE);
|
||||||
btnToggleResize.setTooltip(new Tooltip("Ativar/desativar alça interativa para alterar o tamanho do selo"));
|
btnToggleResize.setTooltip(new Tooltip("Ativar/desativar alça interativa para alterar o tamanho do selo"));
|
||||||
btnToggleResize.setOnAction(e -> toggleResizeMode());
|
btnToggleResize.setOnAction(e -> toggleResizeMode());
|
||||||
|
|
||||||
@@ -497,6 +497,7 @@ public final class PdfSignDialog {
|
|||||||
|
|
||||||
btnResetSealScale = new Button(" 100%", SvgIcons.refreshIcon(12));
|
btnResetSealScale = new Button(" 100%", SvgIcons.refreshIcon(12));
|
||||||
btnResetSealScale.getStyleClass().addAll("btn-secondary", "btn-compact");
|
btnResetSealScale.getStyleClass().addAll("btn-secondary", "btn-compact");
|
||||||
|
btnResetSealScale.setMinWidth(Region.USE_PREF_SIZE);
|
||||||
btnResetSealScale.setTooltip(new Tooltip("Restaurar tamanho padrão original (100%)"));
|
btnResetSealScale.setTooltip(new Tooltip("Restaurar tamanho padrão original (100%)"));
|
||||||
btnResetSealScale.setVisible(false);
|
btnResetSealScale.setVisible(false);
|
||||||
btnResetSealScale.setOnAction(e -> applySealScale(1.0));
|
btnResetSealScale.setOnAction(e -> applySealScale(1.0));
|
||||||
@@ -683,11 +684,11 @@ public final class PdfSignDialog {
|
|||||||
if (btnToggleResize != null) {
|
if (btnToggleResize != null) {
|
||||||
if (isResizeModeActive) {
|
if (isResizeModeActive) {
|
||||||
btnToggleResize.setText("✓ Concluir");
|
btnToggleResize.setText("✓ Concluir");
|
||||||
btnToggleResize.getStyleClass().setAll("btn-primary");
|
btnToggleResize.getStyleClass().setAll("button", "btn-primary", "btn-compact");
|
||||||
lblPositionFeedback.setText("👉 Puxe a alça azul no canto do selo para ajustar o tamanho");
|
lblPositionFeedback.setText("👉 Puxe a alça azul no canto do selo para ajustar o tamanho");
|
||||||
} else {
|
} else {
|
||||||
btnToggleResize.setText("📏 Redimensionar");
|
btnToggleResize.setText("📏 Redimensionar");
|
||||||
btnToggleResize.getStyleClass().setAll("btn-secondary");
|
btnToggleResize.getStyleClass().setAll("button", "btn-secondary", "btn-compact");
|
||||||
updateFeedbackLabel();
|
updateFeedbackLabel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -383,24 +383,33 @@ public final class SealDesignerDialog {
|
|||||||
footer.setAlignment(Pos.CENTER_RIGHT);
|
footer.setAlignment(Pos.CENTER_RIGHT);
|
||||||
footer.setPadding(new Insets(6, 0, 0, 0));
|
footer.setPadding(new Insets(6, 0, 0, 0));
|
||||||
|
|
||||||
|
Region spacer = new Region();
|
||||||
|
HBox.setHgrow(spacer, Priority.ALWAYS);
|
||||||
|
|
||||||
Button btnCancel = new Button("Cancelar");
|
Button btnCancel = new Button("Cancelar");
|
||||||
btnCancel.getStyleClass().add("btn-secondary");
|
btnCancel.getStyleClass().add("btn-ghost");
|
||||||
|
btnCancel.setMinWidth(Region.USE_PREF_SIZE);
|
||||||
btnCancel.setPrefHeight(34);
|
btnCancel.setPrefHeight(34);
|
||||||
btnCancel.setOnAction(e -> stage.close());
|
btnCancel.setOnAction(e -> stage.close());
|
||||||
|
|
||||||
Button btnInstall = new Button("💾 Instalar Selo no Sistema");
|
Button btnInstall = new Button("💾 Instalar Selo no Sistema");
|
||||||
btnInstall.getStyleClass().add("btn-primary");
|
btnInstall.getStyleClass().add("btn-primary");
|
||||||
|
btnInstall.setMinWidth(Region.USE_PREF_SIZE);
|
||||||
btnInstall.setPrefHeight(34);
|
btnInstall.setPrefHeight(34);
|
||||||
btnInstall.setDefaultButton(true);
|
btnInstall.setDefaultButton(true);
|
||||||
btnInstall.setOnAction(e -> handleInstallSeal());
|
btnInstall.setOnAction(e -> handleInstallSeal());
|
||||||
|
|
||||||
footer.getChildren().addAll(btnCancel, btnInstall);
|
btnImport.setMinWidth(Region.USE_PREF_SIZE);
|
||||||
|
btnExport.setMinWidth(Region.USE_PREF_SIZE);
|
||||||
|
|
||||||
|
footer.getChildren().addAll(spacer, btnCancel, btnInstall);
|
||||||
|
|
||||||
root.getChildren().addAll(header, mainSplit, footer);
|
root.getChildren().addAll(header, mainSplit, footer);
|
||||||
|
|
||||||
Scene scene = new Scene(root, 880, 580);
|
Scene scene = new Scene(root, 880, 580);
|
||||||
ThemeManager.applyTheme(scene);
|
ThemeManager.applyTheme(scene);
|
||||||
stage.setScene(scene);
|
stage.setScene(scene);
|
||||||
|
stage.sizeToScene();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updatePreview() {
|
private void updatePreview() {
|
||||||
@@ -640,4 +649,8 @@ public final class SealDesignerDialog {
|
|||||||
stage.centerOnScreen();
|
stage.centerOnScreen();
|
||||||
stage.show();
|
stage.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Stage getStage() {
|
||||||
|
return this.stage;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -222,9 +222,8 @@ public final class SealManagementDialog {
|
|||||||
footer.setAlignment(Pos.CENTER_RIGHT);
|
footer.setAlignment(Pos.CENTER_RIGHT);
|
||||||
|
|
||||||
Button btnClose = new Button(" Fechar", SvgIcons.closeIcon(13));
|
Button btnClose = new Button(" Fechar", SvgIcons.closeIcon(13));
|
||||||
btnClose.getStyleClass().addAll("btn-secondary", "btn-dialog");
|
btnClose.getStyleClass().addAll("btn-ghost", "btn-dialog");
|
||||||
btnClose.setPrefWidth(110);
|
btnClose.setMinWidth(javafx.scene.layout.Region.USE_PREF_SIZE);
|
||||||
btnClose.setMinWidth(110);
|
|
||||||
btnClose.setOnAction(e -> stage.close());
|
btnClose.setOnAction(e -> stage.close());
|
||||||
|
|
||||||
footer.getChildren().add(btnClose);
|
footer.getChildren().add(btnClose);
|
||||||
@@ -234,6 +233,7 @@ public final class SealManagementDialog {
|
|||||||
Scene scene = new Scene(root, 880, 560);
|
Scene scene = new Scene(root, 880, 560);
|
||||||
ThemeManager.applyTheme(scene);
|
ThemeManager.applyTheme(scene);
|
||||||
stage.setScene(scene);
|
stage.setScene(scene);
|
||||||
|
stage.sizeToScene();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void refreshSealsList() {
|
private void refreshSealsList() {
|
||||||
@@ -441,6 +441,10 @@ public final class SealManagementDialog {
|
|||||||
stage.showAndWait();
|
stage.showAndWait();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Stage getStage() {
|
||||||
|
return this.stage;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Célula customizada para a listagem visual dos selos.
|
* Célula customizada para a listagem visual dos selos.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -66,7 +66,6 @@ public class SignaturePromptDialog {
|
|||||||
VBox root = new VBox(16);
|
VBox root = new VBox(16);
|
||||||
root.getStyleClass().add("dialog-container");
|
root.getStyleClass().add("dialog-container");
|
||||||
root.setPadding(new Insets(20, 24, 20, 24));
|
root.setPadding(new Insets(20, 24, 20, 24));
|
||||||
root.setPrefWidth(560);
|
|
||||||
|
|
||||||
HBox headerBox = new HBox(14);
|
HBox headerBox = new HBox(14);
|
||||||
headerBox.setAlignment(Pos.CENTER_LEFT);
|
headerBox.setAlignment(Pos.CENTER_LEFT);
|
||||||
@@ -240,14 +239,19 @@ public class SignaturePromptDialog {
|
|||||||
actionsRow.setPadding(new Insets(4, 0, 0, 0));
|
actionsRow.setPadding(new Insets(4, 0, 0, 0));
|
||||||
|
|
||||||
Button btnCancel = new Button("Cancelar");
|
Button btnCancel = new Button("Cancelar");
|
||||||
btnCancel.getStyleClass().add("btn-cancel");
|
btnCancel.getStyleClass().addAll("btn-ghost", "btn-dialog");
|
||||||
|
btnCancel.setMinWidth(javafx.scene.layout.Region.USE_PREF_SIZE);
|
||||||
btnCancel.setOnAction(e -> {
|
btnCancel.setOnAction(e -> {
|
||||||
approvalFuture.complete(false);
|
approvalFuture.complete(false);
|
||||||
stage.close();
|
stage.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
HBox spacer = new HBox();
|
||||||
|
HBox.setHgrow(spacer, Priority.ALWAYS);
|
||||||
|
|
||||||
Button btnConfirm = new Button(isAuthentication ? "Confirmar e Entrar" : "Confirmar e Assinar");
|
Button btnConfirm = new Button(isAuthentication ? "Confirmar e Entrar" : "Confirmar e Assinar");
|
||||||
btnConfirm.getStyleClass().add("btn-primary");
|
btnConfirm.getStyleClass().addAll("btn-primary", "btn-dialog");
|
||||||
|
btnConfirm.setMinWidth(javafx.scene.layout.Region.USE_PREF_SIZE);
|
||||||
btnConfirm.setDefaultButton(true);
|
btnConfirm.setDefaultButton(true);
|
||||||
btnConfirm.setOnAction(e -> {
|
btnConfirm.setOnAction(e -> {
|
||||||
if (isA3 && !isWindowsStore) {
|
if (isA3 && !isWindowsStore) {
|
||||||
@@ -277,7 +281,7 @@ public class SignaturePromptDialog {
|
|||||||
stage.close();
|
stage.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
actionsRow.getChildren().addAll(btnCancel, btnConfirm);
|
actionsRow.getChildren().addAll(btnCancel, spacer, btnConfirm);
|
||||||
|
|
||||||
if (isA3) {
|
if (isA3) {
|
||||||
root.getChildren().addAll(headerBox, detailsBox, pinContainer, validationLabel, actionsRow);
|
root.getChildren().addAll(headerBox, detailsBox, pinContainer, validationLabel, actionsRow);
|
||||||
@@ -335,4 +339,8 @@ public class SignaturePromptDialog {
|
|||||||
}
|
}
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Stage getStage() {
|
||||||
|
return this.stage;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import javafx.scene.control.TextField;
|
|||||||
import javafx.scene.control.ToggleGroup;
|
import javafx.scene.control.ToggleGroup;
|
||||||
import javafx.scene.layout.HBox;
|
import javafx.scene.layout.HBox;
|
||||||
import javafx.scene.layout.Priority;
|
import javafx.scene.layout.Priority;
|
||||||
|
import javafx.scene.layout.Region;
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
import javafx.stage.FileChooser;
|
import javafx.stage.FileChooser;
|
||||||
import javafx.stage.Modality;
|
import javafx.stage.Modality;
|
||||||
@@ -120,6 +121,7 @@ public final class SignatureValidationDialog {
|
|||||||
|
|
||||||
Button btnBrowseSig = new Button("Procurar...");
|
Button btnBrowseSig = new Button("Procurar...");
|
||||||
btnBrowseSig.getStyleClass().add("btn-secondary");
|
btnBrowseSig.getStyleClass().add("btn-secondary");
|
||||||
|
btnBrowseSig.setMinWidth(Region.USE_PREF_SIZE);
|
||||||
btnBrowseSig.setOnAction(e -> handleBrowseSig());
|
btnBrowseSig.setOnAction(e -> handleBrowseSig());
|
||||||
|
|
||||||
sigRow.getChildren().addAll(txtSigPath, btnBrowseSig);
|
sigRow.getChildren().addAll(txtSigPath, btnBrowseSig);
|
||||||
@@ -139,6 +141,7 @@ public final class SignatureValidationDialog {
|
|||||||
|
|
||||||
Button btnBrowseDoc = new Button("Procurar...");
|
Button btnBrowseDoc = new Button("Procurar...");
|
||||||
btnBrowseDoc.getStyleClass().add("btn-secondary");
|
btnBrowseDoc.getStyleClass().add("btn-secondary");
|
||||||
|
btnBrowseDoc.setMinWidth(Region.USE_PREF_SIZE);
|
||||||
btnBrowseDoc.setOnAction(e -> handleBrowseDoc());
|
btnBrowseDoc.setOnAction(e -> handleBrowseDoc());
|
||||||
|
|
||||||
docRow.getChildren().addAll(txtDocPath, btnBrowseDoc);
|
docRow.getChildren().addAll(txtDocPath, btnBrowseDoc);
|
||||||
@@ -161,28 +164,35 @@ public final class SignatureValidationDialog {
|
|||||||
|
|
||||||
btnSaveExtracted = new Button("Salvar Conteúdo Extraído...");
|
btnSaveExtracted = new Button("Salvar Conteúdo Extraído...");
|
||||||
btnSaveExtracted.getStyleClass().add("btn-secondary");
|
btnSaveExtracted.getStyleClass().add("btn-secondary");
|
||||||
|
btnSaveExtracted.setMinWidth(Region.USE_PREF_SIZE);
|
||||||
btnSaveExtracted.setVisible(false);
|
btnSaveExtracted.setVisible(false);
|
||||||
btnSaveExtracted.setManaged(false);
|
btnSaveExtracted.setManaged(false);
|
||||||
btnSaveExtracted.setOnAction(e -> handleSaveExtracted());
|
btnSaveExtracted.setOnAction(e -> handleSaveExtracted());
|
||||||
|
|
||||||
HBox.setHgrow(btnSaveExtracted, Priority.NEVER);
|
HBox.setHgrow(btnSaveExtracted, Priority.NEVER);
|
||||||
|
|
||||||
|
Region spacer = new Region();
|
||||||
|
HBox.setHgrow(spacer, Priority.ALWAYS);
|
||||||
|
|
||||||
Button btnClose = new Button("Fechar");
|
Button btnClose = new Button("Fechar");
|
||||||
btnClose.getStyleClass().add("btn-secondary");
|
btnClose.getStyleClass().add("btn-ghost");
|
||||||
|
btnClose.setMinWidth(Region.USE_PREF_SIZE);
|
||||||
btnClose.setOnAction(e -> stage.close());
|
btnClose.setOnAction(e -> stage.close());
|
||||||
|
|
||||||
Button btnValidate = new Button("Validar Agora");
|
Button btnValidate = new Button("Validar Agora");
|
||||||
btnValidate.getStyleClass().add("btn-primary");
|
btnValidate.getStyleClass().add("btn-primary");
|
||||||
|
btnValidate.setMinWidth(Region.USE_PREF_SIZE);
|
||||||
btnValidate.setDefaultButton(true);
|
btnValidate.setDefaultButton(true);
|
||||||
btnValidate.setOnAction(e -> handleValidate());
|
btnValidate.setOnAction(e -> handleValidate());
|
||||||
|
|
||||||
footer.getChildren().addAll(btnSaveExtracted, btnClose, btnValidate);
|
footer.getChildren().addAll(btnSaveExtracted, spacer, btnClose, btnValidate);
|
||||||
|
|
||||||
root.getChildren().addAll(header, typeBox, sigBox, docBox, scrollResult, footer);
|
root.getChildren().addAll(header, typeBox, sigBox, docBox, scrollResult, footer);
|
||||||
|
|
||||||
Scene scene = new Scene(root);
|
Scene scene = new Scene(root);
|
||||||
ThemeManager.applyTheme(scene);
|
ThemeManager.applyTheme(scene);
|
||||||
stage.setScene(scene);
|
stage.setScene(scene);
|
||||||
|
stage.sizeToScene();
|
||||||
|
|
||||||
updateModeView();
|
updateModeView();
|
||||||
}
|
}
|
||||||
@@ -382,4 +392,8 @@ public final class SignatureValidationDialog {
|
|||||||
public void show() {
|
public void show() {
|
||||||
stage.show();
|
stage.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Stage getStage() {
|
||||||
|
return this.stage;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,9 +77,8 @@ public final class TutorialDialog {
|
|||||||
HBox footer = new HBox();
|
HBox footer = new HBox();
|
||||||
footer.setAlignment(Pos.CENTER_RIGHT);
|
footer.setAlignment(Pos.CENTER_RIGHT);
|
||||||
Button btnClose = new Button(" Entendi", SvgIcons.closeIcon(13));
|
Button btnClose = new Button(" Entendi", SvgIcons.closeIcon(13));
|
||||||
btnClose.getStyleClass().addAll("btn-secondary", "btn-dialog");
|
btnClose.getStyleClass().addAll("btn-ghost", "btn-dialog");
|
||||||
btnClose.setPrefWidth(110);
|
btnClose.setMinWidth(javafx.scene.layout.Region.USE_PREF_SIZE);
|
||||||
btnClose.setMinWidth(110);
|
|
||||||
btnClose.setOnAction(e -> stage.close());
|
btnClose.setOnAction(e -> stage.close());
|
||||||
footer.getChildren().add(btnClose);
|
footer.getChildren().add(btnClose);
|
||||||
|
|
||||||
@@ -89,6 +88,7 @@ public final class TutorialDialog {
|
|||||||
ThemeManager.applyTheme(scene);
|
ThemeManager.applyTheme(scene);
|
||||||
|
|
||||||
stage.setScene(scene);
|
stage.setScene(scene);
|
||||||
|
stage.sizeToScene();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addStep(VBox container, String title, String description) {
|
private void addStep(VBox container, String title, String description) {
|
||||||
@@ -116,4 +116,8 @@ public final class TutorialDialog {
|
|||||||
public void show() {
|
public void show() {
|
||||||
stage.show();
|
stage.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Stage getStage() {
|
||||||
|
return this.stage;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -294,6 +294,7 @@ public final class XmlSignDialog {
|
|||||||
|
|
||||||
btnCancel = new Button("Cancelar");
|
btnCancel = new Button("Cancelar");
|
||||||
btnCancel.getStyleClass().addAll("btn-ghost", "btn-dialog");
|
btnCancel.getStyleClass().addAll("btn-ghost", "btn-dialog");
|
||||||
|
btnCancel.setMinWidth(Region.USE_PREF_SIZE);
|
||||||
btnCancel.setOnAction(e -> stage.close());
|
btnCancel.setOnAction(e -> stage.close());
|
||||||
|
|
||||||
btnSign = new Button("Assinar Documento XML");
|
btnSign = new Button("Assinar Documento XML");
|
||||||
@@ -309,6 +310,7 @@ public final class XmlSignDialog {
|
|||||||
Scene scene = new Scene(root);
|
Scene scene = new Scene(root);
|
||||||
ThemeManager.applyTheme(scene);
|
ThemeManager.applyTheme(scene);
|
||||||
stage.setScene(scene);
|
stage.setScene(scene);
|
||||||
|
stage.sizeToScene();
|
||||||
|
|
||||||
updatePinFieldVisibility();
|
updatePinFieldVisibility();
|
||||||
}
|
}
|
||||||
@@ -554,4 +556,8 @@ public final class XmlSignDialog {
|
|||||||
stage.show();
|
stage.show();
|
||||||
stage.toFront();
|
stage.toFront();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Stage getStage() {
|
||||||
|
return this.stage;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,8 +25,6 @@ assets/
|
|||||||
│ ├── tutorial.svg # Ícone Tutorial e Ajuda (monitor com vídeo/play)
|
│ ├── tutorial.svg # Ícone Tutorial e Ajuda (monitor com vídeo/play)
|
||||||
│ ├── cert.svg # Ícone Certificados Digitais (pasta com cadeado seguro)
|
│ ├── cert.svg # Ícone Certificados Digitais (pasta com cadeado seguro)
|
||||||
│ ├── about.svg # Ícone Sobre o Sistema (círculo azul com 'i')
|
│ ├── about.svg # Ícone Sobre o Sistema (círculo azul com 'i')
|
||||||
│ ├── sun.svg # Ícone Tema Claro (sol dourado com raios vetoriais)
|
|
||||||
│ ├── moon.svg # Ícone Tema Escuro (lua crescente azul/índigo)
|
|
||||||
│ ├── shield.svg # Ícone Brasão de Segurança (CyberShield com chave interna)
|
│ ├── shield.svg # Ícone Brasão de Segurança (CyberShield com chave interna)
|
||||||
│ ├── seal.svg # Ícone Designer e Gerenciador de Selos Notariais
|
│ ├── seal.svg # Ícone Designer e Gerenciador de Selos Notariais
|
||||||
│ ├── check.svg # Ícone Checkmark de sucesso e validação
|
│ ├── check.svg # Ícone Checkmark de sucesso e validação
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
||||||
<rect x="3" y="4" width="18" height="16" rx="2.5" fill="none" stroke="currentColor" stroke-width="2"/>
|
<rect x="3.5" y="2.5" width="17" height="19" rx="2.5" fill="none" stroke="currentColor" stroke-width="2"/>
|
||||||
<circle cx="12" cy="10" r="2.5" fill="none" stroke="#0288d1" stroke-width="1.8"/>
|
<line x1="7" y1="6.5" x2="13" y2="6.5" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/>
|
||||||
<path d="M 10.5 12.2 L 9.5 17 L 12 15.5 L 14.5 17 L 13.5 12.2" fill="none" stroke="#0288d1" stroke-width="1.8" stroke-linejoin="round"/>
|
<line x1="7" y1="9.5" x2="11" y2="9.5" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" opacity="0.6"/>
|
||||||
<line x1="6" y1="7" x2="8" y2="7" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/>
|
<circle cx="14.5" cy="14.5" r="3.5" fill="none" stroke="#0288d1" stroke-width="1.8"/>
|
||||||
<line x1="16" y1="7" x2="18" y2="7" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/>
|
<path d="M 13 14.5 L 14.2 15.7 L 16.5 13.2" fill="none" stroke="#0288d1" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 625 B After Width: | Height: | Size: 659 B |
@@ -1,3 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
|
||||||
<path d="M 12 3 A 9 9 0 1 0 21 12 A 7.5 7.5 0 0 1 12 3 Z" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 251 B |
@@ -1,5 +1,7 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
||||||
<circle cx="12" cy="10" r="7.5" fill="none" stroke="#f59e0b" stroke-width="2"/>
|
<rect x="2.5" y="4.5" width="19" height="15" rx="3" fill="none" stroke="currentColor" stroke-width="2"/>
|
||||||
<circle cx="12" cy="10" r="4.5" fill="none" stroke="#f59e0b" stroke-width="1.2" stroke-dasharray="2 1.5"/>
|
<path d="M 6 13.5 C 7.5 10 9 10 10.5 13 C 11.5 15 13 11 15 12" fill="none" stroke="#0288d1" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
<path d="M 9.5 15.5 L 7 21.5 L 12 19 L 17 21.5 L 14.5 15.5" fill="none" stroke="#d97706" stroke-width="1.8" stroke-linejoin="round"/>
|
<circle cx="17.5" cy="8.5" r="1.5" fill="#f59e0b"/>
|
||||||
|
<line x1="6" y1="8.5" x2="13" y2="8.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" opacity="0.5"/>
|
||||||
|
<line x1="6" y1="16" x2="18" y2="16" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" opacity="0.4"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 418 B After Width: | Height: | Size: 654 B |
@@ -1,11 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
|
||||||
<circle cx="12" cy="12" r="4.5" fill="none" stroke="currentColor" stroke-width="2"/>
|
|
||||||
<line x1="12" y1="2" x2="12" y2="4.5" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
|
||||||
<line x1="12" y1="19.5" x2="12" y2="22" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
|
||||||
<line x1="2" y1="12" x2="4.5" y2="12" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
|
||||||
<line x1="19.5" y1="12" x2="22" y2="12" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
|
||||||
<line x1="4.9" y1="4.9" x2="6.7" y2="6.7" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
|
||||||
<line x1="17.3" y1="17.3" x2="19.1" y2="19.1" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
|
||||||
<line x1="4.9" y1="19.1" x2="6.7" y2="17.3" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
|
||||||
<line x1="17.3" y1="6.7" x2="19.1" y2="4.9" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.0 KiB |
@@ -3,14 +3,11 @@
|
|||||||
.cyber-action-card,
|
.cyber-action-card,
|
||||||
.cyber-side-card,
|
.cyber-side-card,
|
||||||
.cyber-header-bar,
|
.cyber-header-bar,
|
||||||
.theme-toggle-btn,
|
|
||||||
.cyber-status-badge {
|
.cyber-status-badge {
|
||||||
-fx-effect: null !important;
|
-fx-effect: null !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.theme-light .cyber-action-card:hover,
|
.cyber-action-card:hover,
|
||||||
.theme-dark .cyber-action-card:hover,
|
.cyber-side-card:hover {
|
||||||
.cyber-side-card:hover,
|
|
||||||
.theme-toggle-btn:hover {
|
|
||||||
-fx-effect: null !important;
|
-fx-effect: null !important;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,15 +10,7 @@
|
|||||||
.root {
|
.root {
|
||||||
-fx-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
-fx-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||||
-fx-font-size: 13px;
|
-fx-font-size: 13px;
|
||||||
-fx-text-primary: -fx-color-text;
|
|
||||||
-fx-text-secondary: -fx-color-text-muted;
|
|
||||||
-fx-bg-primary: -fx-color-surface;
|
|
||||||
-fx-border-light: -fx-color-border;
|
|
||||||
-fx-accent: -fx-color-primary;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --- Tema Claro (Light Theme) --- */
|
|
||||||
.theme-light {
|
|
||||||
/* Superfícies & Fundos */
|
/* Superfícies & Fundos */
|
||||||
-fx-color-background: #f1f5f9;
|
-fx-color-background: #f1f5f9;
|
||||||
-fx-color-surface: #ffffff;
|
-fx-color-surface: #ffffff;
|
||||||
@@ -73,7 +65,7 @@
|
|||||||
-fx-control-inner-background: -fx-color-surface;
|
-fx-control-inner-background: -fx-color-surface;
|
||||||
-fx-text-fill: -fx-color-text;
|
-fx-text-fill: -fx-color-text;
|
||||||
|
|
||||||
/* Tokens semânticos complementares e aliases de compatibilidade */
|
/* Tokens semânticos essenciais usados pelo Java */
|
||||||
-fx-text-primary: -fx-color-text;
|
-fx-text-primary: -fx-color-text;
|
||||||
-fx-text-secondary: -fx-color-text-muted;
|
-fx-text-secondary: -fx-color-text-muted;
|
||||||
-fx-bg-primary: -fx-color-surface;
|
-fx-bg-primary: -fx-color-surface;
|
||||||
@@ -81,70 +73,6 @@
|
|||||||
-fx-accent: -fx-color-primary;
|
-fx-accent: -fx-color-primary;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --- Tema Escuro (Dark Theme) --- */
|
|
||||||
.theme-dark {
|
|
||||||
/* Superfícies & Fundos */
|
|
||||||
-fx-color-background: #090d16;
|
|
||||||
-fx-color-surface: #111827;
|
|
||||||
-fx-color-surface-hover: #1e293b;
|
|
||||||
-fx-color-surface-active: #334155;
|
|
||||||
-fx-color-surface-subtle: #0f172a;
|
|
||||||
|
|
||||||
/* Bordas */
|
|
||||||
-fx-color-border: #334155;
|
|
||||||
-fx-color-border-subtle: #1e293b;
|
|
||||||
-fx-color-border-hover: #475569;
|
|
||||||
|
|
||||||
/* Tipografia (WCAG AA: contraste >= 4.5:1) */
|
|
||||||
-fx-color-text: #f8fafc;
|
|
||||||
-fx-color-text-muted: #94a3b8;
|
|
||||||
-fx-color-text-inverse: #0f172a;
|
|
||||||
|
|
||||||
/* Primária (Azul da Marca CyberCore Dark) */
|
|
||||||
-fx-color-primary: #38bdf8;
|
|
||||||
-fx-color-primary-hover: #0284c7;
|
|
||||||
-fx-color-primary-active: #0369a1;
|
|
||||||
-fx-color-primary-subtle: rgba(56, 189, 248, 0.15);
|
|
||||||
|
|
||||||
/* Sucesso */
|
|
||||||
-fx-color-success: #34d399;
|
|
||||||
-fx-color-success-hover: #10b981;
|
|
||||||
-fx-color-success-subtle: rgba(52, 211, 153, 0.15);
|
|
||||||
-fx-color-success-border: rgba(52, 211, 153, 0.4);
|
|
||||||
|
|
||||||
/* Aviso */
|
|
||||||
-fx-color-warning: #fbbf24;
|
|
||||||
-fx-color-warning-hover: #f59e0b;
|
|
||||||
-fx-color-warning-subtle: rgba(251, 191, 36, 0.15);
|
|
||||||
-fx-color-warning-border: rgba(251, 191, 36, 0.4);
|
|
||||||
|
|
||||||
/* Erro */
|
|
||||||
-fx-color-error: #f87171;
|
|
||||||
-fx-color-error-hover: #ef4444;
|
|
||||||
-fx-color-error-subtle: rgba(248, 113, 113, 0.15);
|
|
||||||
-fx-color-error-border: rgba(248, 113, 113, 0.4);
|
|
||||||
|
|
||||||
/* Foco & Realce */
|
|
||||||
-fx-color-focus: #38bdf8;
|
|
||||||
|
|
||||||
/* Sombras & Efeitos */
|
|
||||||
-fx-color-shadow: rgba(0, 0, 0, 0.4);
|
|
||||||
-fx-color-shadow-hover: rgba(0, 0, 0, 0.6);
|
|
||||||
|
|
||||||
/* Lookups nativos do JavaFX */
|
|
||||||
-fx-base: -fx-color-background;
|
|
||||||
-fx-background: -fx-color-background;
|
|
||||||
-fx-control-inner-background: -fx-color-surface;
|
|
||||||
-fx-text-fill: -fx-color-text;
|
|
||||||
|
|
||||||
/* Tokens semânticos complementares e aliases de compatibilidade */
|
|
||||||
-fx-text-primary: -fx-color-text;
|
|
||||||
-fx-text-secondary: -fx-color-text-muted;
|
|
||||||
-fx-bg-primary: -fx-color-surface;
|
|
||||||
-fx-border-light: -fx-color-border;
|
|
||||||
-fx-accent: -fx-color-primary;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ==========================================================================
|
/* ==========================================================================
|
||||||
2. REGRAS GERAIS DE ESTRUTURA E CONTAINERS
|
2. REGRAS GERAIS DE ESTRUTURA E CONTAINERS
|
||||||
========================================================================== */
|
========================================================================== */
|
||||||
@@ -213,31 +141,11 @@
|
|||||||
-fx-font-weight: bold;
|
-fx-font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Botão de Alternância de Tema */
|
|
||||||
.theme-toggle-btn {
|
|
||||||
-fx-background-color: -fx-color-surface;
|
|
||||||
-fx-border-color: -fx-color-border;
|
|
||||||
-fx-border-radius: 18px;
|
|
||||||
-fx-border-width: 1px;
|
|
||||||
-fx-background-radius: 18px;
|
|
||||||
-fx-text-fill: -fx-color-text;
|
|
||||||
-fx-padding: 4px 12px;
|
|
||||||
-fx-font-size: 11px;
|
|
||||||
-fx-font-weight: bold;
|
|
||||||
-fx-cursor: hand;
|
|
||||||
-fx-effect: dropshadow(three-pass-box, -fx-color-shadow, 4, 0, 0, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.theme-toggle-btn:hover {
|
|
||||||
-fx-background-color: -fx-color-surface-hover;
|
|
||||||
-fx-border-color: -fx-color-primary;
|
|
||||||
-fx-text-fill: -fx-color-primary;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ==========================================================================
|
/* ==========================================================================
|
||||||
3. HUB DE AÇÕES E PAINEL LATERAL
|
3. HUB DE AÇÕES E PAINEL LATERAL
|
||||||
========================================================================== */
|
========================================================================== */
|
||||||
|
|
||||||
|
.button.cyber-action-card,
|
||||||
.cyber-action-card {
|
.cyber-action-card {
|
||||||
-fx-background-color: -fx-color-surface;
|
-fx-background-color: -fx-color-surface;
|
||||||
-fx-border-color: -fx-color-border;
|
-fx-border-color: -fx-color-border;
|
||||||
@@ -253,16 +161,24 @@
|
|||||||
-fx-effect: dropshadow(three-pass-box, -fx-color-shadow, 6, 0, 0, 2);
|
-fx-effect: dropshadow(three-pass-box, -fx-color-shadow, 6, 0, 0, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.button.cyber-action-card:hover,
|
||||||
.cyber-action-card:hover {
|
.cyber-action-card:hover {
|
||||||
-fx-background-color: -fx-color-surface-hover;
|
-fx-background-color: -fx-color-surface-hover;
|
||||||
-fx-border-color: -fx-color-primary;
|
-fx-border-color: -fx-color-primary;
|
||||||
-fx-effect: dropshadow(three-pass-box, -fx-color-shadow-hover, 10, 0, 0, 4);
|
-fx-effect: dropshadow(three-pass-box, -fx-color-shadow-hover, 10, 0, 0, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.button.cyber-action-card:pressed,
|
||||||
.cyber-action-card:pressed {
|
.cyber-action-card:pressed {
|
||||||
-fx-background-color: -fx-color-surface-active;
|
-fx-background-color: -fx-color-surface-active;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.button.cyber-action-card:focused,
|
||||||
|
.cyber-action-card:focused {
|
||||||
|
-fx-border-color: -fx-color-focus;
|
||||||
|
-fx-border-width: 1.5px;
|
||||||
|
}
|
||||||
|
|
||||||
.cyber-action-text {
|
.cyber-action-text {
|
||||||
-fx-font-size: 13px;
|
-fx-font-size: 13px;
|
||||||
-fx-font-weight: bold;
|
-fx-font-weight: bold;
|
||||||
@@ -280,6 +196,7 @@
|
|||||||
-fx-effect: dropshadow(three-pass-box, -fx-color-shadow, 4, 0, 0, 1);
|
-fx-effect: dropshadow(three-pass-box, -fx-color-shadow, 4, 0, 0, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.button.cyber-side-card,
|
||||||
.cyber-side-card {
|
.cyber-side-card {
|
||||||
-fx-background-color: transparent;
|
-fx-background-color: transparent;
|
||||||
-fx-border-color: transparent;
|
-fx-border-color: transparent;
|
||||||
@@ -289,18 +206,29 @@
|
|||||||
-fx-padding: 6px 10px;
|
-fx-padding: 6px 10px;
|
||||||
-fx-alignment: CENTER_LEFT;
|
-fx-alignment: CENTER_LEFT;
|
||||||
-fx-cursor: hand;
|
-fx-cursor: hand;
|
||||||
|
-fx-pref-height: 40px;
|
||||||
|
-fx-min-height: 40px;
|
||||||
-fx-max-width: Infinity;
|
-fx-max-width: Infinity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.button.cyber-side-card:hover,
|
||||||
.cyber-side-card:hover {
|
.cyber-side-card:hover {
|
||||||
-fx-background-color: -fx-color-surface-hover;
|
-fx-background-color: -fx-color-surface-hover;
|
||||||
-fx-border-color: -fx-color-border-subtle;
|
-fx-border-color: -fx-color-border-subtle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.button.cyber-side-card:pressed,
|
||||||
.cyber-side-card:pressed {
|
.cyber-side-card:pressed {
|
||||||
-fx-background-color: -fx-color-surface-active;
|
-fx-background-color: -fx-color-surface-active;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.button.cyber-side-card:focused,
|
||||||
|
.cyber-side-card:focused {
|
||||||
|
-fx-background-color: -fx-color-surface-hover;
|
||||||
|
-fx-border-color: -fx-color-focus;
|
||||||
|
-fx-border-width: 1.5px;
|
||||||
|
}
|
||||||
|
|
||||||
.cyber-side-text {
|
.cyber-side-text {
|
||||||
-fx-font-size: 13px;
|
-fx-font-size: 13px;
|
||||||
-fx-text-fill: -fx-color-text;
|
-fx-text-fill: -fx-color-text;
|
||||||
@@ -615,7 +543,7 @@
|
|||||||
-fx-background-color: transparent;
|
-fx-background-color: transparent;
|
||||||
-fx-border-color: transparent;
|
-fx-border-color: transparent;
|
||||||
-fx-border-width: 1px;
|
-fx-border-width: 1px;
|
||||||
-fx-text-fill: -fx-color-text;
|
-fx-text-fill: -fx-color-text-muted;
|
||||||
-fx-font-weight: 500;
|
-fx-font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -635,7 +563,8 @@
|
|||||||
.btn-ghost:focused,
|
.btn-ghost:focused,
|
||||||
.btn-toolbar:focused,
|
.btn-toolbar:focused,
|
||||||
.btn-cancel:focused {
|
.btn-cancel:focused {
|
||||||
-fx-border-color: -fx-color-focus;
|
-fx-background-color: -fx-color-surface-hover;
|
||||||
|
-fx-border-color: -fx-color-border-subtle;
|
||||||
-fx-border-width: 1.5px;
|
-fx-border-width: 1.5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,9 @@ import javafx.geometry.Insets;
|
|||||||
import javafx.geometry.Pos;
|
import javafx.geometry.Pos;
|
||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
|
import javafx.scene.SnapshotParameters;
|
||||||
import javafx.scene.control.Button;
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.scene.transform.Transform;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
import javafx.scene.control.ScrollPane;
|
import javafx.scene.control.ScrollPane;
|
||||||
import javafx.scene.image.PixelReader;
|
import javafx.scene.image.PixelReader;
|
||||||
@@ -45,6 +47,10 @@ import java.time.Instant;
|
|||||||
import java.time.temporal.ChronoUnit;
|
import java.time.temporal.ChronoUnit;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utilitário de captura automatizada das telas da interface do CyberCore Signer.
|
||||||
|
* Gera as capturas de tela atualizadas no tema claro padronizado.
|
||||||
|
*/
|
||||||
public class UiPreviewGenerator extends Application {
|
public class UiPreviewGenerator extends Application {
|
||||||
|
|
||||||
private static final File OUTPUT_DIR = new File("docs/screenshots");
|
private static final File OUTPUT_DIR = new File("docs/screenshots");
|
||||||
@@ -74,53 +80,66 @@ public class UiPreviewGenerator extends Application {
|
|||||||
// 5. Validação de Assinatura
|
// 5. Validação de Assinatura
|
||||||
captureValidationScreenshots(primaryStage);
|
captureValidationScreenshots(primaryStage);
|
||||||
|
|
||||||
// 6. Solicitação de Assinatura (Prompt CCSP)
|
// 6. Solicitação de Assinatura Local (Prompt de Autorização)
|
||||||
capturePromptScreenshots(primaryStage);
|
capturePromptScreenshots(primaryStage);
|
||||||
|
|
||||||
// 7. Selos Visuais
|
// 7. Gerenciador de Selos
|
||||||
captureSealsScreenshots(primaryStage);
|
captureSealsScreenshots(primaryStage);
|
||||||
|
|
||||||
// 8. Sobre
|
// 8. Sobre
|
||||||
captureAboutScreenshots(primaryStage);
|
captureAboutScreenshots(primaryStage);
|
||||||
|
|
||||||
// 9. Diálogo de Encerramento / Bandeja
|
// 9. Confirmação de Saída
|
||||||
captureExitDialogScreenshots(primaryStage);
|
captureExitDialogScreenshots(primaryStage);
|
||||||
|
|
||||||
// 10. Diálogo de Posicionamento e Assinatura PDF (PAdES)
|
// 10. Assinador Visual de PDF
|
||||||
capturePdfSignDialogScreenshots(primaryStage);
|
capturePdfSignDialogScreenshots(primaryStage);
|
||||||
|
|
||||||
// 11. Diálogo de Seleção de Certificado e PIN do PDF
|
// 11. Seleção de Certificado e PIN do PDF
|
||||||
capturePdfCertificateSignDialogScreenshots(primaryStage);
|
capturePdfCertificateSignDialogScreenshots(primaryStage);
|
||||||
|
|
||||||
|
System.out.println(">>> Capturas de tela concluídas com sucesso em: " + OUTPUT_DIR.getAbsolutePath());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
} finally {
|
} finally {
|
||||||
Platform.exit();
|
Platform.exit();
|
||||||
|
System.exit(0);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void captureHubScreenshots(Stage stage) throws Exception {
|
private void captureHubScreenshots(Stage stage) throws Exception {
|
||||||
for (ThemeManager.AppTheme theme : ThemeManager.AppTheme.values()) {
|
|
||||||
ThemeManager.setCurrentTheme(theme);
|
|
||||||
MainAppView appView = new MainAppView(stage);
|
MainAppView appView = new MainAppView(stage);
|
||||||
|
|
||||||
|
// 1. Resolução padrão 780x500
|
||||||
|
stage.setWidth(780);
|
||||||
|
stage.setHeight(500);
|
||||||
stage.show();
|
stage.show();
|
||||||
stage.getScene().getRoot().applyCss();
|
stage.getScene().getRoot().applyCss();
|
||||||
stage.getScene().getRoot().layout();
|
stage.getScene().getRoot().layout();
|
||||||
|
|
||||||
String suffix = theme == ThemeManager.AppTheme.LIGHT ? "claro" : "escuro";
|
saveNodeSnapshot(stage.getScene(), new File(OUTPUT_DIR, "01_hub.png"));
|
||||||
saveNodeSnapshot(stage.getScene(), new File(OUTPUT_DIR, "01_hub_" + suffix + ".png"));
|
saveNodeSnapshot(stage.getScene(), new File(OUTPUT_DIR, "01_hub_780x500.png"));
|
||||||
if (theme == ThemeManager.AppTheme.LIGHT) {
|
saveNodeSnapshot(stage.getScene(), new File(OUTPUT_DIR, "01_hub_claro.png"));
|
||||||
saveNodeSnapshot(stage.getScene(), new File(OUTPUT_DIR, "01_layout_claro.png"));
|
saveNodeSnapshot(stage.getScene(), new File(OUTPUT_DIR, "01_layout_claro.png"));
|
||||||
} else {
|
|
||||||
saveNodeSnapshot(stage.getScene(), new File(OUTPUT_DIR, "02_layout_escuro.png"));
|
// 2. Escala 150% (High-DPI / Zoom 1.5x)
|
||||||
}
|
saveNodeSnapshotScaled(stage.getScene(), new File(OUTPUT_DIR, "01_hub_scale150.png"), 1.5);
|
||||||
}
|
|
||||||
|
// 3. Resolução expandida 1024x768
|
||||||
|
stage.setWidth(1024);
|
||||||
|
stage.setHeight(768);
|
||||||
|
stage.getScene().getRoot().applyCss();
|
||||||
|
stage.getScene().getRoot().layout();
|
||||||
|
|
||||||
|
saveNodeSnapshot(stage.getScene(), new File(OUTPUT_DIR, "01_hub_1024x768.png"));
|
||||||
|
|
||||||
|
// Restaura tamanho padrão para os diálogos subsequentes
|
||||||
|
stage.setWidth(780);
|
||||||
|
stage.setHeight(500);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void captureDiagnosticsScreenshots(Stage stage) throws Exception {
|
private void captureDiagnosticsScreenshots(Stage stage) throws Exception {
|
||||||
for (ThemeManager.AppTheme theme : ThemeManager.AppTheme.values()) {
|
|
||||||
ThemeManager.setCurrentTheme(theme);
|
|
||||||
DiagnosticsView diagView = new DiagnosticsView(new DiagnosticService());
|
DiagnosticsView diagView = new DiagnosticsView(new DiagnosticService());
|
||||||
|
|
||||||
VBox container = wrapInDialogContainer("DIAGNÓSTICO DO SISTEMA",
|
VBox container = wrapInDialogContainer("DIAGNÓSTICO DO SISTEMA",
|
||||||
@@ -133,17 +152,11 @@ public class UiPreviewGenerator extends Application {
|
|||||||
scene.getRoot().applyCss();
|
scene.getRoot().applyCss();
|
||||||
scene.getRoot().layout();
|
scene.getRoot().layout();
|
||||||
|
|
||||||
String suffix = theme == ThemeManager.AppTheme.LIGHT ? "claro" : "escuro";
|
|
||||||
saveNodeSnapshot(scene, new File(OUTPUT_DIR, "02_diagnostico_" + suffix + ".png"));
|
|
||||||
if (theme == ThemeManager.AppTheme.LIGHT) {
|
|
||||||
saveNodeSnapshot(scene, new File(OUTPUT_DIR, "02_diagnostico.png"));
|
saveNodeSnapshot(scene, new File(OUTPUT_DIR, "02_diagnostico.png"));
|
||||||
}
|
saveNodeSnapshot(scene, new File(OUTPUT_DIR, "02_diagnostico_claro.png"));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void captureSettingsScreenshots(Stage stage) throws Exception {
|
private void captureSettingsScreenshots(Stage stage) throws Exception {
|
||||||
for (ThemeManager.AppTheme theme : ThemeManager.AppTheme.values()) {
|
|
||||||
ThemeManager.setCurrentTheme(theme);
|
|
||||||
SettingsView settingsView = new SettingsView();
|
SettingsView settingsView = new SettingsView();
|
||||||
|
|
||||||
ScrollPane sp = new ScrollPane(settingsView);
|
ScrollPane sp = new ScrollPane(settingsView);
|
||||||
@@ -161,18 +174,11 @@ public class UiPreviewGenerator extends Application {
|
|||||||
scene.getRoot().applyCss();
|
scene.getRoot().applyCss();
|
||||||
scene.getRoot().layout();
|
scene.getRoot().layout();
|
||||||
|
|
||||||
String suffix = theme == ThemeManager.AppTheme.LIGHT ? "claro" : "escuro";
|
|
||||||
saveNodeSnapshot(scene, new File(OUTPUT_DIR, "03_configuracoes_" + suffix + ".png"));
|
|
||||||
if (theme == ThemeManager.AppTheme.LIGHT) {
|
|
||||||
saveNodeSnapshot(scene, new File(OUTPUT_DIR, "03_configuracoes.png"));
|
saveNodeSnapshot(scene, new File(OUTPUT_DIR, "03_configuracoes.png"));
|
||||||
}
|
saveNodeSnapshot(scene, new File(OUTPUT_DIR, "03_configuracoes_claro.png"));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void captureCertificatesScreenshots(Stage stage) throws Exception {
|
private void captureCertificatesScreenshots(Stage stage) throws Exception {
|
||||||
for (ThemeManager.AppTheme theme : ThemeManager.AppTheme.values()) {
|
|
||||||
ThemeManager.setCurrentTheme(theme);
|
|
||||||
|
|
||||||
VBox cardsBox = new VBox(12);
|
VBox cardsBox = new VBox(12);
|
||||||
cardsBox.setPadding(new Insets(12));
|
cardsBox.setPadding(new Insets(12));
|
||||||
|
|
||||||
@@ -235,31 +241,23 @@ public class UiPreviewGenerator extends Application {
|
|||||||
scene.getRoot().applyCss();
|
scene.getRoot().applyCss();
|
||||||
scene.getRoot().layout();
|
scene.getRoot().layout();
|
||||||
|
|
||||||
String suffix = theme == ThemeManager.AppTheme.LIGHT ? "claro" : "escuro";
|
saveNodeSnapshot(scene, new File(OUTPUT_DIR, "04_certificados.png"));
|
||||||
saveNodeSnapshot(scene, new File(OUTPUT_DIR, "04_certificados_" + suffix + ".png"));
|
saveNodeSnapshot(scene, new File(OUTPUT_DIR, "04_certificados_claro.png"));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void captureValidationScreenshots(Stage stage) throws Exception {
|
private void captureValidationScreenshots(Stage stage) throws Exception {
|
||||||
for (ThemeManager.AppTheme theme : ThemeManager.AppTheme.values()) {
|
|
||||||
ThemeManager.setCurrentTheme(theme);
|
|
||||||
SignatureValidationDialog dlg = new SignatureValidationDialog(null);
|
SignatureValidationDialog dlg = new SignatureValidationDialog(null);
|
||||||
// Captura layout do diálogo
|
|
||||||
Scene scene = createValidationSampleScene(stage);
|
Scene scene = createValidationSampleScene(stage);
|
||||||
stage.setScene(scene);
|
stage.setScene(scene);
|
||||||
stage.show();
|
stage.show();
|
||||||
scene.getRoot().applyCss();
|
scene.getRoot().applyCss();
|
||||||
scene.getRoot().layout();
|
scene.getRoot().layout();
|
||||||
|
|
||||||
String suffix = theme == ThemeManager.AppTheme.LIGHT ? "claro" : "escuro";
|
saveNodeSnapshot(scene, new File(OUTPUT_DIR, "05_validacao.png"));
|
||||||
saveNodeSnapshot(scene, new File(OUTPUT_DIR, "05_validacao_" + suffix + ".png"));
|
saveNodeSnapshot(scene, new File(OUTPUT_DIR, "05_validacao_claro.png"));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void capturePromptScreenshots(Stage stage) throws Exception {
|
private void capturePromptScreenshots(Stage stage) throws Exception {
|
||||||
for (ThemeManager.AppTheme theme : ThemeManager.AppTheme.values()) {
|
|
||||||
ThemeManager.setCurrentTheme(theme);
|
|
||||||
|
|
||||||
ManagedCertificate cert = new ManagedCertificate(
|
ManagedCertificate cert = new ManagedCertificate(
|
||||||
"cert-a1",
|
"cert-a1",
|
||||||
"MARIA OLIVEIRA:98765432100",
|
"MARIA OLIVEIRA:98765432100",
|
||||||
@@ -307,14 +305,11 @@ public class UiPreviewGenerator extends Application {
|
|||||||
scene.getRoot().applyCss();
|
scene.getRoot().applyCss();
|
||||||
scene.getRoot().layout();
|
scene.getRoot().layout();
|
||||||
|
|
||||||
String suffix = theme == ThemeManager.AppTheme.LIGHT ? "claro" : "escuro";
|
saveNodeSnapshot(scene, new File(OUTPUT_DIR, "06_solicitacao.png"));
|
||||||
saveNodeSnapshot(scene, new File(OUTPUT_DIR, "06_solicitacao_" + suffix + ".png"));
|
saveNodeSnapshot(scene, new File(OUTPUT_DIR, "06_solicitacao_claro.png"));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void captureSealsScreenshots(Stage stage) throws Exception {
|
private void captureSealsScreenshots(Stage stage) throws Exception {
|
||||||
for (ThemeManager.AppTheme theme : ThemeManager.AppTheme.values()) {
|
|
||||||
ThemeManager.setCurrentTheme(theme);
|
|
||||||
SealManagementDialog dlg = new SealManagementDialog(stage);
|
SealManagementDialog dlg = new SealManagementDialog(stage);
|
||||||
Scene scene = createSealsSampleScene();
|
Scene scene = createSealsSampleScene();
|
||||||
stage.setScene(scene);
|
stage.setScene(scene);
|
||||||
@@ -322,14 +317,11 @@ public class UiPreviewGenerator extends Application {
|
|||||||
scene.getRoot().applyCss();
|
scene.getRoot().applyCss();
|
||||||
scene.getRoot().layout();
|
scene.getRoot().layout();
|
||||||
|
|
||||||
String suffix = theme == ThemeManager.AppTheme.LIGHT ? "claro" : "escuro";
|
saveNodeSnapshot(scene, new File(OUTPUT_DIR, "07_selos.png"));
|
||||||
saveNodeSnapshot(scene, new File(OUTPUT_DIR, "07_selos_" + suffix + ".png"));
|
saveNodeSnapshot(scene, new File(OUTPUT_DIR, "07_selos_claro.png"));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void captureAboutScreenshots(Stage stage) throws Exception {
|
private void captureAboutScreenshots(Stage stage) throws Exception {
|
||||||
for (ThemeManager.AppTheme theme : ThemeManager.AppTheme.values()) {
|
|
||||||
ThemeManager.setCurrentTheme(theme);
|
|
||||||
AboutView aboutView = new AboutView();
|
AboutView aboutView = new AboutView();
|
||||||
|
|
||||||
VBox container = wrapInDialogContainer("SOBRE O ASSINADOR",
|
VBox container = wrapInDialogContainer("SOBRE O ASSINADOR",
|
||||||
@@ -342,9 +334,8 @@ public class UiPreviewGenerator extends Application {
|
|||||||
scene.getRoot().applyCss();
|
scene.getRoot().applyCss();
|
||||||
scene.getRoot().layout();
|
scene.getRoot().layout();
|
||||||
|
|
||||||
String suffix = theme == ThemeManager.AppTheme.LIGHT ? "claro" : "escuro";
|
saveNodeSnapshot(scene, new File(OUTPUT_DIR, "08_sobre.png"));
|
||||||
saveNodeSnapshot(scene, new File(OUTPUT_DIR, "08_sobre_" + suffix + ".png"));
|
saveNodeSnapshot(scene, new File(OUTPUT_DIR, "08_sobre_claro.png"));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Scene createScene(VBox root, double w, double h) {
|
private Scene createScene(VBox root, double w, double h) {
|
||||||
@@ -358,105 +349,140 @@ public class UiPreviewGenerator extends Application {
|
|||||||
|
|
||||||
private VBox wrapInDialogContainer(String title, String subtitle, Node icon, Node content, double w, double h) {
|
private VBox wrapInDialogContainer(String title, String subtitle, Node icon, Node content, double w, double h) {
|
||||||
VBox container = new VBox(16);
|
VBox container = new VBox(16);
|
||||||
container.getStyleClass().addAll("dialog-container", "cyber-root");
|
container.getStyleClass().add("dialog-container");
|
||||||
container.setPadding(new Insets(20));
|
container.setPadding(new Insets(24));
|
||||||
|
|
||||||
HBox header = new HBox(12);
|
HBox header = new HBox(12);
|
||||||
header.setAlignment(Pos.CENTER_LEFT);
|
header.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
|
||||||
VBox titleBox = new VBox(2);
|
VBox titleBox = new VBox(2);
|
||||||
Label titleLbl = new Label(title);
|
Label lblTitle = new Label(title);
|
||||||
titleLbl.getStyleClass().add("dialog-header-title");
|
lblTitle.getStyleClass().addAll("dialog-header-title", "text-bold");
|
||||||
Label subLbl = new Label(subtitle);
|
|
||||||
subLbl.getStyleClass().add("dialog-header-subtitle");
|
Label lblSub = new Label(subtitle);
|
||||||
titleBox.getChildren().addAll(titleLbl, subLbl);
|
lblSub.getStyleClass().add("dialog-subtitle");
|
||||||
|
titleBox.getChildren().addAll(lblTitle, lblSub);
|
||||||
|
|
||||||
header.getChildren().addAll(icon, titleBox);
|
header.getChildren().addAll(icon, titleBox);
|
||||||
|
|
||||||
VBox contentBox = new VBox(content);
|
container.getChildren().addAll(header, content);
|
||||||
VBox.setVgrow(contentBox, Priority.ALWAYS);
|
|
||||||
|
|
||||||
container.getChildren().addAll(header, contentBox);
|
|
||||||
return container;
|
return container;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Scene createValidationSampleScene(Stage stage) {
|
private Scene createValidationSampleScene(Stage stage) {
|
||||||
VBox root = new VBox(16);
|
VBox root = new VBox(16);
|
||||||
root.setPadding(new Insets(20));
|
root.getStyleClass().add("dialog-container");
|
||||||
root.getStyleClass().addAll("dialog-container", "cyber-root");
|
root.setPadding(new Insets(24));
|
||||||
|
|
||||||
HBox header = new HBox(12);
|
HBox header = new HBox(12);
|
||||||
header.setAlignment(Pos.CENTER_LEFT);
|
header.setAlignment(Pos.CENTER_LEFT);
|
||||||
header.getChildren().addAll(
|
|
||||||
SvgIcons.validateIcon(),
|
|
||||||
new Label("VALIDAÇÃO DE ASSINATURA DIGITAL")
|
|
||||||
);
|
|
||||||
|
|
||||||
VBox box = new VBox(12);
|
VBox titleBox = new VBox(2);
|
||||||
box.getStyleClass().add("dialog-box");
|
Label title = new Label("VALIDAÇÃO DE DOCUMENTO ASSINADO");
|
||||||
box.getChildren().addAll(
|
title.getStyleClass().addAll("dialog-header-title", "text-bold");
|
||||||
new Label("Arquivo: Contrato_Prestacao_Servicos_Assinado.pdf"),
|
Label sub = new Label("Verificação criptográfica de conformidade de assinaturas PAdES, CAdES e XMLDSig.");
|
||||||
new Label("Status: Assinatura Válida e Íntegra"),
|
sub.getStyleClass().add("dialog-subtitle");
|
||||||
new Label("Assinado por: JOAO DA SILVA (CPF ***.123.456-**)"),
|
titleBox.getChildren().addAll(title, sub);
|
||||||
new Label("Autoridade Certificadora: AC SERPRO RFB v5 (ICP-Brasil)")
|
header.getChildren().addAll(SvgIcons.validateIcon(), titleBox);
|
||||||
);
|
|
||||||
|
|
||||||
root.getChildren().addAll(header, box);
|
VBox resultBox = new VBox(12);
|
||||||
|
resultBox.getStyleClass().add("content-card");
|
||||||
|
resultBox.setPadding(new Insets(16));
|
||||||
|
|
||||||
|
Label docTitle = new Label("Documento: Relatorio_Financeiro_2026.pdf (Conforme ICP-Brasil)");
|
||||||
|
docTitle.getStyleClass().add("text-bold");
|
||||||
|
|
||||||
|
Label statusLbl = new Label("✓ 1 Assinatura Digital VÁLIDA detectada");
|
||||||
|
statusLbl.getStyleClass().addAll("status-badge", "status-badge-success", "text-bold");
|
||||||
|
|
||||||
|
VBox signerCard = new VBox(6);
|
||||||
|
signerCard.getStyleClass().add("certificate-card");
|
||||||
|
signerCard.setPadding(new Insets(12));
|
||||||
|
|
||||||
|
Label signerName = new Label("Assinante: CARLOS EDUARDO SANTOS:55443322100");
|
||||||
|
signerName.getStyleClass().add("text-bold");
|
||||||
|
Label signerAc = new Label("Autoridade Certificadora: AC SERPRO RFB v5");
|
||||||
|
signerAc.getStyleClass().add("text-muted");
|
||||||
|
Label signerDate = new Label("Data/Hora: 20/10/2026 15:42:10 (Carimbo do Tempo Validado)");
|
||||||
|
signerDate.getStyleClass().add("text-muted");
|
||||||
|
|
||||||
|
signerCard.getChildren().addAll(signerName, signerAc, signerDate);
|
||||||
|
resultBox.getChildren().addAll(docTitle, statusLbl, signerCard);
|
||||||
|
|
||||||
|
root.getChildren().addAll(header, resultBox);
|
||||||
return createScene(root, 720, 480);
|
return createScene(root, 720, 480);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Scene createPromptSampleScene(ManagedCertificate cert, SigningContext ctx) {
|
private Scene createPromptSampleScene(ManagedCertificate cert, SigningContext ctx) {
|
||||||
VBox root = new VBox(16);
|
VBox root = new VBox(16);
|
||||||
root.setPadding(new Insets(22));
|
root.getStyleClass().add("dialog-container");
|
||||||
root.getStyleClass().addAll("dialog-container", "cyber-root");
|
root.setPadding(new Insets(24));
|
||||||
|
|
||||||
HBox header = new HBox(12);
|
HBox header = new HBox(12);
|
||||||
header.setAlignment(Pos.CENTER_LEFT);
|
header.setAlignment(Pos.CENTER_LEFT);
|
||||||
header.getChildren().addAll(
|
|
||||||
SvgIcons.cyberShieldLogo(24),
|
|
||||||
new Label("SOLICITAÇÃO DE ASSINATURA DIGITAL")
|
|
||||||
);
|
|
||||||
|
|
||||||
VBox box = new VBox(10);
|
VBox titleBox = new VBox(2);
|
||||||
box.getStyleClass().add("dialog-box");
|
Label title = new Label("SOLICITAÇÃO DE ASSINATURA DIGITAL");
|
||||||
box.getChildren().addAll(
|
title.getStyleClass().addAll("dialog-header-title", "text-bold");
|
||||||
new Label("Aplicação Solicitante: Portal de Assinaturas Web"),
|
Label sub = new Label("Uma aplicação web autorizada requer sua confirmação para assinar um documento.");
|
||||||
new Label("Origem: https://assinador.empresa.com.br"),
|
sub.getStyleClass().add("dialog-subtitle");
|
||||||
new Label("Documento: Contrato_Prestacao_Servicos_2026.pdf"),
|
titleBox.getChildren().addAll(title, sub);
|
||||||
new Label("Algoritmo: " + ctx.algorithm()),
|
header.getChildren().addAll(SvgIcons.cyberShieldLogo(24), titleBox);
|
||||||
new Label("Certificado: " + cert.commonName() + " [" + cert.type().getCode() + "]")
|
|
||||||
);
|
|
||||||
|
|
||||||
HBox actions = new HBox(12);
|
VBox box = new VBox(14);
|
||||||
actions.setAlignment(Pos.CENTER_RIGHT);
|
box.getStyleClass().add("content-card");
|
||||||
Button btnReject = new Button("Recusar");
|
box.setPadding(new Insets(16));
|
||||||
btnReject.getStyleClass().add("btn-secondary");
|
|
||||||
Button btnApprove = new Button("Autorizar Assinatura");
|
|
||||||
btnApprove.getStyleClass().add("btn-primary");
|
|
||||||
actions.getChildren().addAll(btnReject, btnApprove);
|
|
||||||
|
|
||||||
root.getChildren().addAll(header, box, actions);
|
Label appLbl = new Label("Aplicação Solicitante: Portal de Assinaturas Web (https://assinador.empresa.com.br)");
|
||||||
return createScene(root, 620, 440);
|
appLbl.getStyleClass().add("text-bold");
|
||||||
|
|
||||||
|
Label docLbl = new Label("Documento: Contrato_Prestacao_Servicos_2026.pdf (Hash SHA-256)");
|
||||||
|
docLbl.getStyleClass().add("text-muted");
|
||||||
|
|
||||||
|
VBox certNode = new VBox(6);
|
||||||
|
certNode.getStyleClass().add("certificate-card");
|
||||||
|
certNode.setPadding(new Insets(12));
|
||||||
|
Label certTitle = new Label("Certificado Digital Selecionado:");
|
||||||
|
certTitle.getStyleClass().addAll("text-bold", "text-muted");
|
||||||
|
Label certSubject = new Label(cert.commonName());
|
||||||
|
certSubject.getStyleClass().add("text-bold");
|
||||||
|
Label certEmissor = new Label("Emissor: " + cert.issuerDn());
|
||||||
|
certEmissor.getStyleClass().add("text-muted");
|
||||||
|
certNode.getChildren().addAll(certTitle, certSubject, certEmissor);
|
||||||
|
|
||||||
|
HBox btnBar = new HBox(12);
|
||||||
|
btnBar.setAlignment(Pos.CENTER_RIGHT);
|
||||||
|
Button btnRecusar = new Button("Recusar");
|
||||||
|
btnRecusar.getStyleClass().addAll("btn-ghost");
|
||||||
|
Button btnConfirmar = new Button("Autorizar e Assinar");
|
||||||
|
btnConfirmar.getStyleClass().addAll("btn-primary");
|
||||||
|
btnBar.getChildren().addAll(btnRecusar, btnConfirmar);
|
||||||
|
|
||||||
|
box.getChildren().addAll(appLbl, docLbl, certNode, btnBar);
|
||||||
|
root.getChildren().addAll(header, box);
|
||||||
|
return createScene(root, 700, 480);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Scene createSealsSampleScene() {
|
private Scene createSealsSampleScene() {
|
||||||
VBox root = new VBox(16);
|
VBox root = new VBox(16);
|
||||||
root.setPadding(new Insets(20));
|
root.getStyleClass().add("dialog-container");
|
||||||
root.getStyleClass().addAll("dialog-container", "cyber-root");
|
root.setPadding(new Insets(24));
|
||||||
|
|
||||||
HBox header = new HBox(12);
|
HBox header = new HBox(12);
|
||||||
header.setAlignment(Pos.CENTER_LEFT);
|
header.setAlignment(Pos.CENTER_LEFT);
|
||||||
header.getChildren().addAll(
|
|
||||||
SvgIcons.sealIcon(),
|
|
||||||
new Label("GERENCIAMENTO DE SELOS VISUAIS")
|
|
||||||
);
|
|
||||||
|
|
||||||
VBox box = new VBox(10);
|
VBox titleBox = new VBox(2);
|
||||||
box.getStyleClass().add("dialog-box");
|
Label title = new Label("TEMAS E SELOS VISUAIS PAdES");
|
||||||
|
title.getStyleClass().addAll("dialog-header-title", "text-bold");
|
||||||
|
Label sub = new Label("Personalize a aparência dos carimbos visuais de assinatura digital em PDFs.");
|
||||||
|
sub.getStyleClass().add("dialog-subtitle");
|
||||||
|
titleBox.getChildren().addAll(title, sub);
|
||||||
|
header.getChildren().addAll(SvgIcons.sealIcon(), titleBox);
|
||||||
|
|
||||||
|
HBox box = new HBox(16);
|
||||||
box.getChildren().addAll(
|
box.getChildren().addAll(
|
||||||
new Label("Selo Ativo: Padrão ICP-Brasil Institucional"),
|
new Label("Visualização de Selos Padronizados ICP-Brasil"),
|
||||||
new Label("Dimensões: 200 x 60 mm • Posição: Canto inferior direito"),
|
br.com.cybercore.signer.ui.components.Icons.sealIcpBrasil()
|
||||||
new Label("Elementos: Carimbo de Tempo + Brasão + Dados do Titular")
|
|
||||||
);
|
);
|
||||||
|
|
||||||
root.getChildren().addAll(header, box);
|
root.getChildren().addAll(header, box);
|
||||||
@@ -464,32 +490,26 @@ public class UiPreviewGenerator extends Application {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void captureExitDialogScreenshots(Stage stage) throws Exception {
|
private void captureExitDialogScreenshots(Stage stage) throws Exception {
|
||||||
for (ThemeManager.AppTheme theme : ThemeManager.AppTheme.values()) {
|
|
||||||
ThemeManager.setCurrentTheme(theme);
|
|
||||||
ExitConfirmationDialog dialog = new ExitConfirmationDialog(null);
|
ExitConfirmationDialog dialog = new ExitConfirmationDialog(null);
|
||||||
Scene scene = dialog.getStage().getScene();
|
Scene scene = dialog.getStage().getScene();
|
||||||
scene.getRoot().applyCss();
|
scene.getRoot().applyCss();
|
||||||
scene.getRoot().layout();
|
scene.getRoot().layout();
|
||||||
|
|
||||||
String suffix = theme == ThemeManager.AppTheme.LIGHT ? "claro" : "escuro";
|
saveNodeSnapshot(scene, new File(OUTPUT_DIR, "09_exit_dialog.png"));
|
||||||
saveNodeSnapshot(scene, new File(OUTPUT_DIR, "09_exit_dialog_" + suffix + ".png"));
|
saveNodeSnapshot(scene, new File(OUTPUT_DIR, "09_exit_dialog_claro.png"));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void capturePdfSignDialogScreenshots(Stage stage) throws Exception {
|
private void capturePdfSignDialogScreenshots(Stage stage) throws Exception {
|
||||||
File samplePdf = getOrCreateSamplePdf();
|
File samplePdf = getOrCreateSamplePdf();
|
||||||
CertificateManager certManager = CertificateManager.createDefault();
|
CertificateManager certManager = CertificateManager.createDefault();
|
||||||
|
|
||||||
for (ThemeManager.AppTheme theme : ThemeManager.AppTheme.values()) {
|
|
||||||
ThemeManager.setCurrentTheme(theme);
|
|
||||||
PdfSignDialog dialog = new PdfSignDialog(null, certManager, samplePdf);
|
PdfSignDialog dialog = new PdfSignDialog(null, certManager, samplePdf);
|
||||||
Scene scene = dialog.getStage().getScene();
|
Scene scene = dialog.getStage().getScene();
|
||||||
scene.getRoot().applyCss();
|
scene.getRoot().applyCss();
|
||||||
scene.getRoot().layout();
|
scene.getRoot().layout();
|
||||||
|
|
||||||
String suffix = theme == ThemeManager.AppTheme.LIGHT ? "claro" : "escuro";
|
saveNodeSnapshot(scene, new File(OUTPUT_DIR, "10_pdf_sign.png"));
|
||||||
saveNodeSnapshot(scene, new File(OUTPUT_DIR, "10_pdf_sign_" + suffix + ".png"));
|
saveNodeSnapshot(scene, new File(OUTPUT_DIR, "10_pdf_sign_claro.png"));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void capturePdfCertificateSignDialogScreenshots(Stage stage) throws Exception {
|
private void capturePdfCertificateSignDialogScreenshots(Stage stage) throws Exception {
|
||||||
@@ -514,8 +534,6 @@ public class UiPreviewGenerator extends Application {
|
|||||||
);
|
);
|
||||||
|
|
||||||
br.com.cybercore.signer.core.pdf.PdfSigningService pdfSigningService = new br.com.cybercore.signer.core.pdf.PdfSigningService(certManager);
|
br.com.cybercore.signer.core.pdf.PdfSigningService pdfSigningService = new br.com.cybercore.signer.core.pdf.PdfSigningService(certManager);
|
||||||
for (ThemeManager.AppTheme theme : ThemeManager.AppTheme.values()) {
|
|
||||||
ThemeManager.setCurrentTheme(theme);
|
|
||||||
PdfCertificateSignDialog dialog = new PdfCertificateSignDialog(
|
PdfCertificateSignDialog dialog = new PdfCertificateSignDialog(
|
||||||
null,
|
null,
|
||||||
certManager,
|
certManager,
|
||||||
@@ -528,9 +546,8 @@ public class UiPreviewGenerator extends Application {
|
|||||||
scene.getRoot().applyCss();
|
scene.getRoot().applyCss();
|
||||||
scene.getRoot().layout();
|
scene.getRoot().layout();
|
||||||
|
|
||||||
String suffix = theme == ThemeManager.AppTheme.LIGHT ? "claro" : "escuro";
|
saveNodeSnapshot(scene, new File(OUTPUT_DIR, "11_pdf_cert_sign.png"));
|
||||||
saveNodeSnapshot(scene, new File(OUTPUT_DIR, "11_pdf_cert_sign_" + suffix + ".png"));
|
saveNodeSnapshot(scene, new File(OUTPUT_DIR, "11_pdf_cert_sign_claro.png"));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static File getOrCreateSamplePdf() throws Exception {
|
private static File getOrCreateSamplePdf() throws Exception {
|
||||||
@@ -539,33 +556,42 @@ public class UiPreviewGenerator extends Application {
|
|||||||
if (pdf.getParentFile() != null) {
|
if (pdf.getParentFile() != null) {
|
||||||
pdf.getParentFile().mkdirs();
|
pdf.getParentFile().mkdirs();
|
||||||
}
|
}
|
||||||
try (org.apache.pdfbox.pdmodel.PDDocument doc = new org.apache.pdfbox.pdmodel.PDDocument()) {
|
try (org.apache.pdfbox.pdmodel.PDDocument document = new org.apache.pdfbox.pdmodel.PDDocument()) {
|
||||||
org.apache.pdfbox.pdmodel.PDPage page = new org.apache.pdfbox.pdmodel.PDPage();
|
org.apache.pdfbox.pdmodel.PDPage page = new org.apache.pdfbox.pdmodel.PDPage();
|
||||||
doc.addPage(page);
|
document.addPage(page);
|
||||||
try (org.apache.pdfbox.pdmodel.PDPageContentStream stream = new org.apache.pdfbox.pdmodel.PDPageContentStream(doc, page)) {
|
try (org.apache.pdfbox.pdmodel.PDPageContentStream cs = new org.apache.pdfbox.pdmodel.PDPageContentStream(document, page)) {
|
||||||
stream.beginText();
|
cs.beginText();
|
||||||
stream.setFont(new org.apache.pdfbox.pdmodel.font.PDType1Font(org.apache.pdfbox.pdmodel.font.Standard14Fonts.FontName.HELVETICA_BOLD), 16);
|
cs.setFont(new org.apache.pdfbox.pdmodel.font.PDType1Font(org.apache.pdfbox.pdmodel.font.Standard14Fonts.FontName.HELVETICA_BOLD), 16);
|
||||||
stream.newLineAtOffset(50, 700);
|
cs.newLineAtOffset(50, 700);
|
||||||
stream.showText("CyberCore Signer — Documento PDF de Demonstração");
|
cs.showText("CyberCore Signer - Documento de Teste de Assinatura");
|
||||||
stream.endText();
|
cs.endText();
|
||||||
}
|
}
|
||||||
doc.save(pdf);
|
document.save(pdf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return pdf;
|
return pdf;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void saveNodeSnapshot(Scene scene, File targetFile) throws Exception {
|
private static void saveNodeSnapshot(Scene scene, File targetFile) throws Exception {
|
||||||
WritableImage image = scene.snapshot(null);
|
saveNodeSnapshotScaled(scene, targetFile, 1.0);
|
||||||
int w = (int) image.getWidth();
|
}
|
||||||
int h = (int) image.getHeight();
|
|
||||||
BufferedImage bufferedImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
|
private static void saveNodeSnapshotScaled(Scene scene, File targetFile, double scale) throws Exception {
|
||||||
|
SnapshotParameters params = new SnapshotParameters();
|
||||||
|
if (scale != 1.0) {
|
||||||
|
params.setTransform(Transform.scale(scale, scale));
|
||||||
|
}
|
||||||
|
WritableImage image = scene.getRoot().snapshot(params, null);
|
||||||
|
int width = (int) image.getWidth();
|
||||||
|
int height = (int) image.getHeight();
|
||||||
|
BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
|
||||||
PixelReader reader = image.getPixelReader();
|
PixelReader reader = image.getPixelReader();
|
||||||
for (int y = 0; y < h; y++) {
|
for (int y = 0; y < height; y++) {
|
||||||
for (int x = 0; x < w; x++) {
|
for (int x = 0; x < width; x++) {
|
||||||
bufferedImage.setRGB(x, y, reader.getArgb(x, y));
|
bufferedImage.setRGB(x, y, reader.getArgb(x, y));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ImageIO.write(bufferedImage, "png", targetFile);
|
ImageIO.write(bufferedImage, "png", targetFile);
|
||||||
|
System.out.println("Screenshot gerada: " + targetFile.getName() + " (" + width + "x" + height + ")");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ class AssetManagerTest {
|
|||||||
String[] iconNames = {
|
String[] iconNames = {
|
||||||
"pdf", "xml", "file", "batch", "attached", "co-sign",
|
"pdf", "xml", "file", "batch", "attached", "co-sign",
|
||||||
"timestamp", "ltv", "validate", "validate-timestamp", "exit",
|
"timestamp", "ltv", "validate", "validate-timestamp", "exit",
|
||||||
"logs", "settings", "tutorial", "cert", "about", "sun", "moon",
|
"logs", "settings", "tutorial", "cert", "about",
|
||||||
"shield", "seal", "seal-icp-brasil", "check", "error", "star", "close", "arrow-right",
|
"shield", "seal", "seal-icp-brasil", "check", "error", "star", "close", "arrow-right",
|
||||||
"zoom-in", "zoom-out", "zoom-fit", "refresh"
|
"zoom-in", "zoom-out", "zoom-fit", "refresh"
|
||||||
};
|
};
|
||||||
@@ -56,17 +56,10 @@ class AssetManagerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("Deve repintar dinamicamente elementos currentColor ao mudar o tema")
|
@DisplayName("Deve carregar ícones SVG com cor dinâmica sem lançar exceções")
|
||||||
void shouldRepaintCurrentColorDynamicallyOnThemeChange() {
|
void shouldRenderDynamicSvgIcons() {
|
||||||
Node iconNode = br.com.cybercore.signer.ui.components.Icons.settings(24);
|
Node iconNode = br.com.cybercore.signer.ui.components.Icons.settings(24);
|
||||||
assertThat(iconNode).isInstanceOf(javafx.scene.layout.Pane.class);
|
assertThat(iconNode).isInstanceOf(javafx.scene.layout.Pane.class);
|
||||||
|
|
||||||
// Alterna o tema e garante que não lança erro
|
|
||||||
br.com.cybercore.signer.ui.theme.ThemeManager.setCurrentTheme(br.com.cybercore.signer.ui.theme.ThemeManager.AppTheme.DARK);
|
|
||||||
assertThat(br.com.cybercore.signer.ui.theme.ThemeManager.isDark()).isTrue();
|
|
||||||
|
|
||||||
br.com.cybercore.signer.ui.theme.ThemeManager.setCurrentTheme(br.com.cybercore.signer.ui.theme.ThemeManager.AppTheme.LIGHT);
|
|
||||||
assertThat(br.com.cybercore.signer.ui.theme.ThemeManager.isDark()).isFalse();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -46,8 +46,8 @@ class DesignTokenGuardrailTest {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Detecta entrada nos blocos de tokens .theme-light e .theme-dark
|
// Detecta entrada no bloco canônico de tokens .root
|
||||||
if (trimmed.contains(".theme-light") || trimmed.contains(".theme-dark")) {
|
if (trimmed.startsWith(".root") || trimmed.contains(".root ")) {
|
||||||
insideTokenBlock = true;
|
insideTokenBlock = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,6 +108,51 @@ class DesignTokenGuardrailTest {
|
|||||||
.isEmpty();
|
.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Código da UI e CSS não devem conter referências a .theme-dark, AppTheme ou isDark")
|
||||||
|
void shouldNotContainDarkThemeReferencesInUiCodeOrCss() throws Exception {
|
||||||
|
// 1. Valida theme.css e no-effects.css
|
||||||
|
String[] cssFiles = {"/styles/theme.css", "/styles/no-effects.css"};
|
||||||
|
for (String cssPath : cssFiles) {
|
||||||
|
InputStream stream = getClass().getResourceAsStream(cssPath);
|
||||||
|
if (stream != null) {
|
||||||
|
String content = new String(stream.readAllBytes(), StandardCharsets.UTF_8);
|
||||||
|
assertThat(content)
|
||||||
|
.withFailMessage("Arquivo %s ainda contém referências a .theme-dark ou .theme-light!", cssPath)
|
||||||
|
.doesNotContain(".theme-dark")
|
||||||
|
.doesNotContain(".theme-light");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Valida fontes Java em src/main/java
|
||||||
|
java.nio.file.Path sourceRoot = java.nio.file.Files.exists(java.nio.file.Path.of("src/main/java"))
|
||||||
|
? java.nio.file.Path.of("src/main/java")
|
||||||
|
: java.nio.file.Path.of("modules/cybercore-ui/src/main/java");
|
||||||
|
|
||||||
|
List<String> violations = new ArrayList<>();
|
||||||
|
try (var stream = java.nio.file.Files.walk(sourceRoot)) {
|
||||||
|
List<java.nio.file.Path> javaFiles = stream
|
||||||
|
.filter(java.nio.file.Files::isRegularFile)
|
||||||
|
.filter(p -> p.toString().endsWith(".java"))
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
for (java.nio.file.Path file : javaFiles) {
|
||||||
|
List<String> lines = java.nio.file.Files.readAllLines(file, StandardCharsets.UTF_8);
|
||||||
|
for (int i = 0; i < lines.size(); i++) {
|
||||||
|
String line = lines.get(i);
|
||||||
|
if (line.contains("AppTheme") || line.contains("isDark(") || line.contains(".theme-dark")) {
|
||||||
|
violations.add(String.format("%s:%d -> %s", file.getFileName(), i + 1, line.trim()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assertThat(violations)
|
||||||
|
.withFailMessage("Foram encontradas referências obsoletas a tema escuro no código da UI:\n%s",
|
||||||
|
String.join("\n", violations))
|
||||||
|
.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("theme.css deve definir todos os tokens de cores semânticas primários e de superfície")
|
@DisplayName("theme.css deve definir todos os tokens de cores semânticas primários e de superfície")
|
||||||
void shouldDefineAllRequiredSemanticColorTokens() throws Exception {
|
void shouldDefineAllRequiredSemanticColorTokens() throws Exception {
|
||||||
@@ -248,8 +293,8 @@ class DesignTokenGuardrailTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("(c) Relação de contraste WCAG 2.1 deve ser >= 4.5:1 para texto normal nos temas claro e escuro")
|
@DisplayName("(c) Relação de contraste WCAG 2.1 deve ser >= 4.5:1 para texto normal no tema único (.root)")
|
||||||
void shouldMeetWcagContrastRequirementsInBothThemes() throws Exception {
|
void shouldMeetWcagContrastRequirementsInTheme() throws Exception {
|
||||||
InputStream stream = getClass().getResourceAsStream("/styles/theme.css");
|
InputStream stream = getClass().getResourceAsStream("/styles/theme.css");
|
||||||
assertThat(stream).isNotNull();
|
assertThat(stream).isNotNull();
|
||||||
|
|
||||||
@@ -258,14 +303,11 @@ class DesignTokenGuardrailTest {
|
|||||||
content = new String(stream.readAllBytes(), StandardCharsets.UTF_8);
|
content = new String(stream.readAllBytes(), StandardCharsets.UTF_8);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extrai mapa de tokens por tema (.theme-light e .theme-dark)
|
// Extrai mapa de tokens definidos no bloco .root
|
||||||
java.util.Map<String, String> lightTokens = extractTokensFromBlock(content, ".theme-light");
|
java.util.Map<String, String> tokens = extractTokensFromBlock(content, ".root");
|
||||||
java.util.Map<String, String> darkTokens = extractTokensFromBlock(content, ".theme-dark");
|
assertThat(tokens).isNotEmpty();
|
||||||
|
|
||||||
assertThat(lightTokens).isNotEmpty();
|
// Pares a validar: (texto, fundo)
|
||||||
assertThat(darkTokens).isNotEmpty();
|
|
||||||
|
|
||||||
// Pares a validar em cada tema: (texto, fundo)
|
|
||||||
String[][] pairs = {
|
String[][] pairs = {
|
||||||
{"-fx-color-text", "-fx-color-background"},
|
{"-fx-color-text", "-fx-color-background"},
|
||||||
{"-fx-color-text", "-fx-color-surface"},
|
{"-fx-color-text", "-fx-color-surface"},
|
||||||
@@ -281,24 +323,12 @@ class DesignTokenGuardrailTest {
|
|||||||
String textToken = pair[0];
|
String textToken = pair[0];
|
||||||
String bgToken = pair[1];
|
String bgToken = pair[1];
|
||||||
|
|
||||||
// Tema Claro
|
if (tokens.containsKey(textToken) && tokens.containsKey(bgToken)) {
|
||||||
if (lightTokens.containsKey(textToken) && lightTokens.containsKey(bgToken)) {
|
String textColor = tokens.get(textToken);
|
||||||
String textColor = lightTokens.get(textToken);
|
String bgColor = tokens.get(bgToken);
|
||||||
String bgColor = lightTokens.get(bgToken);
|
|
||||||
double ratio = calculateContrastRatio(textColor, bgColor);
|
double ratio = calculateContrastRatio(textColor, bgColor);
|
||||||
if (ratio < 4.5) {
|
if (ratio < 4.5) {
|
||||||
contrastFailures.add(String.format("Tema Claro: %s (%s) sobre %s (%s) = %.2f:1 (mínimo 4.5:1)",
|
contrastFailures.add(String.format("%s (%s) sobre %s (%s) = %.2f:1 (mínimo 4.5:1)",
|
||||||
textToken, textColor, bgToken, bgColor, ratio));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tema Escuro
|
|
||||||
if (darkTokens.containsKey(textToken) && darkTokens.containsKey(bgToken)) {
|
|
||||||
String textColor = darkTokens.get(textToken);
|
|
||||||
String bgColor = darkTokens.get(bgToken);
|
|
||||||
double ratio = calculateContrastRatio(textColor, bgColor);
|
|
||||||
if (ratio < 4.5) {
|
|
||||||
contrastFailures.add(String.format("Tema Escuro: %s (%s) sobre %s (%s) = %.2f:1 (mínimo 4.5:1)",
|
|
||||||
textToken, textColor, bgToken, bgColor, ratio));
|
textToken, textColor, bgToken, bgColor, ratio));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,406 @@
|
|||||||
|
package br.com.cybercore.signer.ui.view;
|
||||||
|
|
||||||
|
import br.com.cybercore.signer.core.crypto.SigningService;
|
||||||
|
import br.com.cybercore.signer.core.domain.CertificateStatus;
|
||||||
|
import br.com.cybercore.signer.core.domain.CertificateType;
|
||||||
|
import br.com.cybercore.signer.core.domain.ManagedCertificate;
|
||||||
|
import br.com.cybercore.signer.core.domain.SigningContext;
|
||||||
|
import br.com.cybercore.signer.core.pdf.PdfSigningService;
|
||||||
|
import br.com.cybercore.signer.core.pdf.seal.CpfDisplayMode;
|
||||||
|
import br.com.cybercore.signer.core.pdf.seal.PdfSealThemeRegistry;
|
||||||
|
import br.com.cybercore.signer.core.provider.CertificateManager;
|
||||||
|
import br.com.cybercore.signer.ui.components.SvgIcons;
|
||||||
|
import javafx.application.Platform;
|
||||||
|
import javafx.scene.Node;
|
||||||
|
import javafx.scene.Parent;
|
||||||
|
import javafx.scene.Scene;
|
||||||
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.stage.Stage;
|
||||||
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.time.temporal.ChronoUnit;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Teste automatizado de integridade de layout para telas e diálogos do CyberCore Signer.
|
||||||
|
* Garante que nenhum botão tenha seu texto ou área comprimida/truncada (largura ou altura menor que pref).
|
||||||
|
* Valida também as dimensões mínimas dos cartões do Hub (min 68px) e da lista lateral (min 40px).
|
||||||
|
*/
|
||||||
|
class ButtonLayoutIntegrityTest {
|
||||||
|
|
||||||
|
private static boolean javaFxAvailable = false;
|
||||||
|
|
||||||
|
@BeforeAll
|
||||||
|
static void initJavaFx() {
|
||||||
|
CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
try {
|
||||||
|
Platform.startup(() -> {
|
||||||
|
javaFxAvailable = true;
|
||||||
|
latch.countDown();
|
||||||
|
});
|
||||||
|
if (!latch.await(5, TimeUnit.SECONDS)) {
|
||||||
|
javaFxAvailable = false;
|
||||||
|
}
|
||||||
|
} catch (Throwable t) {
|
||||||
|
if (t instanceof IllegalStateException && t.getMessage() != null
|
||||||
|
&& t.getMessage().contains("Toolkit already initialized")) {
|
||||||
|
javaFxAvailable = true;
|
||||||
|
} else {
|
||||||
|
javaFxAvailable = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void runOnFxThread(ThrowingRunnable action) throws Throwable {
|
||||||
|
org.junit.jupiter.api.Assumptions.assumeTrue(javaFxAvailable, "Ambiente gráfico JavaFX (X11/Xvfb) não disponível.");
|
||||||
|
|
||||||
|
CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
AtomicReference<Throwable> error = new AtomicReference<>();
|
||||||
|
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
try {
|
||||||
|
action.run();
|
||||||
|
} catch (Throwable t) {
|
||||||
|
error.set(t);
|
||||||
|
} finally {
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
assertThat(latch.await(8, TimeUnit.SECONDS)).isTrue();
|
||||||
|
if (error.get() != null) {
|
||||||
|
throw error.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
interface ThrowingRunnable {
|
||||||
|
void run() throws Throwable;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void layoutAndAssert(Stage stage, double width, double height, String screenName) {
|
||||||
|
Scene scene = stage.getScene();
|
||||||
|
if (width > 0 && height > 0) {
|
||||||
|
stage.setWidth(width);
|
||||||
|
stage.setHeight(height);
|
||||||
|
}
|
||||||
|
Parent root = scene.getRoot();
|
||||||
|
root.applyCss();
|
||||||
|
if (width > 0 && height > 0) {
|
||||||
|
root.resizeRelocate(0, 0, width, height);
|
||||||
|
}
|
||||||
|
root.layout();
|
||||||
|
|
||||||
|
List<Button> buttons = new ArrayList<>();
|
||||||
|
findButtons(root, buttons);
|
||||||
|
|
||||||
|
for (Button b : buttons) {
|
||||||
|
if (!b.isVisible() || !b.isManaged()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
double w = b.getWidth();
|
||||||
|
double h = b.getHeight();
|
||||||
|
double prefW = b.prefWidth(-1);
|
||||||
|
double prefH = b.prefHeight(-1);
|
||||||
|
|
||||||
|
assertThat(w)
|
||||||
|
.withFailMessage("Botão '%s' na tela [%s] teve largura comprimida: w=%.1f < prefW=%.1f",
|
||||||
|
b.getText(), screenName, w, prefW)
|
||||||
|
.isGreaterThanOrEqualTo(prefW - 0.5);
|
||||||
|
|
||||||
|
assertThat(h)
|
||||||
|
.withFailMessage("Botão '%s' na tela [%s] teve altura comprimida: h=%.1f < prefH=%.1f",
|
||||||
|
b.getText(), screenName, h, prefH)
|
||||||
|
.isGreaterThanOrEqualTo(prefH - 0.5);
|
||||||
|
|
||||||
|
if (b.getStyleClass().contains("cyber-action-card")) {
|
||||||
|
assertThat(h)
|
||||||
|
.withFailMessage("Cartão do Hub '%s' na tela [%s] teve altura menor que 68px: h=%.1f",
|
||||||
|
b.getText(), screenName, h)
|
||||||
|
.isGreaterThanOrEqualTo(68.0 - 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (b.getStyleClass().contains("cyber-side-card")) {
|
||||||
|
assertThat(h)
|
||||||
|
.withFailMessage("Item lateral do Hub '%s' na tela [%s] teve altura menor que 40px: h=%.1f",
|
||||||
|
b.getText(), screenName, h)
|
||||||
|
.isGreaterThanOrEqualTo(40.0 - 0.5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void findButtons(Node node, List<Button> accumulator) {
|
||||||
|
if (node instanceof Button btn) {
|
||||||
|
accumulator.add(btn);
|
||||||
|
}
|
||||||
|
if (node instanceof Parent parent) {
|
||||||
|
for (Node child : parent.getChildrenUnmodifiable()) {
|
||||||
|
findButtons(child, accumulator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ManagedCertificate createSampleCertificate() {
|
||||||
|
return new ManagedCertificate(
|
||||||
|
"cert-test-id",
|
||||||
|
"JOAO DA SILVA:12345678900",
|
||||||
|
CertificateType.A1,
|
||||||
|
CertificateStatus.AVAILABLE,
|
||||||
|
"CN=JOAO DA SILVA:12345678900, OU=Autenticado por AR, O=ICP-Brasil, C=BR",
|
||||||
|
"CN=AC SERPRO RFB v5, O=ICP-Brasil, C=BR",
|
||||||
|
"1A2B3C4D5E",
|
||||||
|
"11223344556677889900",
|
||||||
|
"AABBCCDDEEFF00112233445566778899",
|
||||||
|
Instant.now().minus(30, ChronoUnit.DAYS),
|
||||||
|
Instant.now().plus(330, ChronoUnit.DAYS),
|
||||||
|
"RSA",
|
||||||
|
2048,
|
||||||
|
"JOAO DA SILVA:12345678900",
|
||||||
|
"12345678900",
|
||||||
|
null,
|
||||||
|
"SunJSSE",
|
||||||
|
null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static File getOrCreateSamplePdf() throws Exception {
|
||||||
|
File pdf = new File("target/test-sample.pdf");
|
||||||
|
if (!pdf.exists()) {
|
||||||
|
if (pdf.getParentFile() != null) {
|
||||||
|
pdf.getParentFile().mkdirs();
|
||||||
|
}
|
||||||
|
try (org.apache.pdfbox.pdmodel.PDDocument document = new org.apache.pdfbox.pdmodel.PDDocument()) {
|
||||||
|
org.apache.pdfbox.pdmodel.PDPage page = new org.apache.pdfbox.pdmodel.PDPage();
|
||||||
|
document.addPage(page);
|
||||||
|
try (org.apache.pdfbox.pdmodel.PDPageContentStream cs = new org.apache.pdfbox.pdmodel.PDPageContentStream(document, page)) {
|
||||||
|
cs.beginText();
|
||||||
|
cs.setFont(new org.apache.pdfbox.pdmodel.font.PDType1Font(org.apache.pdfbox.pdmodel.font.Standard14Fonts.FontName.HELVETICA_BOLD), 14);
|
||||||
|
cs.newLineAtOffset(50, 700);
|
||||||
|
cs.showText("CyberCore Signer Test PDF");
|
||||||
|
cs.endText();
|
||||||
|
}
|
||||||
|
document.save(pdf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pdf;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Hub Principal (MainAppView): nenhum botão deve truncar e os cartões devem ter altura >= 68px")
|
||||||
|
void testMainAppViewButtons() throws Throwable {
|
||||||
|
runOnFxThread(() -> {
|
||||||
|
Stage stage = new Stage();
|
||||||
|
MainAppView view = new MainAppView(stage);
|
||||||
|
layoutAndAssert(stage, 780, 500, "MainAppView 780x500");
|
||||||
|
layoutAndAssert(stage, 1024, 768, "MainAppView 1024x768");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("ExitConfirmationDialog: botões Cancelar, Encerrar e Minimizar com tamanho preferencial")
|
||||||
|
void testExitConfirmationDialogButtons() throws Throwable {
|
||||||
|
runOnFxThread(() -> {
|
||||||
|
ExitConfirmationDialog dialog = new ExitConfirmationDialog(null);
|
||||||
|
layoutAndAssert(dialog.getStage(), 480, 220, "ExitConfirmationDialog");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("ApplicationApprovalDialog: botões com tamanho preferencial e sem truncamento")
|
||||||
|
void testApplicationApprovalDialogButtons() throws Throwable {
|
||||||
|
runOnFxThread(() -> {
|
||||||
|
Stage stage = new Stage();
|
||||||
|
ApplicationApprovalDialog dialog = new ApplicationApprovalDialog(
|
||||||
|
stage,
|
||||||
|
"https://contratos.empresa.com.br",
|
||||||
|
"Sistema de Contratos Online",
|
||||||
|
"app-test-id"
|
||||||
|
);
|
||||||
|
layoutAndAssert(dialog.getStage(), 520, 360, "ApplicationApprovalDialog");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("A1PasswordDialog: botões Cancelar e Desbloquear com tamanho preferencial")
|
||||||
|
void testA1PasswordDialogButtons() throws Throwable {
|
||||||
|
runOnFxThread(() -> {
|
||||||
|
A1PasswordDialog dialog = new A1PasswordDialog(null, "certificado-empresa.pfx");
|
||||||
|
layoutAndAssert(dialog.getStage(), 460, 260, "A1PasswordDialog");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("SignaturePromptDialog: botões Cancelar e Confirmar com tamanho preferencial")
|
||||||
|
void testSignaturePromptDialogButtons() throws Throwable {
|
||||||
|
runOnFxThread(() -> {
|
||||||
|
ManagedCertificate cert = createSampleCertificate();
|
||||||
|
SigningContext ctx = new SigningContext(
|
||||||
|
"req-1234",
|
||||||
|
"app-web",
|
||||||
|
new byte[32],
|
||||||
|
"SHA256withRSA",
|
||||||
|
null,
|
||||||
|
Instant.now().plusSeconds(60)
|
||||||
|
);
|
||||||
|
SignaturePromptDialog dialog = new SignaturePromptDialog(
|
||||||
|
null,
|
||||||
|
"Portal Financeiro",
|
||||||
|
"https://financeiro.gov.br",
|
||||||
|
cert,
|
||||||
|
ctx,
|
||||||
|
"Ordem_Pagamento_2026.pdf",
|
||||||
|
"PDF",
|
||||||
|
false
|
||||||
|
);
|
||||||
|
layoutAndAssert(dialog.getStage(), 560, 420, "SignaturePromptDialog");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("CertificatesManagerDialog: botões de ação e fechamento com tamanho preferencial")
|
||||||
|
void testCertificatesManagerDialogButtons() throws Throwable {
|
||||||
|
runOnFxThread(() -> {
|
||||||
|
CertificateManager certManager = CertificateManager.createDefault();
|
||||||
|
CertificatesManagerDialog dialog = new CertificatesManagerDialog(null, certManager);
|
||||||
|
layoutAndAssert(dialog.getStage(), 760, 520, "CertificatesManagerDialog");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("CertificateDetailsDialog: botão Fechar com tamanho preferencial")
|
||||||
|
void testCertificateDetailsDialogButtons() throws Throwable {
|
||||||
|
runOnFxThread(() -> {
|
||||||
|
ManagedCertificate cert = createSampleCertificate();
|
||||||
|
CertificateDetailsDialog dialog = new CertificateDetailsDialog(null, cert);
|
||||||
|
layoutAndAssert(dialog.getStage(), 620, 500, "CertificateDetailsDialog");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("FileSignDialog: botões com tamanho preferencial")
|
||||||
|
void testFileSignDialogButtons() throws Throwable {
|
||||||
|
runOnFxThread(() -> {
|
||||||
|
CertificateManager certManager = CertificateManager.createDefault();
|
||||||
|
SigningService signingService = new SigningService(certManager);
|
||||||
|
FileSignDialog dialog = new FileSignDialog(
|
||||||
|
null,
|
||||||
|
"Assinar Arquivo",
|
||||||
|
"*.*",
|
||||||
|
SvgIcons.fileIcon(),
|
||||||
|
certManager,
|
||||||
|
signingService
|
||||||
|
);
|
||||||
|
layoutAndAssert(dialog.getStage(), 720, 500, "FileSignDialog");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("LogsViewerDialog: botões Limpar, Exportar e Fechar com tamanho preferencial")
|
||||||
|
void testLogsViewerDialogButtons() throws Throwable {
|
||||||
|
runOnFxThread(() -> {
|
||||||
|
LogsViewerDialog dialog = new LogsViewerDialog(null);
|
||||||
|
layoutAndAssert(dialog.getStage(), 800, 520, "LogsViewerDialog");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("TutorialDialog: botões de navegação e fechar com tamanho preferencial")
|
||||||
|
void testTutorialDialogButtons() throws Throwable {
|
||||||
|
runOnFxThread(() -> {
|
||||||
|
TutorialDialog dialog = new TutorialDialog(null);
|
||||||
|
layoutAndAssert(dialog.getStage(), 640, 480, "TutorialDialog");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("XmlSignDialog: botões com tamanho preferencial")
|
||||||
|
void testXmlSignDialogButtons() throws Throwable {
|
||||||
|
runOnFxThread(() -> {
|
||||||
|
CertificateManager certManager = CertificateManager.createDefault();
|
||||||
|
XmlSignDialog dialog = new XmlSignDialog(null, certManager);
|
||||||
|
layoutAndAssert(dialog.getStage(), 720, 500, "XmlSignDialog");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("BatchSigningDialog: botões com tamanho preferencial")
|
||||||
|
void testBatchSigningDialogButtons() throws Throwable {
|
||||||
|
runOnFxThread(() -> {
|
||||||
|
CertificateManager certManager = CertificateManager.createDefault();
|
||||||
|
SigningService signingService = new SigningService(certManager);
|
||||||
|
BatchSigningDialog dialog = new BatchSigningDialog(null, certManager, signingService);
|
||||||
|
layoutAndAssert(dialog.getStage(), 760, 520, "BatchSigningDialog");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("SealManagementDialog e SealDesignerDialog: botões com tamanho preferencial")
|
||||||
|
void testSealDialogsButtons() throws Throwable {
|
||||||
|
runOnFxThread(() -> {
|
||||||
|
SealManagementDialog sealMgmt = new SealManagementDialog(null);
|
||||||
|
layoutAndAssert(sealMgmt.getStage(), 700, 480, "SealManagementDialog");
|
||||||
|
|
||||||
|
SealDesignerDialog designer = new SealDesignerDialog(null, PdfSealThemeRegistry.getDefaultTheme(), saved -> {});
|
||||||
|
layoutAndAssert(designer.getStage(), 880, 580, "SealDesignerDialog");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("SignatureValidationDialog: botões com tamanho preferencial")
|
||||||
|
void testSignatureValidationDialogButtons() throws Throwable {
|
||||||
|
runOnFxThread(() -> {
|
||||||
|
SignatureValidationDialog dialog = new SignatureValidationDialog(null);
|
||||||
|
layoutAndAssert(dialog.getStage(), 680, 520, "SignatureValidationDialog");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("PdfSignDialog e PdfCertificateSignDialog: botões com tamanho preferencial")
|
||||||
|
void testPdfDialogsButtons() throws Throwable {
|
||||||
|
runOnFxThread(() -> {
|
||||||
|
File samplePdf = getOrCreateSamplePdf();
|
||||||
|
CertificateManager certManager = CertificateManager.createDefault();
|
||||||
|
PdfSigningService pdfSigningService = new PdfSigningService(certManager);
|
||||||
|
|
||||||
|
PdfSignDialog pdfSignDialog = new PdfSignDialog(null, certManager, samplePdf);
|
||||||
|
layoutAndAssert(pdfSignDialog.getStage(), 980, 680, "PdfSignDialog");
|
||||||
|
|
||||||
|
PdfCertificateSignDialog.SealConfig config = new PdfCertificateSignDialog.SealConfig(
|
||||||
|
true,
|
||||||
|
PdfSealThemeRegistry.getDefaultTheme(),
|
||||||
|
CpfDisplayMode.MASKED,
|
||||||
|
true,
|
||||||
|
"Assinado digitalmente",
|
||||||
|
PdfSignDialog.PositionOption.BOTTOM_RIGHT,
|
||||||
|
PdfSignDialog.PageOption.LAST_PAGE,
|
||||||
|
false,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
"ICP-Brasil",
|
||||||
|
"Brasília, DF",
|
||||||
|
1.0
|
||||||
|
);
|
||||||
|
|
||||||
|
PdfCertificateSignDialog certSignDialog = new PdfCertificateSignDialog(
|
||||||
|
null,
|
||||||
|
certManager,
|
||||||
|
pdfSigningService,
|
||||||
|
samplePdf,
|
||||||
|
config,
|
||||||
|
"Selo Padrão ICP-Brasil"
|
||||||
|
);
|
||||||
|
layoutAndAssert(certSignDialog.getStage(), 760, 520, "PdfCertificateSignDialog");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||