Skip to content

Commit f340abc

Browse files
committed
feat: logo
1 parent c5d5e39 commit f340abc

4 files changed

Lines changed: 229 additions & 2 deletions

File tree

src/main/java/com/tractionrec/recrec/RecRecApplication.java

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,33 @@
99
import com.tractionrec.recrec.ui.TypographyConstants;
1010

1111
import javax.swing.*;
12+
import javax.imageio.ImageIO;
13+
import java.awt.*;
14+
import java.awt.image.BufferedImage;
15+
import java.io.IOException;
16+
import java.io.InputStream;
17+
import java.util.ArrayList;
18+
import java.util.Arrays;
19+
import java.util.List;
1220

1321
public class RecRecApplication {
1422

1523
public static String MANIFEST_PROD_PROPERTY_NAME = "Element-Express-Production";
1624

1725
public static void main(String[] args) {
26+
// Configure platform-specific properties before UI setup
27+
configurePlatformProperties();
28+
1829
// Configure DNS caching for better performance with high concurrent requests
1930
configureDNSCaching();
2031

2132
TractionRecTheme.setup();
2233
RecRecState state = new RecRecState();
2334
JFrame applicationFrame = new JFrame("RecRec");
35+
36+
// Set the taskbar icon with multiple approaches for better platform support
37+
setApplicationIcon(applicationFrame);
38+
2439
applicationFrame.setJMenuBar(buildMenuBar());
2540
RecFormStack stack = new RecFormStack(applicationFrame);
2641
RecRecStart startForm = new RecRecStart(state, stack);
@@ -36,6 +51,64 @@ public static boolean isProduction() {
3651
return Manifests.exists(MANIFEST_PROD_PROPERTY_NAME) && "true".equals(Manifests.read(MANIFEST_PROD_PROPERTY_NAME));
3752
}
3853

54+
/**
55+
* Set application icon using multiple approaches for maximum compatibility
56+
* @param frame The main application frame
57+
*/
58+
private static void setApplicationIcon(JFrame frame) {
59+
Image logoImage = loadLogoImage();
60+
if (logoImage == null) {
61+
System.err.println("Warning: Could not load application logo");
62+
return;
63+
}
64+
65+
try {
66+
// Approach 1: Traditional JFrame icon (for window title bar and Alt+Tab)
67+
List<Image> iconImages = loadIconImages();
68+
if (!iconImages.isEmpty()) {
69+
frame.setIconImages(iconImages);
70+
}
71+
72+
// Approach 2: Modern Java 9+ Taskbar API (for dock/taskbar)
73+
if (Taskbar.isTaskbarSupported()) {
74+
Taskbar taskbar = Taskbar.getTaskbar();
75+
if (taskbar.isSupported(Taskbar.Feature.ICON_IMAGE)) {
76+
// Use a larger icon for taskbar (typically 64px or 128px works well)
77+
Image taskbarIcon = createSquareIcon(logoImage, 128);
78+
taskbar.setIconImage(taskbarIcon);
79+
}
80+
}
81+
82+
} catch (Exception e) {
83+
System.err.println("Warning: Could not set application icon: " + e.getMessage());
84+
// Fallback to basic approach
85+
frame.setIconImage(logoImage);
86+
}
87+
}
88+
89+
/**
90+
* Configure platform-specific properties for better application integration
91+
*/
92+
private static void configurePlatformProperties() {
93+
// macOS specific properties
94+
if (System.getProperty("os.name").toLowerCase().contains("mac")) {
95+
// Set application name in dock
96+
System.setProperty("apple.awt.application.name", "RecRec");
97+
98+
// Enable native look and feel integration
99+
System.setProperty("apple.laf.useScreenMenuBar", "true");
100+
101+
// Set dock icon behavior
102+
System.setProperty("apple.awt.application.appearance", "system");
103+
}
104+
105+
// Windows specific properties
106+
if (System.getProperty("os.name").toLowerCase().contains("windows")) {
107+
// Enable Windows-specific optimizations
108+
System.setProperty("sun.java2d.d3d", "true");
109+
}
110+
}
111+
39112
/**
40113
* Configure DNS caching to prevent DNS resolution issues with high concurrent requests
41114
*/
@@ -63,6 +136,16 @@ private static JMenuBar buildMenuBar() {
63136

64137
topMenuBar.add(Box.createHorizontalStrut(10));
65138

139+
// Add company logo
140+
ImageIcon logoIcon = loadLogoIcon(20); // 20px height for menu bar
141+
if (logoIcon != null) {
142+
JLabel logoLabel = new JLabel(logoIcon);
143+
// Add vertical padding to better align with menu bar
144+
logoLabel.setBorder(BorderFactory.createEmptyBorder(4, 0, 4, 0));
145+
topMenuBar.add(logoLabel);
146+
topMenuBar.add(Box.createHorizontalStrut(8));
147+
}
148+
66149
JLabel titleLabel = new JLabel("RecRec");
67150
titleLabel.setForeground(TractionRecTheme.TEXT_INVERSE);
68151
titleLabel.setFont(TypographyConstants.FONT_SUBHEADING);
@@ -103,4 +186,98 @@ private static JMenuBar buildMenuBar() {
103186
return topMenuBar;
104187
}
105188

189+
/**
190+
* Load multiple sizes of the company logo for taskbar/dock icons
191+
* @return List of Image objects in different sizes
192+
*/
193+
private static List<Image> loadIconImages() {
194+
List<Image> iconImages = new ArrayList<>();
195+
Image baseImage = loadLogoImage();
196+
197+
if (baseImage != null) {
198+
// Common icon sizes for different platforms
199+
int[] sizes = {16, 20, 24, 32, 40, 48, 64, 128, 256};
200+
201+
for (int size : sizes) {
202+
// For taskbar icons, we want to maintain aspect ratio but fit within square bounds
203+
Image scaledImage = createSquareIcon(baseImage, size);
204+
iconImages.add(scaledImage);
205+
}
206+
}
207+
208+
return iconImages;
209+
}
210+
211+
/**
212+
* Create a square icon from the base image, maintaining aspect ratio and centering
213+
* @param baseImage The source image
214+
* @param size The target square size
215+
* @return Square image with the logo centered
216+
*/
217+
private static Image createSquareIcon(Image baseImage, int size) {
218+
int originalWidth = baseImage.getWidth(null);
219+
int originalHeight = baseImage.getHeight(null);
220+
221+
// Calculate scaling to fit within square while maintaining aspect ratio
222+
double scale = Math.min((double) size / originalWidth, (double) size / originalHeight);
223+
int scaledWidth = (int) (originalWidth * scale);
224+
int scaledHeight = (int) (originalHeight * scale);
225+
226+
// Create a square buffered image with transparent background
227+
BufferedImage squareIcon = new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB);
228+
Graphics2D g2d = squareIcon.createGraphics();
229+
230+
// Enable high-quality rendering
231+
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
232+
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
233+
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
234+
235+
// Center the scaled image in the square
236+
int x = (size - scaledWidth) / 2;
237+
int y = (size - scaledHeight) / 2;
238+
239+
g2d.drawImage(baseImage, x, y, scaledWidth, scaledHeight, null);
240+
g2d.dispose();
241+
242+
return squareIcon;
243+
}
244+
245+
/**
246+
* Load the company logo image from resources
247+
* @return Image object or null if loading fails
248+
*/
249+
public static Image loadLogoImage() {
250+
try {
251+
InputStream logoStream = RecRecApplication.class.getResourceAsStream("/logo.png");
252+
if (logoStream != null) {
253+
BufferedImage logoImage = ImageIO.read(logoStream);
254+
logoStream.close();
255+
return logoImage;
256+
}
257+
} catch (IOException e) {
258+
System.err.println("Failed to load logo image: " + e.getMessage());
259+
}
260+
return null;
261+
}
262+
263+
/**
264+
* Load the company logo as an ImageIcon scaled to the specified height
265+
* @param height The desired height in pixels
266+
* @return ImageIcon or null if loading fails
267+
*/
268+
public static ImageIcon loadLogoIcon(int height) {
269+
Image logoImage = loadLogoImage();
270+
if (logoImage != null) {
271+
// Calculate width to maintain aspect ratio
272+
int originalWidth = logoImage.getWidth(null);
273+
int originalHeight = logoImage.getHeight(null);
274+
int scaledWidth = (originalWidth * height) / originalHeight;
275+
276+
// Scale the image smoothly
277+
Image scaledImage = logoImage.getScaledInstance(scaledWidth, height, Image.SCALE_SMOOTH);
278+
return new ImageIcon(scaledImage);
279+
}
280+
return null;
281+
}
282+
106283
}

src/main/java/com/tractionrec/recrec/ui/RecRecAbout.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,17 @@ private JPanel createMainCard() {
6565
card.setLayout(new BoxLayout(card, BoxLayout.Y_AXIS));
6666
card.setPreferredSize(new Dimension(400, 300));
6767

68-
// Application title with icon
69-
JLabel titleLabel = new JLabel(StyleUtils.Icons.INFO + " RecRec Query Tool");
68+
// Company logo
69+
ImageIcon logoIcon = RecRecApplication.loadLogoIcon(48); // 48px height for about dialog
70+
if (logoIcon != null) {
71+
JLabel logoLabel = new JLabel(logoIcon);
72+
logoLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
73+
card.add(logoLabel);
74+
StyleUtils.addVerticalSpacing(card, StyleUtils.SPACING_MEDIUM);
75+
}
76+
77+
// Application title
78+
JLabel titleLabel = new JLabel("RecRec Query Tool");
7079
titleLabel.setFont(TypographyConstants.FONT_TITLE);
7180
titleLabel.setForeground(TractionRecTheme.PRIMARY_BLUE);
7281
titleLabel.setAlignmentX(Component.CENTER_ALIGNMENT);

src/main/resources/logo.png

12.8 KB
Loading
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package com.tractionrec.recrec;
2+
3+
import org.junit.jupiter.api.Test;
4+
5+
import javax.imageio.ImageIO;
6+
import javax.swing.ImageIcon;
7+
import java.awt.*;
8+
import java.awt.image.BufferedImage;
9+
import java.io.IOException;
10+
import java.io.InputStream;
11+
12+
import static org.junit.jupiter.api.Assertions.*;
13+
14+
public class LogoLoadingTest {
15+
16+
@Test
17+
public void testLogoFileExists() {
18+
InputStream logoStream = getClass().getResourceAsStream("/logo.png");
19+
assertNotNull(logoStream, "Logo file should exist in resources");
20+
21+
try {
22+
logoStream.close();
23+
} catch (IOException e) {
24+
// Ignore close errors in test
25+
}
26+
}
27+
28+
@Test
29+
public void testLogoCanBeLoaded() throws IOException {
30+
InputStream logoStream = getClass().getResourceAsStream("/logo.png");
31+
assertNotNull(logoStream, "Logo file should exist in resources");
32+
33+
BufferedImage logoImage = ImageIO.read(logoStream);
34+
assertNotNull(logoImage, "Logo should be readable as an image");
35+
36+
assertTrue(logoImage.getWidth() > 0, "Logo should have positive width");
37+
assertTrue(logoImage.getHeight() > 0, "Logo should have positive height");
38+
39+
logoStream.close();
40+
}
41+
}

0 commit comments

Comments
 (0)