Skip to content

Instantly share code, notes, and snippets.

@DeflatedPickle
Last active September 1, 2020 18:58
Show Gist options
  • Save DeflatedPickle/ae7618deda19c2ba7f0334654e864cec to your computer and use it in GitHub Desktop.
Save DeflatedPickle/ae7618deda19c2ba7f0334654e864cec to your computer and use it in GitHub Desktop.
A tabbed panel implementation for LeGUI.
package org.liquidengine.legui.component;
import org.joml.Vector2f;
import org.liquidengine.legui.component.optional.align.HorizontalAlign;
import org.liquidengine.legui.component.optional.align.VerticalAlign;
import org.liquidengine.legui.event.MouseClickEvent;
import org.liquidengine.legui.icon.CharIcon;
import org.liquidengine.legui.icon.Icon;
import org.liquidengine.legui.listener.MouseClickEventListener;
import org.liquidengine.legui.style.Style;
import org.liquidengine.legui.style.color.ColorConstants;
import org.liquidengine.legui.style.flex.FlexStyle;
import org.liquidengine.legui.style.font.FontRegistry;
import org.liquidengine.legui.theme.Themes;
import java.util.HashMap;
// TODO: Style the tabs better
// TODO: Hide the nav bars when there aren't enough tabs to scroll
// TODO: Add tab close buttons
// TODO: Add a new tab button
// TODO: Make the tabs drag-able
// TODO: Add a selectTab method
public class TabbedPanel extends Panel {
private static final int INITIAL_TITLE_HEIGHT = 20;
private static final int NAV_BUTTON_WIDTH = 10;
private static final int ICON_SIZE = INITIAL_TITLE_HEIGHT * 2 / 3;
private static final int TAB_BUTTON_WIDTH = 40;
private static final int CLOSE_BUTTON_WIDTH = 20;
private static final int FORWARDS_ICON_CHAR = 0xF142;
private static final int BACKWARDS_ICON_CHAR = 0xF141;
private static final int CLOSE_ICON_CHAR = 0xF156;
private HashMap<RadioButton, Panel> buttonToPanelHashMap = new HashMap<>();
// A wrapper panel that contains a scroll panel for tabs and buttons
private Panel titleWrapper = new Panel();
// A panel that contains the tabs
private ScrollablePanel tabWrapper = new ScrollablePanel();
// A wrapper for the contents
private Panel contentsWrapper = new Panel();
// Only one tab can be active at a time, so they may as well be radio buttons
private RadioButtonGroup tabButtonGroup = new RadioButtonGroup();
// Navigation buttons
private Button forwardsButton = new Button("");
private Button backwardsButton = new Button("");
private Icon forwardsIcon = new CharIcon(
new Vector2f(ICON_SIZE),
FontRegistry.MATERIAL_DESIGN_ICONS,
(char) FORWARDS_ICON_CHAR,
ColorConstants.black()
);
private Icon backwardsIcon = new CharIcon(
new Vector2f(ICON_SIZE),
FontRegistry.MATERIAL_DESIGN_ICONS,
(char) BACKWARDS_ICON_CHAR,
ColorConstants.black()
);
private Icon closeIcon = new CharIcon(
new Vector2f(ICON_SIZE),
FontRegistry.MATERIAL_DESIGN_ICONS,
(char) CLOSE_ICON_CHAR,
ColorConstants.black()
);
private float scrollStep = 6.0f;
private boolean navButtons = false;
public TabbedPanel() {
this.initialize();
}
private void initialize() {
this.getStyle().setDisplay(Style.DisplayType.FLEX);
this.getStyle().getFlexStyle().setFlexDirection(FlexStyle.FlexDirection.COLUMN);
this.getStyle().setMinWidth(50f);
this.getStyle().setMinHeight(50f);
this.getStyle().setPadding(0f);
this.titleWrapper.setTabFocusable(false);
this.titleWrapper.getStyle().setHeight((float) INITIAL_TITLE_HEIGHT);
this.titleWrapper.getStyle().setMinHeight((float) INITIAL_TITLE_HEIGHT);
this.titleWrapper.getStyle().setMaxHeight((float) INITIAL_TITLE_HEIGHT);
this.add(titleWrapper);
this.add(contentsWrapper);
this.tabWrapper.setAutoResize(true);
this.tabWrapper.setHorizontalScrollBarVisible(false);
this.tabWrapper.setVerticalScrollBarVisible(false);
this.tabWrapper.getContainer().getStyle().setDisplay(Style.DisplayType.FLEX);
this.tabWrapper.getContainer().getStyle().getFlexStyle().setFlexDirection(FlexStyle.FlexDirection.ROW);
this.titleWrapper.add(tabWrapper);
backwardsIcon.setHorizontalAlign(HorizontalAlign.CENTER);
backwardsIcon.setVerticalAlign(VerticalAlign.MIDDLE);
forwardsIcon.setHorizontalAlign(HorizontalAlign.CENTER);
forwardsIcon.setVerticalAlign(VerticalAlign.MIDDLE);
this.setPanelFlex(titleWrapper.getStyle(), FlexStyle.FlexDirection.ROW);
this.setPanelFlex(tabWrapper.getStyle(), FlexStyle.FlexDirection.ROW);
this.setPanelFlex(contentsWrapper.getStyle(), FlexStyle.FlexDirection.COLUMN);
forwardsButton.getStyle().getBackground().setIcon(forwardsIcon);
backwardsButton.getStyle().getBackground().setIcon(backwardsIcon);
forwardsButton.getListenerMap().addListener(MouseClickEvent.class, (MouseClickEventListener) event -> {
this.tabWrapper.getHorizontalScrollBar().setCurValue(
this.tabWrapper.getHorizontalScrollBar().getCurValue() + scrollStep
);
});
backwardsButton.getListenerMap().addListener(MouseClickEvent.class, (MouseClickEventListener) event -> {
this.tabWrapper.getHorizontalScrollBar().setCurValue(
this.tabWrapper.getHorizontalScrollBar().getCurValue() - scrollStep
);
});
this.setChildFlex(forwardsButton.getStyle(), NAV_BUTTON_WIDTH);
this.setChildFlex(backwardsButton.getStyle(), NAV_BUTTON_WIDTH);
this.addNavButtons();
Themes.getDefaultTheme().applyAll(this);
}
private void setPanelFlex(Style style, FlexStyle.FlexDirection direction) {
style.setDisplay(Style.DisplayType.FLEX);
style.setPosition(Style.PositionType.RELATIVE);
style.getFlexStyle().setFlexGrow(1);
style.getFlexStyle().setFlexShrink(1);
style.getFlexStyle().setFlexDirection(direction);
}
private void setChildFlex(Style style, int width) {
style.setPosition(Style.PositionType.RELATIVE);
style.getBackground().setColor(ColorConstants.transparent());
style.setMaxWidth((float) width);
style.setMaxHeight((float) INITIAL_TITLE_HEIGHT);
style.setMinWidth((float) width);
style.setMinHeight((float) INITIAL_TITLE_HEIGHT);
style.setWidth((float) width);
style.setHeight((float) INITIAL_TITLE_HEIGHT);
style.getFlexStyle().setFlexGrow(1);
style.getFlexStyle().setFlexShrink(1);
}
public Panel addTab() {
return this.addTab(new Panel());
}
public Panel addTab(Panel panel) {
return this.addTab(String.valueOf(buttonToPanelHashMap.keySet().size()), panel);
}
public Panel addTab(String name, Panel panel) {
Panel titleWrapper = new Panel();
this.setPanelFlex(titleWrapper.getStyle(), FlexStyle.FlexDirection.ROW);
RadioButton title = new RadioButton(name);
title.setRadioButtonGroup(tabButtonGroup);
tabButtonGroup.setSelection(title, true);
titleWrapper.add(title);
title.getListenerMap().addListener(MouseClickEvent.class, (MouseClickEventListener) event -> {
if (event.getAction() == MouseClickEvent.MouseClickAction.CLICK) {
this.contentsWrapper.clearChildComponents();
this.contentsWrapper.add(panel);
}
});
Button close = new Button("");
close.getStyle().getBackground().setIcon(closeIcon);
titleWrapper.add(close);
close.getListenerMap().addListener(MouseClickEvent.class, (MouseClickEventListener) event -> {
if (event.getAction() == MouseClickEvent.MouseClickAction.CLICK) {
// Doesn't actially work well for this
if (this.tabButtonGroup.getIndex() < buttonToPanelHashMap.keySet().size()) {
System.out.println("Select before");
}
else {
System.out.println("Select end");
}
// We know it's this panel that's shown, remove it
this.contentsWrapper.remove(panel);
this.buttonToPanelHashMap.remove(title);
this.tabWrapper.getContainer().remove(titleWrapper);
}
});
this.setChildFlex(title.getStyle(), TAB_BUTTON_WIDTH);
this.setChildFlex(close.getStyle(), CLOSE_BUTTON_WIDTH);
this.setPanelFlex(panel.getStyle(), FlexStyle.FlexDirection.COLUMN);
this.buttonToPanelHashMap.put(title, panel);
this.tabWrapper.getContainer().add(titleWrapper);
// They have to be hidden/shown because there isn't a selection event causer
this.contentsWrapper.clearChildComponents();
this.contentsWrapper.add(panel);
return panel;
}
public void selectTab(int index) {
}
private void addNavButtons() {
this.titleWrapper.add(backwardsButton);
this.titleWrapper.add(forwardsButton);
}
private void removeNavButtons() {
this.titleWrapper.remove(backwardsButton);
this.titleWrapper.remove(forwardsButton);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment