Skip to content

Component Utilities

Samera2022 edited this page Jan 30, 2026 · 1 revision

Component Utilities

Relevant source files

Purpose and Scope

The ComponentUtil class provides cross-cutting functionality for UI component manipulation, theme application, and window sizing management. This utility serves as the central mechanism for applying dark/light mode themes recursively across Swing component hierarchies and managing window dimensions with configurable sizing strategies.

For information about the color schemes and constants used by this utility, see Constants and Resources. For details on the dark/light mode system architecture, see Theming and UI Styling. For window state persistence, see CacheManager.

Sources: src/io/github/samera2022/mouse_macros/util/ComponentUtil.java L1-L196


Class Structure and Dependencies

The ComponentUtil class is a utility class containing only static methods. It has no instance state and serves as a functional module for component manipulation operations.

Core Dependencies

Dependency Purpose Usage
ColorConsts Color scheme definitions Provides 9-color palettes for dark/light modes
ConfigManager Configuration access Reads readjustFrameMode setting for window sizing
CacheManager Runtime state access Reads/writes window size cache via windowSizeMap
CustomScrollBarUI Custom UI component Applied to JScrollBar components during theme application
OtherConsts Mode constants Provides DARK_MODE and LIGHT_MODE constants

Sources: src/io/github/samera2022/mouse_macros/util/ComponentUtil.java L1-L12

src/io/github/samera2022/mouse_macros/util/ComponentUtil.java L46-L151


Theme Application System

Theme Application Entry Point

The public setMode() method serves as the entry point for applying themes to component hierarchies. It accepts a root Component and a mode integer constant (OtherConsts.DARK_MODE or OtherConsts.LIGHT_MODE).


Method Signature: public static void setMode(Component root, int mode)

Sources: src/io/github/samera2022/mouse_macros/util/ComponentUtil.java L46-L66

Color Scheme Structure

Both dark and light mode color schemes contain exactly 9 colors, each serving a specific purpose in the component styling system:

Index Variable Name (in recursive method) Purpose
0 bg General background color
1 fg General foreground (text) color
2 pbg Panel background color
3 pfg Panel foreground color
4 bbg Button background color
5 bfg Button foreground color
6 lbg Label background color (unused in current implementation)
7 lfg Label foreground color (unused in current implementation)
8 caret Caret color for text input components

If a color scheme does not contain exactly 9 colors, the system prints an error message and falls back to dark mode.

Sources: src/io/github/samera2022/mouse_macros/util/ComponentUtil.java L50-L66

src/io/github/samera2022/mouse_macros/util/ComponentUtil.java L69

Recursive Component Styling

The private setComponent() method recursively traverses the component hierarchy, applying appropriate colors based on component type. The method handles the following Swing component types:


Sources: src/io/github/samera2022/mouse_macros/util/ComponentUtil.java L69-L151

UIManager Integration

For certain complex components (JTextField and JComboBox), the method directly manipulates the Swing UIManager to set Look and Feel properties, followed by calling SwingUtilities.updateComponentTreeUI() to refresh the component's appearance.

JTextField Properties Set

Mode Properties Updated
Dark Mode TextField.background, TextField.foreground, TextField.inactiveForeground, TextField.inactiveBackground
Light Mode Same properties with light mode colors

JComboBox Properties Set

Mode Properties Updated
Dark Mode ComboBox.disabledBackground, ComboBox.disabledForeground, ComboBox.background, ComboBox.foreground, ComboBox.selectionBackground, ComboBox.selectionForeground, ComboBox.buttonBackground, ComboBox.buttonShadow
Light Mode Same properties with light mode colors

Sources: src/io/github/samera2022/mouse_macros/util/ComponentUtil.java L90-L139

Custom Scrollbar Styling

When a JScrollBar is encountered, the method applies a CustomScrollBarUI instance constructed with the current mode. This provides custom-styled scrollbars that match the application theme. The UI is updated asynchronously using SwingUtilities.invokeLater() to ensure proper rendering.

Sources: src/io/github/samera2022/mouse_macros/util/ComponentUtil.java L142-L144


Window Sizing and Layout Management

Window Sizing Strategy

The ComponentUtil class provides sophisticated window sizing logic that balances three competing concerns:

  1. Proper Size: Calculated dimensions based on component preferred sizes
  2. Cached Size: Previously stored window dimensions from user resizing
  3. Aspect Ratio: A 3:2 aspect ratio constraint for aesthetic consistency

Sources: src/io/github/samera2022/mouse_macros/util/ComponentUtil.java L173-L195

src/io/github/samera2022/mouse_macros/util/ComponentUtil.java L16-L44

Proper Size Calculation

The getProperSize() method calculates the minimum required window dimensions by:

  1. Measuring Components: Iterating through variable-length arrays of JComponent arrays, summing preferred widths and heights within each array
  2. Finding Maxima: Taking the maximum width and height across all component arrays
  3. Adding Padding: Adding fixed padding (80+20 pixels horizontal, hAdjust+20 pixels vertical)
  4. Fitting to Aspect Ratio: Calling fitSize() to enforce the 3:2 aspect ratio

The hAdjust parameter compensates for extra spacing elements like Box glue components that affect vertical spacing but don't report accurate preferred sizes.

Method Signature: private static int[] getProperSize(int hAdjust, JComponent[]... comps2)

Sources: src/io/github/samera2022/mouse_macros/util/ComponentUtil.java L14-L33

Aspect Ratio Enforcement

The fitSize() method enforces a 3:2 (width:height) aspect ratio by:

  1. Starting with the provided height and calculating targetW = height * 3/2
  2. If calculated width is too small, using the provided width and calculating targetH = width * 2/3
  3. Returning the adjusted dimensions as an int[] array

This ensures all windows maintain consistent proportions regardless of content.

Method Signature: private static int[] fitSize(int width, int height)

Sources: src/io/github/samera2022/mouse_macros/util/ComponentUtil.java L36-L44

Cache Size Parsing

The parseWindowSize() method parses cached size strings that can be in two formats:

  • Comma-separated: "width,height" (e.g., "600,400")
  • Asterisk-separated: "width*height" (e.g., "600*400")

The method returns null if the string is malformed or cannot be parsed.

Method Signature: private static int[] parseWindowSize(String sizeStr)

Sources: src/io/github/samera2022/mouse_macros/util/ComponentUtil.java L153-L171

Readjust Frame Modes

The adjustFrameWithCache() method supports three window sizing strategies controlled by ConfigManager.config.readjustFrameMode:

Mode Constant Behavior
Mixed RFM_MIXED Takes maximum of proper size and cached size for both dimensions, then fits to aspect ratio
Standardized RFM_STANDARDIZED Always uses calculated proper size, ignoring cache
Memorized RFM_MEMORIZED Always uses cached size if available, ignoring calculations

The Mixed mode is particularly useful after language changes, as it allows windows to grow to accommodate longer translated strings while preserving user preferences when the new proper size is smaller than the cached size.

Method Signature: public static void adjustFrameWithCache(Window window, int hAdjust, JComponent[]... comps)

Sources: src/io/github/samera2022/mouse_macros/util/ComponentUtil.java L173-L195


Integration with Application Systems

Integration with Configuration System

ComponentUtil reads from ConfigManager.config.readjustFrameMode to determine window sizing behavior. This setting is user-configurable through the Settings dialog and persists across application sessions.

Configuration Path: ConfigManager.config.readjustFrameMode → one of RFM_MIXED, RFM_STANDARDIZED, or RFM_MEMORIZED

Integration with Cache System

ComponentUtil reads from CacheManager.cache.windowSizeMap, which is a Map<String, String> storing window dimensions keyed by window name. Window names are set via Window.setName() in dialog constructors.

The cache is automatically updated by window listeners when users manually resize windows. ComponentUtil only reads from the cache; it does not write to it.

Cache Path: CacheManager.cache.windowSizeMap.get(window.getName())"width,height" or "width*height"

Integration with Localization System

When the application language changes, all windows need to be resized to accommodate the new text dimensions. The sequence is:

  1. User changes language in Settings dialog
  2. All text components update their content via Localizer
  3. Components report new preferred sizes
  4. Windows call adjustFrameWithCache() to recalculate and apply new dimensions
  5. The readjustFrameMode setting determines how cached sizes and new proper sizes are reconciled

This integration enables smooth language transitions without windows becoming too small for their content or discarding user size preferences unnecessarily.

Sources: src/io/github/samera2022/mouse_macros/util/ComponentUtil.java L173-L195


Usage Patterns

Typical Theme Application Pattern

// In dialog constructor or when theme changes
ComponentUtil.setMode(this, currentMode);

Where currentMode is obtained from:

  • ConfigManager.config.enableDarkMode (if not following system settings)
  • SystemUtil detection (if following system settings)

Typical Window Sizing Pattern

// In dialog constructor, after all components are added
ComponentUtil.adjustFrameWithCache(
    this,                          // the Window
    50,                            // hAdjust value for extra spacing
    new JComponent[]{comp1, comp2}, // first row components
    new JComponent[]{comp3, comp4}  // second row components
);

The variable-length JComponent[]... parameter allows grouping components by logical rows or sections to calculate maximum dimensions accurately.

Sources: src/io/github/samera2022/mouse_macros/util/ComponentUtil.java L173-L195


Technical Notes

Component Type Detection

The recursive styling method uses instanceof checks to determine component types. Components are styled in this order of precedence:

  1. Container components (JScrollPane, JPanel) - recursively processed
  2. Leaf components - directly styled based on type

This ensures parent containers are styled before their children, and the recursion naturally handles arbitrarily nested component hierarchies.

Focus Painting Disabled

For JButton and JRadioButton components, focus painting is explicitly disabled via setFocusPainted(false). This removes the dotted focus rectangle that appears around focused buttons, providing a cleaner visual appearance consistent with modern UI design.

Sources: src/io/github/samera2022/mouse_macros/util/ComponentUtil.java L87

Asynchronous Scrollbar Updates

Scrollbar UI updates are scheduled asynchronously using SwingUtilities.invokeLater(comp::repaint) to ensure proper rendering after the custom UI is applied. This avoids potential threading issues and ensures the scrollbar appears correctly.

Sources: src/io/github/samera2022/mouse_macros/util/ComponentUtil.java L144

Comment Documentation

The source file contains Chinese comments explaining implementation details:

  • Line 14: Explains the hAdjust parameter's purpose - compensating for Box spacers that aren't included in height calculations
  • Line 68: Notes that the method recursively sets styles

Sources: src/io/github/samera2022/mouse_macros/util/ComponentUtil.java L14

src/io/github/samera2022/mouse_macros/util/ComponentUtil.java L68

Clone this wiki locally