internal-frame-internal-frame-title-pane-internal-frame-title-pane-maximize-button-window-not-focused-state
v3.1.0
Published
One and not the only: InternalFrameInternalFrameTitlePaneInternalFrameTitlePaneMaximizeButtonWindowNotFocusedState
Readme
InternalFrameInternalFrameTitlePaneInternalFrameTitlePaneMaximizeButtonWindowNotFocusedState
A TypeScript/React port of the synthetic Synth State class Nimbus generates for the maximize button of an unfocused JInternalFrame title pane: com.sun.java.swing.plaf.nimbus.InternalFrameInternalFrameTitlePaneInternalFrameTitlePaneMaximizeButtonWindowNotFocusedState.
If you've ever pulled a stack trace out of a Swing app on Nimbus and wondered where the 95-character class names come from: this is one of them. Nimbus emits them from its UI-defaults code generator, one synthetic State subclass per component/region/substate triple. There are hundreds. This package is one.
Background
Nimbus extends SynthLookAndFeel. It doesn't ship hand-written ComponentUI classes with paint() methods like Metal does. It loads a skin and synthesizes styles at class-load time from a model under javax.swing.plaf.nimbus.
Every paintable region gets a SynthStyle. Each style carries an ordered list of State objects, and State is abstract with one interesting method:
public abstract boolean isInState(JComponent c);To paint a region, Nimbus walks that state list, ORs the standard SynthConstants bits (ENABLED, FOCUSED, MOUSE_OVER, and friends), then asks each custom State whether it currently applies. Whichever states match decide which AbstractRegionPainter runs and which UIDefaults values get resolved. The class modeled here is the one that returns true when the maximize button's owning internal frame is not the focus owner. That's the thing that dims the button.
The state identifier Nimbus uses internally:
InternalFrame:InternalFrameTitlePane:"InternalFrameTitlePane.maximizeButton"[WindowNotFocused].stateYes, the quotes and brackets are part of the string. No, I didn't make that up.
What it reproduces
- The relevant slice of the Swing containment hierarchy as plain classes:
JComponent,JInternalFrame,InternalFrameTitlePane, plus theStatebase. - The
isInState(JComponent)predicate. The real generated code climbs two parents from the button to land on theJInternalFrame, then returns!frame.isFrameFocusOwner(). This port does the same two-hopgetParent()walk and the same negated focus check, including the null guards that bail out when the ancestry doesn't match. InternalFrameTitlePaneMaximizeButton, a React component that resolves the matched state's style tokens and renders the button SVG across the focused, hover, and unfocused token sets.
The implementation is wrapped in more indirection than any sane person would write: an AbstractStyleTokenFactory, a builder, a getInstance() singleton, a hand-rolled StringBuilder, and a fake classloader. That's on purpose. Nimbus is itself a factory-and-defaults labyrinth, so the port commits to the bit. Variable names are machine-mangled and there are no comments. This won't change.
Install
npm install internal-frame-internal-frame-title-pane-internal-frame-title-pane-maximize-button-window-not-focused-stateNeeds react and react-dom 18 or newer.
Usage
The component:
import { InternalFrameTitlePaneMaximizeButton } from 'internal-frame-internal-frame-title-pane-internal-frame-title-pane-maximize-button-window-not-focused-state';
function App() {
return (
<div style={{ background: '#7FA1C7', padding: 10 }}>
<InternalFrameTitlePaneMaximizeButton
isFocused={false}
isMaximized={false}
onClick={() => console.log('maximize')}
/>
</div>
);
}Driving the state model directly:
import {
JComponent,
JInternalFrame,
InternalFrameTitlePane,
InternalFrameInternalFrameTitlePaneInternalFrameTitlePaneMaximizeButtonWindowNotFocusedState as UnfocusedMaximizeState,
} from 'internal-frame-internal-frame-title-pane-internal-frame-title-pane-maximize-button-window-not-focused-state';
// frame (unfocused) -> title pane -> maximize button
const frame = new JInternalFrame(null, /* isFocused */ false);
const titlePane = new InternalFrameTitlePane(frame);
const button = new JComponent(titlePane);
const state = UnfocusedMaximizeState.getInstance();
if (state.isInState(button)) {
// resolves the inactive-window token set
console.log(state.getStyleTokens());
}isInState returns false for any component whose two-deep ancestor isn't a JInternalFrame, which is the same guard the generated source uses. It also logs a line to the console every time it's called, because the original does too in spirit and I wasn't going to fight it.
Props
| Prop | Type | Default | Notes |
|---------------|-----------------|---------|-------|
| isFocused | boolean | (required) | When false the unfocused state matches; onClick is gated off |
| isMaximized | boolean | (required) | Switches the maximize vs. restore glyph and the title text |
| isHovered | boolean | false | Forces the MOUSE_OVER token set; also set internally on mouse enter |
| onClick | () => void | (none) | Fires only while isFocused is true |
| style | CSSProperties | (none) | Merged last, so it wins over the resolved tokens |
License
MIT. Keep your class names long.
