0

I am using a JTabbedPane and I set all of its tabs to be disabled. At the same time I need to keep the tabs look just like when they are active. I don't need them to be grayed in colors. I move between tabs using next and previous buttons. The only purpose of the tabs is just to be a visual indicator.

I tried this code answer. It works, but how to modify it correctly so that disabled tabs look exactly like when they are active tabs.

I mean the default look of swing active tab components:

By active tab I mean just enabled no matter whether it is selected or not.

Three Swing tabs with tab2 selected

Community
  • 1
  • 1
Saleh Feek
  • 2,048
  • 8
  • 34
  • 56
  • 1
    Won't this be confusing to the user, who may wonder why buttons that _appear_ enabled don't work? Would it help to expand on the underlying [*problem*](http://meta.stackoverflow.com/q/66377/163188)? – trashgod Aug 24 '16 at 01:30
  • @trashgod **(1)** It will not confuse users because the app is for solving a specific math question *known to its audience*. The user feeds the fields in `tab1` then press next. `tab2` will use the data from `tab1` to prepare some intermediate results. The user feeds the required fields of `tab2`.. and so on. The last tab will contain the final result. **(2)** It is required to fill the fields of one tab before moving to the next. **(3)** It is beautiful to see visual indicator to show which step user currently in. **(4)** No other alternative known for me to use as visual indicator. – Saleh Feek Aug 24 '16 at 02:28
  • 2
    Ah, thank your for elaborating; for strict modal navigation of this sort, I might consider `CardLayout`, for [example](http://stackoverflow.com/a/5655843/230513), disabling buttons until the current panel is complete. – trashgod Aug 24 '16 at 02:35

1 Answers1

2

I set all of its tabs to be disabled. At the same time I need to keep the tabs look just like when they are active. I don't need them to be grayed in colors.

Maybe you can use a JLayer or JTabbedPane#setTabComponentAt(Component).

(4) No other alternative known for me to use as visual indicator.

How about using the JSlider.

import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.plaf.*;

public class TabbedPaneTest {
  private int prev = 0;
  public JComponent makeUI() {
    JPanel p = new JPanel(new GridLayout(0, 1, 0, 5));
    p.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));

    JTabbedPane tabbedPane0 = new SmallTabbedPane();
    JTabbedPane tabbedPane1 = new SmallTabbedPane();
    JTabbedPane tabbedPane2 = new SmallTabbedPane() {
      @Override public void addTab(String title, Component content) {
        super.addTab(title, content);
        int currentIndex = getTabCount() - 1;
        setTabComponentAt(currentIndex, new JLabel(title));
        setComponentAt(currentIndex, content);
      }
    };

    initTabbedPane(tabbedPane0);
    p.add(new JLayer<JComponent>(tabbedPane0, new DisableInputLayerUI()));

    for (JTabbedPane tabs : Arrays.asList(tabbedPane1, tabbedPane2)) {
      initTabbedPane(tabs);
      tabs.setEnabledAt(0, false);
      tabs.setEnabledAt(1, false);
      tabs.setEnabledAt(2, false);
      tabs.setEnabledAt(3, false);
      p.add(tabs);
    }

    Dictionary<Integer, Component> labelTable = new Hashtable<>();
    int c = 0;
    for (String s : Arrays.asList("tab1", "tab2", "tab2", "tab4")) {
      JLabel l = new JLabel(s);
      if (c == 0) {
        resetForeground(l, Color.RED, Color.WHITE);
      }
      labelTable.put(c++, l);
    }
    JSlider slider = new JSlider(0, 3, 0);
    //slider.setEnabled(false);
    slider.setSnapToTicks(true);
    slider.setPaintTicks(true);
    slider.setLabelTable(labelTable);
    slider.setPaintLabels(true);
    slider.getModel().addChangeListener(e -> {
      BoundedRangeModel m = (BoundedRangeModel) e.getSource();
      int i = m.getValue();
      if (i != prev) {
        Dictionary dictionary = slider.getLabelTable();
        resetForeground(dictionary.get(i), Color.RED, Color.WHITE);
        resetForeground(dictionary.get(prev), Color.BLACK, null);
        slider.repaint();
        prev = i;
      }
    });
    p.add(new JLayer<JComponent>(slider, new DisableInputLayerUI()));

    JButton button = new JButton("next");
    button.addActionListener(e -> {
      int i = tabbedPane1.getSelectedIndex() + 1;
      int next = i >= tabbedPane1.getTabCount() ? 0 : i;
      tabbedPane0.setSelectedIndex(next);
      tabbedPane1.setSelectedIndex(next);
      tabbedPane2.setSelectedIndex(next);
      slider.setValue(next);
    });

    JPanel pp = new JPanel(new BorderLayout());
    pp.add(p, BorderLayout.NORTH);
    pp.add(button, BorderLayout.SOUTH);
    return pp;
  }
  private static void initTabbedPane(JTabbedPane tabs) {
    tabs.addTab("tab1", new JLabel());
    tabs.addTab("tab2", new JLabel());
    tabs.addTab("tab3", new JLabel());
    tabs.addTab("tab4", new JLabel());
  }
  private static void resetForeground(Object o, Color fc, Color bc) {
    if (o instanceof JComponent) {
      JComponent c = (JComponent) o;
      c.setForeground(fc);
        if (bc != null) {
            c.setOpaque(true);
            c.setBackground(bc);
        } else {
            c.setOpaque(false);
        }
    }
  }
  public static void main(String... args) {
    EventQueue.invokeLater(() -> {
      JFrame f = new JFrame();
      f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
      f.getContentPane().add(new TabbedPaneTest().makeUI());
      f.setSize(320, 320);
      f.setLocationRelativeTo(null);
      f.setVisible(true);
    });
  }
}

class SmallTabbedPane extends JTabbedPane {
  @Override public Dimension getPreferredSize() {
    Dimension d = super.getPreferredSize();
    d.height = 24;
    return d;
  }
}

class DisableInputLayerUI extends LayerUI<JComponent> {
  @Override public void installUI(JComponent c) {
    super.installUI(c);
    ((JLayer) c).setLayerEventMask(
      AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK
      | AWTEvent.MOUSE_WHEEL_EVENT_MASK | AWTEvent.KEY_EVENT_MASK
      | AWTEvent.FOCUS_EVENT_MASK | AWTEvent.COMPONENT_EVENT_MASK);
  }
  @Override public void uninstallUI(JComponent c) {
    ((JLayer) c).setLayerEventMask(0);
    super.uninstallUI(c);
  }
  @Override public void eventDispatched(AWTEvent e, JLayer<? extends JComponent> l) {
    if (e instanceof InputEvent) {
      ((InputEvent) e).consume();
    }
  }
}
aterai
  • 9,658
  • 4
  • 35
  • 44
  • Thanks.. I try the code it works OK. But could you please shrink the code to just use a single `JTabbedPane` (one of the two that looks enabled), because that will make the code easier to be understood by me ^_^ – Saleh Feek Aug 24 '16 at 19:24
  • I have achieved myself what I had asked for in the previous comment. I appreciate your effort in the whole code. The only part required for my situation is just the extended anonymous class used by the variable `tabbedPane2`. And it seems simpler than the approach achieved with `tabbedPane0` which makes use of `JLayer` which I have just been introduced to from this answer. Thanks a lot ^_^ – Saleh Feek Aug 24 '16 at 22:53