Skip to content

Commit

Permalink
003 help view (#3)
Browse files Browse the repository at this point in the history
* Added settings information to UI

* Updated UI with more toggle options

* Added sourceforge assets

* Implemented all application settings

* Fix bug not showing correct state for auto startup setting

* Added info window

* Update sourceforge screenshot
  • Loading branch information
kyle-bowden authored Oct 6, 2024
1 parent 2243846 commit 683a89f
Show file tree
Hide file tree
Showing 8 changed files with 476 additions and 170 deletions.
Binary file added misc/sourceforge/1_popup_cap_key_state.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added misc/sourceforge/2_popup_settings.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 4 additions & 5 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>co.uk.bittwisted</groupId>
<artifactId>cap-locks-popup</artifactId>
<version>1.0.2</version>
<version>1.1.0</version>
<organization/>
<build>
<plugins>
Expand Down Expand Up @@ -65,7 +65,9 @@
<appVersion>${project.version}</appVersion> <!-- App version -->
<type>MSI</type> <!-- The output type (exe for Windows) -->
<runtimeImage>${java.home}</runtimeImage> <!-- Optional: Custom JRE, use if needed -->
<copyright>BIT TWISTED LTD 2024</copyright>
<copyright>Kyle Bowden</copyright>
<description>Simple popup for cap locks key</description>
<vendor>Kyle Bowden</vendor>
<winDirChooser>true</winDirChooser>
<winMenu>true</winMenu>
<winShortcut>true</winShortcut>
Expand All @@ -82,8 +84,6 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>



<dependencies>
<!-- https://mvnrepository.com/artifact/com.1stleg/jnativehook -->
<dependency>
Expand All @@ -97,5 +97,4 @@
<version>1.6.5</version>
</dependency>
</dependencies>

</project>
31 changes: 17 additions & 14 deletions src/main/java/co/uk/bittwisted/CapsLockHook.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import co.uk.bittwisted.enums.Position;
import co.uk.bittwisted.service.AnalyticService;
import co.uk.bittwisted.util.Helpers;
import co.uk.bittwisted.views.InfoView;
import co.uk.bittwisted.views.SettingsView;
import org.jnativehook.GlobalScreen;
import org.jnativehook.NativeHookException;
Expand Down Expand Up @@ -33,8 +34,9 @@ public class CapsLockHook extends JFrame implements NativeKeyListener {
private final Robot robot;
private final Font defaultFont = new Font("Arial Black", Font.PLAIN, 50);
private final Logger logger = Logger.getLogger(CapsLockHook.class.getName());
private final InfoView infoView;
private final SettingsView settingsView;
private boolean capsLockState;
private boolean capsLockOn;
private boolean isSuccessPopup;
private boolean RESET_IN_ACTION = false;

Expand All @@ -50,9 +52,6 @@ public class CapsLockHook extends JFrame implements NativeKeyListener {

private final GradientPaint defaultBackgroundGradient;
private final AnalyticService analyticService;
public static final String PROPERTY_LOCATION = "location";
public static final String PROPERTY_CLIENT_ID = "clientId";
public static final String PROPERTY_POPUP_DELAY = "popupDelay";

public CapsLockHook() throws AWTException {
setTitle("CapUp");
Expand Down Expand Up @@ -85,7 +84,7 @@ public CapsLockHook() throws AWTException {

// get initial state of cap lock
Toolkit kit = Toolkit.getDefaultToolkit();
capsLockState = kit.getLockingKeyState(KeyEvent.VK_CAPS_LOCK);
capsLockOn = kit.getLockingKeyState(KeyEvent.VK_CAPS_LOCK);

addMouseListener(new MouseAdapter() {
@Override
Expand All @@ -97,8 +96,11 @@ public void mouseClicked(MouseEvent e) {
Runtime.getRuntime().addShutdownHook(new Thread(this::stopCapsLockHook));

settingsView = new SettingsView(this, appConfig);
infoView = new InfoView(settingsView);
settingsView.setInfoView(infoView);
if(appConfig.isFirstTimeUser()) {
settingsView.showSettings();
infoView.showInfo();
}

updatePopupPosition(Position.valueOf(appConfig.getLocation()));
Expand Down Expand Up @@ -159,7 +161,7 @@ public void windowStateChanged(java.awt.event.WindowEvent evt) {
public void paint(Graphics g) {
super.paint(g);

String message = capsLockState ? "A" : "a";
String message = capsLockOn ? "A" : "a";

Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
Expand Down Expand Up @@ -208,7 +210,7 @@ public void paint(Graphics g) {

private void flipCapLockState() {
if(RESET_IN_ACTION) return;
capsLockState = !capsLockState;
capsLockOn = !capsLockOn;

RESET_IN_ACTION = true;
Timer timer = new Timer(500, t -> {
Expand Down Expand Up @@ -253,7 +255,7 @@ public void updatePopupPosition(Position position) {
case BOTTOM_RIGHT: setLocation(bottomRight); break;
case BOTTOM_CENTER: setLocation(bottomCenter); break;
}
appConfig.updateConfig(position, appConfig.getPopUpDelay());
appConfig.updateLocation(position);
showCapsLockStatusPopup();
}

Expand All @@ -272,9 +274,7 @@ public void updatePopupDelay(boolean isUp) {
popupDelay = 1f;
}
}
appConfig.updateConfig(
Position.valueOf(appConfig.getLocation()),
Helpers.formatWithOneDecimalPlace(popupDelay));
appConfig.updatePopUpDelay(Helpers.formatWithOneDecimalPlace(popupDelay));
setVisible(false);
showCapsLockStatusPopup();
}
Expand Down Expand Up @@ -324,7 +324,8 @@ private void quickFixUpperCaseText() {
String content = Helpers.getClipboardContentAsString();
logger.log(Level.INFO,"Convert content:" + content);
if(!content.isEmpty()) {
StringSelection selection = new StringSelection(Helpers.convertToLowerCaseWithCorrectPunctuation(content));
StringSelection selection =
new StringSelection(Helpers.convertToLowerCaseWithCorrectPunctuation(content));
clipboard.setContents(selection, null);

// Simulate Delete to delete selected text
Expand Down Expand Up @@ -368,11 +369,13 @@ public static void main(String[] args) {
public void nativeKeyPressed(NativeKeyEvent e) {
if (e.getKeyCode() == NativeKeyEvent.VC_CAPS_LOCK) {
RESET_IN_ACTION = false;
capsLockState = !capsLockState;
capsLockOn = !capsLockOn;
showCapsLockStatusPopup();
}

if(e.getKeyCode() == NativeKeyEvent.VC_Q && e.getModifiers() == NativeInputEvent.CTRL_L_MASK) {
if(appConfig.getQuickFixEnabled() &&
e.getModifiers() == NativeInputEvent.CTRL_L_MASK &&
e.getKeyCode() == NativeKeyEvent.VC_Q) {
quickFixUpperCaseText();
}
}
Expand Down
72 changes: 60 additions & 12 deletions src/main/java/co/uk/bittwisted/config/AppConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Optional;
import java.util.Properties;
import java.util.UUID;
import java.util.logging.Level;
Expand All @@ -19,14 +18,20 @@ public class AppConfig {
private final File configFile;
private final Properties properties;

public static final String PROPERTY_LOCATION = "location";
public static final String PROPERTY_CLIENT_ID = "clientId";
public static final String PROPERTY_POPUP_DELAY = "popupDelay";
public static final String PROPERTY_LOCATION = "location";
public static final String PROPERTY_CLIENT_ID = "clientId";
public static final String PROPERTY_POPUP_DELAY = "popupDelay";
public static final String PROPERTY_QUICK_FIX_ENABLED = "quickFixEnabled";
public static final String PROPERTY_AUTO_STARTUP_ENABLED = "autoStartupEnabled";
public static final String PROPERTY_MINIMISE_ON_START_ENABLED = "minimiseOnStartEnabled";

private boolean isFirstTimeUser;

public static Position DEFAULT_POSITION = Position.BOTTOM_RIGHT;
public static String DEFAULT_POPUP_DELAY = Helpers.formatWithOneDecimalPlace(2f);
public static Boolean DEFAULT_QUICK_FIX_ENABLED = Boolean.TRUE;
public static Position DEFAULT_POSITION = Position.BOTTOM_RIGHT;
public static String DEFAULT_POPUP_DELAY = Helpers.formatWithOneDecimalPlace(2f);
public static Boolean DEFAULT_AUTO_STARTUP_ENABLED = Boolean.TRUE;
public static Boolean DEFAULT_MINIMISE_ON_START_ENABLED = Boolean.TRUE;

public AppConfig(String appDataFolderPath) {
File settingsDir = new File(appDataFolderPath);
Expand All @@ -44,8 +49,12 @@ public AppConfig(String appDataFolderPath) {
configFile = new File(appDataFolderPath, configFileName);

if(!configFile.exists()) {
properties.setProperty(PROPERTY_CLIENT_ID, UUID.randomUUID().toString());
updateConfig(DEFAULT_POSITION, DEFAULT_POPUP_DELAY);
updateClientId(UUID.randomUUID().toString());
updateLocation(DEFAULT_POSITION);
updatePopUpDelay(DEFAULT_POPUP_DELAY);
updateQuickFixEnabled(DEFAULT_QUICK_FIX_ENABLED);
updateAutoStartupEnabled(DEFAULT_AUTO_STARTUP_ENABLED);
updateMinimiseOnStart(DEFAULT_MINIMISE_ON_START_ENABLED);
isFirstTimeUser = true;
} else {
try (FileInputStream input = new FileInputStream(configFile)) {
Expand All @@ -58,17 +67,44 @@ public AppConfig(String appDataFolderPath) {

}

public void updateConfig(Position location, String popupDelay) {
properties.setProperty(PROPERTY_LOCATION, location.name());
properties.setProperty(PROPERTY_POPUP_DELAY, popupDelay);

private void persistConfig() {
try (FileOutputStream outputStream = new FileOutputStream(configFile)) {
properties.store(outputStream, "Cap Lock Hook Properties");
} catch (IOException e) {
logger.log(Level.SEVERE, "Failed to store property.", e);
}
}

public void updateLocation(Position location) {
properties.setProperty(PROPERTY_LOCATION, location.name());
persistConfig();
}

private void updateClientId(String clientId) {
properties.setProperty(PROPERTY_CLIENT_ID, clientId);
persistConfig();
}

public void updatePopUpDelay(String popupDelay) {
properties.setProperty(PROPERTY_POPUP_DELAY, popupDelay);
persistConfig();
}

public void updateQuickFixEnabled(boolean enabled) {
properties.setProperty(PROPERTY_QUICK_FIX_ENABLED, String.valueOf(enabled));
persistConfig();
}

public void updateMinimiseOnStart(boolean enabled) {
properties.setProperty(PROPERTY_MINIMISE_ON_START_ENABLED, String.valueOf(enabled));
persistConfig();
}

public void updateAutoStartupEnabled(boolean enabled) {
properties.setProperty(PROPERTY_AUTO_STARTUP_ENABLED, String.valueOf(enabled));
persistConfig();
}

private void keepClientIDAcrossNewConfigVersions() {
if(properties.getProperty(PROPERTY_CLIENT_ID) == null) {
properties.setProperty(PROPERTY_CLIENT_ID, UUID.randomUUID().toString());
Expand All @@ -90,4 +126,16 @@ public String getClientId() {
public String getPopUpDelay() {
return properties.getProperty(PROPERTY_POPUP_DELAY);
}

public Boolean getQuickFixEnabled() {
return Boolean.parseBoolean(properties.getProperty(PROPERTY_QUICK_FIX_ENABLED));
}

public Boolean getMinimiseOnStartEnabled() {
return Boolean.parseBoolean(properties.getProperty(PROPERTY_MINIMISE_ON_START_ENABLED));
}

public Boolean getAutoStartupEnabled() {
return Boolean.parseBoolean(properties.getProperty(PROPERTY_AUTO_STARTUP_ENABLED));
}
}
120 changes: 120 additions & 0 deletions src/main/java/co/uk/bittwisted/views/InfoView.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package co.uk.bittwisted.views;

import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Objects;
import java.util.logging.Level;
import java.util.logging.Logger;

public class InfoView extends JDialog {
private final int WINDOW_WIDTH = 525;
private final int WINDOW_HEIGHT = 400;

private final Logger logger = Logger.getLogger(InfoView.class.getName());

public final Font createdByFont = new Font("Arial", Font.BOLD, 18);
public final Font infoFont = new Font("Arial", Font.BOLD | Font.ITALIC, 24);
public final Font warningFont = new Font("Arial", Font.BOLD | Font.ITALIC, 20);
public final Font titleFont = new Font("Arial", Font.BOLD, 32);

public InfoView(SettingsView settingsView) {
super(settingsView, "Info", true);

setResizable(false);
setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
setDefaultLookAndFeelDecorated(false);
getContentPane().setBackground(Color.WHITE);
setLocation(settingsView.getX(), settingsView.getY() + settingsView.getHeight());

setLayout(new GridLayout(1,1));

JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));

JPanel titlePanel = new JPanel();
titlePanel.setLayout(new BoxLayout(titlePanel, BoxLayout.X_AXIS));

ImageIcon iconImage = new ImageIcon(Objects.requireNonNull(ClassLoader.getSystemResource("icon.png")));
Image originalImage = iconImage.getImage();
// Resize the image
Image resizedImage = originalImage.getScaledInstance(60, 60, Image.SCALE_SMOOTH);
ImageIcon resizedIcon = new ImageIcon(resizedImage);
JLabel iconLabel = new JLabel(resizedIcon);
JLabel titleLabel = new JLabel("CapUp v1.1.0");
titleLabel.setFont(titleFont);

titlePanel.add(iconLabel);
titlePanel.add(Box.createRigidArea(new Dimension(25, 0)));
titlePanel.add(titleLabel);
panel.add(titlePanel);
panel.add(Box.createRigidArea(new Dimension(0, 10)));

JPanel appInfoPanel = new JPanel();
appInfoPanel.setLayout(new BoxLayout(appInfoPanel, BoxLayout.X_AXIS));
appInfoPanel.setBorder(BorderFactory.createEmptyBorder(10, 25, 10, 10));

JLabel appInfoLabel = new JLabel(
"<html><span>This application lets you check the status of your Caps Lock " +
"key without having to glance at your keyboard.</span></html>",
SwingConstants.CENTER);
appInfoLabel.setFont(infoFont);
appInfoPanel.add(appInfoLabel);

panel.add(appInfoPanel);
panel.add(Box.createRigidArea(new Dimension(0, 10)));



JPanel warningInfoPanel = new JPanel();
warningInfoPanel.setLayout(new BoxLayout(warningInfoPanel, BoxLayout.X_AXIS));
warningInfoPanel.setBorder(BorderFactory.createEmptyBorder(0, 25, 10, 10));

JLabel warningInfoLabel = new JLabel(
"<html><span style=\"color:red\">The popup may show the wrong Caps Lock state if another program goes " +
"fullscreen (e.g., a game). Right-click the system tray icon and select " +
"<span style=\"color:black\">'Flip Caps Lock'</span> to fix it.</span></html>",
SwingConstants.CENTER);
warningInfoLabel.setFont(warningFont);
warningInfoPanel.add(warningInfoLabel);

panel.add(warningInfoPanel);
panel.add(Box.createRigidArea(new Dimension(0, 20)));




JPanel devInfoPanel = new JPanel();
devInfoPanel.setLayout(new BoxLayout(devInfoPanel, BoxLayout.X_AXIS));

JLabel devInfoLabel = new JLabel(
"<html><span style=\"color: gray\">- Developed By <a href=\"\">Kyle Bowden</a> -</span></html>",
SwingConstants.CENTER);
devInfoLabel.setCursor(new Cursor(Cursor.HAND_CURSOR));
devInfoLabel.setFont(createdByFont);
devInfoLabel.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
try {
Desktop.getDesktop().browse(new URI("https://www.linkedin.com/in/kyle-bowden-761b366b/"));
} catch (URISyntaxException | IOException ex) {
logger.log(Level.WARNING, "Unable to open link!");
}
}
});
devInfoPanel.add(devInfoLabel);

panel.add(devInfoPanel);
add(panel);
}

public void showInfo() {
setVisible(true);
}
}
Loading

0 comments on commit 683a89f

Please sign in to comment.