UIForPopup.java
package swingtree;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sprouts.Action;
import sprouts.Val;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JSeparator;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import java.awt.Component;
import java.util.Objects;
import java.util.function.Consumer;
/**
* A SwingTree builder node designed for configuring {@link JPopupMenu} instances.
* <p>
* <b>Take a look at the <a href="https://globaltcad.github.io/swing-tree/">living swing-tree documentation</a>
* where you can browse a large collection of examples demonstrating how to use the API of this class or other classes.</b>
*
* @param <P> The type of {@link JPopupMenu} that this {@link UIForPopup} is configuring.
*/
public final class UIForPopup<P extends JPopupMenu> extends UIForAnySwing<UIForPopup<P>, P>
{
private static final Logger log = LoggerFactory.getLogger(UIForPopup.class);
private final BuilderState<P> _state;
UIForPopup( BuilderState<P> state ) {
Objects.requireNonNull(state);
_state = state;
}
@Override
protected BuilderState<P> _state() {
return _state;
}
@Override
protected UIForPopup<P> _newBuilderWithState(BuilderState<P> newState ) {
return new UIForPopup<>(newState);
}
/**
* Determines if the border is painted or not.
*
* @param borderPainted True if the border is painted, false otherwise
* @return This builder node, to qllow for method chaining.
*/
public final UIForPopup<P> borderIsPaintedIf( boolean borderPainted ) {
return _with( thisComponent -> {
thisComponent.setBorderPainted( borderPainted );
})
._this();
}
/**
* Determines if the border is painted or not
* based on the value of the given {@link Val}.
* If the value of the {@link Val} changes, the border will be painted or not.
*
* @param isPainted A {@link Val} which will be used to determine if the border is painted or not.
* @return This builder node, to qllow for method chaining.
*/
public final UIForPopup<P> borderIsPaintedIf( Val<Boolean> isPainted ) {
return _withOnShow( isPainted, (thisComponent,it) -> {
thisComponent.setBorderPainted( it );
})
._with( thisComponent -> {
thisComponent.setBorderPainted( isPainted.get() );
})
._this();
}
/**
* Registers a listener to be notified when the popup is shown.
* This is typically triggered when {@link JPopupMenu#show(Component, int, int)} is called.
*
* @param action The action to be executed when the popup is shown.
* @return this
*/
public UIForPopup<P> onVisible( Action<ComponentDelegate<P, PopupMenuEvent>> action ) {
NullUtil.nullArgCheck(action, "action", Action.class);
return _with( thisComponent -> {
_onPopupOpen(thisComponent,
e -> _runInApp(()->{
try {
action.accept(new ComponentDelegate<>(thisComponent, e));
} catch (Exception ex) {
log.error("Error while executing action on popup open!", ex);
}
})
);
})
._this();
}
private void _onPopupOpen( P thisComponent, Consumer<PopupMenuEvent> consumer ) {
thisComponent.addPopupMenuListener(new PopupMenuListener() {
@Override
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
// This method is called before the popup menu becomes visible.
consumer.accept(e);
}
@Override
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {/* Not relevant here */}
@Override
public void popupMenuCanceled(PopupMenuEvent e) {/* Not relevant here */}
});
}
/**
* Registers a listener to be notified when the popup becomes invisible,
* meaning its popup menu is hidden.
*
* @param action The action to be executed when the popup becomes invisible.
* @return This builder node, to allow for method chaining.
*/
public UIForPopup<P> onInvisible( Action<ComponentDelegate<P, PopupMenuEvent>> action ) {
NullUtil.nullArgCheck(action, "action", Action.class);
return _with( thisComponent -> {
_onPopupClose(thisComponent,
e -> _runInApp(()->{
try {
action.accept(new ComponentDelegate<>((P) thisComponent, e));
} catch (Exception ex) {
log.error("Error while executing action on popup close!", ex);
}
})
);
})
._this();
}
private void _onPopupClose( P thisComponent, Consumer<PopupMenuEvent> consumer ) {
thisComponent.addPopupMenuListener(new PopupMenuListener() {
@Override
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {/* Not relevant here */}
@Override
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
consumer.accept(e); // This method is called before the popup menu becomes invisible
}
@Override
public void popupMenuCanceled(PopupMenuEvent e) {/* Not relevant here */}
});
}
/**
* Registers a listener to be notified when the popup is canceled.
* This is typically triggered when the user clicks outside the popup.
*
* @param action the action to be executed when the popup is canceled.
* @return this
*/
public UIForPopup<P> onCancel( Action<ComponentDelegate<P, PopupMenuEvent>> action ) {
NullUtil.nullArgCheck(action, "action", Action.class);
return _with( thisComponent -> {
_onPopupCancel(thisComponent,
e -> _runInApp(()->{
try {
action.accept(new ComponentDelegate<>(thisComponent, e));
} catch (Exception ex) {
log.error("Error while executing action on popup cancel!", ex);
}
})
);
})
._this();
}
private void _onPopupCancel( P thisComponent, Consumer<PopupMenuEvent> consumer ) {
thisComponent.addPopupMenuListener(new PopupMenuListener() {
@Override
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {/* Not relevant here */}
@Override
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {/* Not relevant here */}
@Override
public void popupMenuCanceled(PopupMenuEvent e) {
consumer.accept(e); // This method is called when the popup menu is canceled
}
});
}
public UIForPopup<P> add(JMenuItem item) { return this.add(UI.of(item)); }
public UIForPopup<P> add(JSeparator separator) { return this.add(UI.of(separator)); }
public UIForPopup<P> add(JPanel panel) { return this.add(UI.of(panel)); }
}