From 683a89f989ad36df3de14cb31cf687ccfdba8719 Mon Sep 17 00:00:00 2001 From: Kyle Bowden <1393788+kyle-bowden@users.noreply.github.com> Date: Sun, 6 Oct 2024 01:03:06 +0100 Subject: [PATCH] 003 help view (#3) * 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 --- misc/sourceforge/1_popup_cap_key_state.png | Bin 0 -> 17308 bytes misc/sourceforge/2_popup_settings.png | Bin 0 -> 23996 bytes pom.xml | 9 +- .../java/co/uk/bittwisted/CapsLockHook.java | 31 ++- .../co/uk/bittwisted/config/AppConfig.java | 72 ++++- .../java/co/uk/bittwisted/views/InfoView.java | 120 ++++++++ .../co/uk/bittwisted/views/SettingsView.java | 256 ++++++++---------- .../components/PopupPositionSelector.java | 158 +++++++++++ 8 files changed, 476 insertions(+), 170 deletions(-) create mode 100644 misc/sourceforge/1_popup_cap_key_state.png create mode 100644 misc/sourceforge/2_popup_settings.png create mode 100644 src/main/java/co/uk/bittwisted/views/InfoView.java create mode 100644 src/main/java/co/uk/bittwisted/views/components/PopupPositionSelector.java diff --git a/misc/sourceforge/1_popup_cap_key_state.png b/misc/sourceforge/1_popup_cap_key_state.png new file mode 100644 index 0000000000000000000000000000000000000000..450ba6096dff48dfbd328b5160275be54eb2dea3 GIT binary patch literal 17308 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 literal 0 HcmV?d00001 diff --git a/misc/sourceforge/2_popup_settings.png b/misc/sourceforge/2_popup_settings.png new file mode 100644 index 0000000000000000000000000000000000000000..80dc6f109c438c779dfe0d9b4f599b506b477905 GIT binary patch literal 23996 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 literal 0 HcmV?d00001 diff --git a/pom.xml b/pom.xml index 815c868..f4f1e09 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ co.uk.bittwisted cap-locks-popup - 1.0.2 + 1.1.0 @@ -65,7 +65,9 @@ ${project.version} MSI ${java.home} - BIT TWISTED LTD 2024 + Kyle Bowden + Simple popup for cap locks key + Kyle Bowden true true true @@ -82,8 +84,6 @@ UTF-8 - - @@ -97,5 +97,4 @@ 1.6.5 - diff --git a/src/main/java/co/uk/bittwisted/CapsLockHook.java b/src/main/java/co/uk/bittwisted/CapsLockHook.java index 1a33da9..282506a 100644 --- a/src/main/java/co/uk/bittwisted/CapsLockHook.java +++ b/src/main/java/co/uk/bittwisted/CapsLockHook.java @@ -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; @@ -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; @@ -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"); @@ -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 @@ -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())); @@ -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); @@ -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 -> { @@ -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(); } @@ -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(); } @@ -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 @@ -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(); } } diff --git a/src/main/java/co/uk/bittwisted/config/AppConfig.java b/src/main/java/co/uk/bittwisted/config/AppConfig.java index 8132395..086a5ed 100644 --- a/src/main/java/co/uk/bittwisted/config/AppConfig.java +++ b/src/main/java/co/uk/bittwisted/config/AppConfig.java @@ -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; @@ -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); @@ -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)) { @@ -58,10 +67,7 @@ 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) { @@ -69,6 +75,36 @@ public void updateConfig(Position location, String popupDelay) { } } + 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()); @@ -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)); + } } diff --git a/src/main/java/co/uk/bittwisted/views/InfoView.java b/src/main/java/co/uk/bittwisted/views/InfoView.java new file mode 100644 index 0000000..5b4b32c --- /dev/null +++ b/src/main/java/co/uk/bittwisted/views/InfoView.java @@ -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( + "This application lets you check the status of your Caps Lock " + + "key without having to glance at your keyboard.", + 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( + "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 " + + "'Flip Caps Lock' to fix it.", + 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( + "- Developed By Kyle Bowden -", + 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); + } +} diff --git a/src/main/java/co/uk/bittwisted/views/SettingsView.java b/src/main/java/co/uk/bittwisted/views/SettingsView.java index 814a438..aa1f7d5 100644 --- a/src/main/java/co/uk/bittwisted/views/SettingsView.java +++ b/src/main/java/co/uk/bittwisted/views/SettingsView.java @@ -2,176 +2,154 @@ import co.uk.bittwisted.CapsLockHook; import co.uk.bittwisted.config.AppConfig; -import co.uk.bittwisted.enums.Position; -import co.uk.bittwisted.util.Helpers; -import co.uk.bittwisted.views.components.SelectableDownTriangle; -import co.uk.bittwisted.views.components.SelectableRoundRect; -import co.uk.bittwisted.views.components.SelectableUpTriangle; -import co.uk.bittwisted.views.components.UIComponent; +import co.uk.bittwisted.views.components.*; +import org.slf4j.LoggerFactory; import javax.swing.*; import java.awt.*; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.awt.event.MouseMotionAdapter; -import java.util.Arrays; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.IOException; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.Paths; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; public class SettingsView extends JFrame { - private final int WINDOW_WIDTH = 250; - private final int WINDOW_HEIGHT = 250; - private final SelectableRoundRect[] selectableRects = new SelectableRoundRect[6]; - private final AppConfig appConfig; + private InfoView infoView; - public final Font defaultFont = new Font("Arial Black", Font.PLAIN, 25); - public final Font propertyFont = new Font("Arial Black", Font.PLAIN, 50); - private final CapsLockHook capsLockHook; - - private final Image offScreen; - private final Graphics2D buffer; + private final Logger logger = Logger.getLogger(SettingsView.class.getName()); - private final SelectableUpTriangle upArrow = new SelectableUpTriangle(205, 100); - private final SelectableDownTriangle downArrow = new SelectableDownTriangle(205, 140); + private final int WINDOW_WIDTH = 525; + private final int WINDOW_HEIGHT = 288; - private final GradientPaint defaultBackgroundGradient = new GradientPaint(100, 0, Color.BLACK, WINDOW_WIDTH, WINDOW_HEIGHT, Color.GRAY); + public final Font defaultFont = new Font("Arial", Font.PLAIN, 18); + public final Font infoFont = new Font("Arial", Font.ITALIC, 14); public SettingsView(CapsLockHook clh, AppConfig config) { - this.capsLockHook = clh; this.appConfig = config; - setTitle("Settings"); + setTitle("CapUp v1.1.0"); + setResizable(false); + setAlwaysOnTop(true); setType(Type.UTILITY); setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE); - setSize(250, 250); + setSize(WINDOW_WIDTH, WINDOW_HEIGHT); setLocationRelativeTo(null); setDefaultLookAndFeelDecorated(true); + getContentPane().setBackground(Color.WHITE); - Position position = Position.valueOf(config.getLocation()); - //35 - int TOP_PADDING = 38; - int RECT_WIDTH = 40; - //12 - int OFFSET = 15; - selectableRects[0] = new SelectableRoundRect(OFFSET, TOP_PADDING, RECT_WIDTH, RECT_WIDTH, position == Position.TOP_LEFT, Position.TOP_LEFT); - selectableRects[1] = new SelectableRoundRect(WINDOW_WIDTH - OFFSET - RECT_WIDTH, TOP_PADDING, RECT_WIDTH, RECT_WIDTH, position == Position.TOP_RIGHT, Position.TOP_RIGHT); - selectableRects[2] = new SelectableRoundRect(OFFSET, WINDOW_WIDTH - OFFSET - RECT_WIDTH, RECT_WIDTH, RECT_WIDTH, position == Position.BOTTOM_LEFT, Position.BOTTOM_LEFT); - selectableRects[3] = new SelectableRoundRect(WINDOW_WIDTH - OFFSET - RECT_WIDTH, WINDOW_WIDTH - OFFSET - RECT_WIDTH, RECT_WIDTH, RECT_WIDTH, position == Position.BOTTOM_RIGHT, Position.BOTTOM_RIGHT); - selectableRects[4] = new SelectableRoundRect(WINDOW_WIDTH / 2 - RECT_WIDTH / 2, TOP_PADDING, RECT_WIDTH, RECT_WIDTH, position == Position.TOP_CENTER, Position.TOP_CENTER); - selectableRects[5] = new SelectableRoundRect(WINDOW_WIDTH / 2 - RECT_WIDTH / 2, WINDOW_WIDTH - OFFSET - RECT_WIDTH, RECT_WIDTH, RECT_WIDTH, position == Position.BOTTOM_CENTER, Position.BOTTOM_CENTER); - - addMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent e) { - Arrays.stream(selectableRects).forEach(selectableRect -> { - if(selectableRect.selected && - selectableRect.position == Position.valueOf(config.getLocation())) { - return; - } - - if(selectableRect.shape.contains(new Point(e.getX(), e.getY()))) { - Arrays.stream(selectableRects).forEach(selectableRoundRect -> selectableRoundRect.selected = false); - selectableRect.selected = true; - capsLockHook.updatePopupPosition(selectableRect.position); - } - repaint(); - }); - - upArrow.selected = upArrow.shape.contains(new Point(e.getX(), e.getY())); - if(upArrow.selected) { - System.out.println("Up"); - capsLockHook.updatePopupDelay(true); - } - downArrow.selected = downArrow.shape.contains(new Point(e.getX(), e.getY())); - if(downArrow.selected) { - System.out.println("Down"); - capsLockHook.updatePopupDelay(false); - } - repaint(); - } - }); + setLayout(new GridLayout(1, 2)); - addMouseMotionListener(new MouseMotionAdapter() { - @Override - public void mouseMoved(MouseEvent e) { - super.mouseMoved(e); - Arrays.stream(selectableRects).forEach(selectableRect -> { - selectableRect.focused = selectableRect.shape.contains(new Point(e.getX(), e.getY())); - repaint(); - }); - - upArrow.focused = upArrow.shape.contains(new Point(e.getX(), e.getY())); - downArrow.focused = downArrow.shape.contains(new Point(e.getX(), e.getY())); - } - }); + PopupPositionSelector popupPositionSelector = new PopupPositionSelector(clh, appConfig); + add(popupPositionSelector); - setAlwaysOnTop(true); + JPanel panel = new JPanel(); + panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 0)); + panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); - setVisible(true); - offScreen = createImage(WINDOW_WIDTH, WINDOW_HEIGHT); - buffer = (Graphics2D) offScreen.getGraphics(); - setVisible(false); - } + JCheckBox checkBoxAutoStartup = new JCheckBox("Startup on login"); + JCheckBox checkBoxQuickFixToggle = new JCheckBox("Enable Quick Fix"); + JCheckBox checkBoxMinimiseOnStart = new JCheckBox("Minimise on start"); - @Override - public void paint(Graphics g) { - if (buffer != null) { - buffer.setRenderingHint( - RenderingHints.KEY_TEXT_ANTIALIASING, - RenderingHints.VALUE_TEXT_ANTIALIAS_GASP); - - buffer.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY); - buffer.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - buffer.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY); - buffer.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE); - buffer.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON); - buffer.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); - buffer.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); - buffer.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE); - - buffer.setPaint(defaultBackgroundGradient); - buffer.fillRect(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT); - - Arrays.stream(selectableRects).forEach(selectableRect -> { - if (selectableRect.selected) { - buffer.setColor(Color.WHITE); - buffer.fill(selectableRect.shape); - - buffer.setColor(Color.BLACK); - buffer.setFont(defaultFont); - buffer.drawString("A", selectableRect.shape.getBounds().x + 10, selectableRect.shape.getBounds().y + 28); - } else { - if (selectableRect.focused) { - buffer.setColor(Color.GRAY); - buffer.fill(selectableRect.shape); - } + checkBoxAutoStartup.addItemListener(e -> { + try { + String keyName = "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run"; + String appName = "CapUp"; - buffer.setColor(Color.WHITE); - buffer.draw(selectableRect.shape); + // Get the location of the currently running class or JAR file + URL location = CapsLockHook.class.getProtectionDomain().getCodeSource().getLocation(); + String appPath = ""; + + if(checkBoxAutoStartup.isSelected()) { + appPath = Paths.get(location.toURI()) + .toString() + .replaceAll("app\\\\CapUp-\\d+(\\.\\d+)*\\.jar", "CapUp.exe"); + logger.log(Level.INFO, "Path to running executable: " + appPath, appPath); } - }); - paintWhenComponentFocused(upArrow); - paintWhenComponentFocused(downArrow); + List command = List.of( + "reg", "add", keyName, "/v", appName, "/t", "REG_SZ", "/d", appPath, "/f" + ); - buffer.setColor(Color.GRAY); - buffer.setFont(propertyFont); - float popupDelay = Float.parseFloat(appConfig.getPopUpDelay()); - buffer.drawString(Helpers.formatWithOneDecimalPlace(popupDelay) +"s", 60, 150); + ProcessBuilder processBuilder = new ProcessBuilder(command); + processBuilder.inheritIO(); // This will redirect the command output to the console + Process process = processBuilder.start(); - g.drawImage(offScreen, 0, 0, this); - } - } + int exitCode = process.waitFor(); + if (exitCode == 0) { + logger.log(Level.INFO, "Registry key updated successfully!"); + appConfig.updateAutoStartupEnabled(checkBoxAutoStartup.isSelected()); + } else { + logger.log(Level.SEVERE, "Registry key update failed!", exitCode); + } + } catch (IOException | InterruptedException ex) { + logger.log(Level.SEVERE, "Coule not update registry!", ex); + } catch (URISyntaxException ex) { + logger.log(Level.SEVERE, "Coule not retrieve runtime exe path!", ex); + } + }); + checkBoxQuickFixToggle.addItemListener(e -> + appConfig.updateQuickFixEnabled(checkBoxQuickFixToggle.isSelected())); + checkBoxMinimiseOnStart.addItemListener(e -> + appConfig.updateMinimiseOnStart(checkBoxMinimiseOnStart.isSelected())); + + checkBoxAutoStartup.setFont(defaultFont); + checkBoxAutoStartup.setSelected(appConfig.getAutoStartupEnabled()); + checkBoxMinimiseOnStart.setFont(defaultFont); + checkBoxMinimiseOnStart.setSelected(appConfig.getMinimiseOnStartEnabled()); + checkBoxQuickFixToggle.setFont(defaultFont); + checkBoxQuickFixToggle.setSelected(appConfig.getQuickFixEnabled()); + + panel.add(checkBoxAutoStartup); + panel.add(Box.createRigidArea(new Dimension(0, 0))); + JLabel autoStartupLabel = new JLabel("(Auto start this application after restarts.)"); + autoStartupLabel.setFont(infoFont); + autoStartupLabel.setPreferredSize(new Dimension(50, 50)); + panel.add(autoStartupLabel); + + panel.add(checkBoxMinimiseOnStart); + panel.add(Box.createRigidArea(new Dimension(0, 0))); + JLabel minimiseOnStartupLabel = new JLabel("(Minimise to system tray on launch.)"); + minimiseOnStartupLabel.setFont(infoFont); + minimiseOnStartupLabel.setPreferredSize(new Dimension(50, 50)); + panel.add(minimiseOnStartupLabel); + + panel.add(checkBoxQuickFixToggle); + panel.add(Box.createRigidArea(new Dimension(20, 0))); + JLabel quickFixLabel = new JLabel("(Use Quick Fix with `Left-Control + Q` to convert highlighted uppercase text to lowercase.)\n"); + quickFixLabel.setFont(infoFont); + quickFixLabel.setPreferredSize(new Dimension(50, 50)); + panel.add(quickFixLabel); + + panel.add(Box.createRigidArea(new Dimension(0, 10))); + JButton infoButton = new JButton("Show Extra Info"); + infoButton.setFont(defaultFont); + infoButton.setPreferredSize(new Dimension(50, 50)); + + infoButton.addActionListener(e -> { + if(infoView != null) { + infoView.showInfo(); + } + }); + + panel.add(infoButton); - private void paintWhenComponentFocused(UIComponent component) { - if(component.focused) { - buffer.setColor(Color.GRAY); - buffer.fill(component.shape); - } - buffer.setColor(Color.WHITE); - buffer.draw(component.shape); + add(panel); + + boolean shouldMinimiseOnStartOnlyIfNotFirstTimeUser = + !appConfig.getMinimiseOnStartEnabled() && !appConfig.isFirstTimeUser(); + setVisible(shouldMinimiseOnStartOnlyIfNotFirstTimeUser); } public void showSettings() { setVisible(true); } + + public void setInfoView(InfoView infoView) { + this.infoView = infoView; + } } diff --git a/src/main/java/co/uk/bittwisted/views/components/PopupPositionSelector.java b/src/main/java/co/uk/bittwisted/views/components/PopupPositionSelector.java new file mode 100644 index 0000000..82711ed --- /dev/null +++ b/src/main/java/co/uk/bittwisted/views/components/PopupPositionSelector.java @@ -0,0 +1,158 @@ +package co.uk.bittwisted.views.components; + +import co.uk.bittwisted.CapsLockHook; +import co.uk.bittwisted.config.AppConfig; +import co.uk.bittwisted.enums.Position; +import co.uk.bittwisted.util.Helpers; + +import javax.swing.*; +import javax.swing.border.BevelBorder; +import java.awt.*; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseMotionAdapter; +import java.util.Arrays; + +public class PopupPositionSelector extends JComponent { + private final AppConfig appConfig; + + private final int COMPONENT_WIDTH = 250; + private final int COMPONENT_HEIGHT = 250; + private final SelectableRoundRect[] selectablePositionRects = new SelectableRoundRect[6]; + + private Graphics2D buffer; + + private final SelectableUpTriangle upArrow = new SelectableUpTriangle(205, 100); + private final SelectableDownTriangle downArrow = new SelectableDownTriangle(205, 140); + + public final Font defaultFont = new Font("Arial Black", Font.PLAIN, 25); + public final Font propertyFont = new Font("Arial Black", Font.PLAIN, 50); + private final GradientPaint defaultBackgroundGradient = + new GradientPaint(0, COMPONENT_HEIGHT / 2, Color.BLACK, COMPONENT_WIDTH, COMPONENT_HEIGHT / 2, Color.GRAY); + + public PopupPositionSelector(CapsLockHook clh, AppConfig appConfig) { + this.appConfig = appConfig; + + setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED)); + + Position position = Position.valueOf(appConfig.getLocation()); + + //35 + int TOP_PADDING = 20; + int RECT_WIDTH = 40; + //12 + int OFFSET = 15; + selectablePositionRects[0] = new SelectableRoundRect(OFFSET, TOP_PADDING, RECT_WIDTH, RECT_WIDTH, position == Position.TOP_LEFT, Position.TOP_LEFT); + selectablePositionRects[1] = new SelectableRoundRect(COMPONENT_WIDTH - OFFSET - RECT_WIDTH, TOP_PADDING, RECT_WIDTH, RECT_WIDTH, position == Position.TOP_RIGHT, Position.TOP_RIGHT); + selectablePositionRects[2] = new SelectableRoundRect(OFFSET, COMPONENT_WIDTH - OFFSET - RECT_WIDTH, RECT_WIDTH, RECT_WIDTH, position == Position.BOTTOM_LEFT, Position.BOTTOM_LEFT); + selectablePositionRects[3] = new SelectableRoundRect(COMPONENT_WIDTH - OFFSET - RECT_WIDTH, COMPONENT_WIDTH - OFFSET - RECT_WIDTH, RECT_WIDTH, RECT_WIDTH, position == Position.BOTTOM_RIGHT, Position.BOTTOM_RIGHT); + selectablePositionRects[4] = new SelectableRoundRect(COMPONENT_WIDTH / 2 - RECT_WIDTH / 2, TOP_PADDING, RECT_WIDTH, RECT_WIDTH, position == Position.TOP_CENTER, Position.TOP_CENTER); + selectablePositionRects[5] = new SelectableRoundRect(COMPONENT_WIDTH / 2 - RECT_WIDTH / 2, COMPONENT_WIDTH - OFFSET - RECT_WIDTH, RECT_WIDTH, RECT_WIDTH, position == Position.BOTTOM_CENTER, Position.BOTTOM_CENTER); + + addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + Arrays.stream(selectablePositionRects).forEach(selectableRect -> { + if(selectableRect.selected && + selectableRect.position == Position.valueOf(appConfig.getLocation())) { + return; + } + + if(selectableRect.shape.contains(new Point(e.getX(), e.getY()))) { + Arrays.stream(selectablePositionRects).forEach(selectableRoundRect -> selectableRoundRect.selected = false); + selectableRect.selected = true; + clh.updatePopupPosition(selectableRect.position); + } + repaint(); + }); + + upArrow.selected = upArrow.shape.contains(new Point(e.getX(), e.getY())); + if(upArrow.selected) { + clh.updatePopupDelay(true); + } + downArrow.selected = downArrow.shape.contains(new Point(e.getX(), e.getY())); + if(downArrow.selected) { + clh.updatePopupDelay(false); + } + + repaint(); + } + }); + + addMouseMotionListener(new MouseMotionAdapter() { + @Override + public void mouseMoved(MouseEvent e) { + super.mouseMoved(e); + Arrays.stream(selectablePositionRects).forEach(selectableRect -> { + selectableRect.focused = selectableRect.shape.contains(new Point(e.getX(), e.getY())); + repaint(); + }); + + upArrow.focused = upArrow.shape.contains(new Point(e.getX(), e.getY())); + downArrow.focused = downArrow.shape.contains(new Point(e.getX(), e.getY())); + } + }); + } + + @Override + protected void paintComponent(Graphics g) { + super.paintComponent(g); + + buffer = (Graphics2D) g; + if (buffer != null) { + buffer.setRenderingHint( + RenderingHints.KEY_TEXT_ANTIALIASING, + RenderingHints.VALUE_TEXT_ANTIALIAS_GASP); + + buffer.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY); + buffer.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + buffer.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY); + buffer.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE); + buffer.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON); + buffer.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); + buffer.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + buffer.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE); + + buffer.setPaint(defaultBackgroundGradient); + buffer.fillRect(0, 0, COMPONENT_WIDTH, COMPONENT_HEIGHT); + + buffer.setColor(Color.WHITE); + + Arrays.stream(selectablePositionRects).forEach(selectableRect -> { + if (selectableRect.selected) { + buffer.setColor(Color.WHITE); + buffer.fill(selectableRect.shape); + + buffer.setColor(Color.BLACK); + buffer.setFont(defaultFont); + buffer.drawString("A", selectableRect.shape.getBounds().x + 10, selectableRect.shape.getBounds().y + 28); + } else { + if (selectableRect.focused) { + buffer.setColor(Color.GRAY); + buffer.fill(selectableRect.shape); + } + + buffer.setColor(Color.WHITE); + buffer.draw(selectableRect.shape); + } + }); + + paintWhenComponentFocused(upArrow); + paintWhenComponentFocused(downArrow); + + buffer.setColor(Color.GRAY); + buffer.setFont(propertyFont); + float popupDelay = Float.parseFloat(appConfig.getPopUpDelay()); + buffer.drawString(Helpers.formatWithOneDecimalPlace(popupDelay) +"s", 60, 150); + } + } + + private void paintWhenComponentFocused(UIComponent component) { + if(component.focused) { + buffer.setColor(Color.GRAY); + buffer.fill(component.shape); + } + buffer.setColor(Color.WHITE); + buffer.draw(component.shape); + } +}