Not logged in.  Login/Logout/Register | List snippets | | Create snippet | Upload image | Upload data

298
LINES

< > BotCompany Repo | #1019718 - installInternalFrameSwitcher_v3

JavaX fragment (include) [tags: use-pretranspiled]

Libraryless. Click here for Pure Java version (4190L/28K).

// Adapted from: https://github.com/topalavlad/ComponentSwitcher

sbool installInternalFrameSwitcher_v3_debug;

static SwitchDispatcher installInternalFrameSwitcher_v3(final JDesktopPane desktop) {
  ret swing -> SwitchDispatcher {
    new DesktopListener desktopListener;
    desktop.addContainerListener(desktopListener);
  
    SwitchDispatcher dispatcher = new(new DesktopSwitcher(desktop), desktopListener);
    dispatcher.start();
    ret dispatcher;
  };
}

sclass JInternalFrameCellRenderer extends DefaultListCellRenderer {
  static int marginW = 20, marginH = 3;
  
  public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
    Component c = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
    setText(((JInternalFrame) value).getTitle());
    setMargin(marginW, marginH, this);
    ret c;
  }
}

sclass DesktopListener extends ContainerAdapter implements SwitchableComponentsListener {
  private final List frames = new ArrayList<>();
  private final boolean titleOptional;
  private final InternalFrameAdapter frameActivatedListener = new InternalFrameAdapter() {
      @Override
      public void internalFrameActivated(InternalFrameEvent e) {
          JInternalFrame frame = e.getInternalFrame();
          frames.remove(frame);
          frames.add(0, frame);
      }
  };

  public DesktopListener() {
      this(false);
  }

  public DesktopListener(boolean titleOptional) {
      this.titleOptional = titleOptional;
  }

  @Override
  public void componentAdded(final ContainerEvent e) {
      if (e.getChild() instanceof JInternalFrame) {
          final JInternalFrame frame = (JInternalFrame) e.getChild();
          if (isTitleOptional() || frame.getTitle() != null && !frame.getTitle().trim().isEmpty()) {
              frames.add(frame);
              frame.addInternalFrameListener(frameActivatedListener);
          }
      }
  }

  @Override
  public void componentRemoved(final ContainerEvent e) {
      if (e.getChild() instanceof JInternalFrame) {
          JInternalFrame frame = (JInternalFrame) e.getChild();
          frames.remove(frame);
          frame.removeInternalFrameListener(frameActivatedListener);
      }
  }

  private boolean isTitleOptional() {
      return titleOptional;
  }

  public List<JInternalFrame> getSwitchableComponentList() {
      return frames;
  }
}

sclass DesktopSwitcher {
  private final JDesktopPane jDesktopPane;
  private SwitchDialog switchDialog;
  bool instaSwitch;

  public DesktopSwitcher(JDesktopPane jDesktopPane) {
      this.jDesktopPane = jDesktopPane;
      switchDialog = new SwitchDialog(SwingUtilities.getWindowAncestor(jDesktopPane), new ArrayList<JInternalFrame>());
      initMouseListener();
  }

  public void previous(List<JInternalFrame> switchableComponentList) {
      bool vis = isVisible();
      showMenu(switchableComponentList);
      switchDialog.selectPrevious();
      if (instaSwitch && !vis) switchDialog.selectPrevious();
  }

  public void next(List<JInternalFrame> switchableComponentList) {
      bool vis = isVisible();
      showMenu(switchableComponentList);
      switchDialog.selectNext();
      if (instaSwitch && !vis) switchDialog.selectNext();
  }

  public void dismiss() {
      hideMenu();
  }

  public JDesktopPane getDesktop() {
      return jDesktopPane;
  }

  public boolean hasFocus() {
      return switchDialog.hasFocus() || switchDialog.getList().hasFocus();
  }

  public SwitchDialog getSwitchDialog() {
      return switchDialog;
  }

  private void showMenu(List<JInternalFrame> switchableComponentList) {
      switchDialog.setSwitchableComponents(switchableComponentList);
      if (!switchableComponentList.isEmpty()) {
          switchDialog.pack();
          switchDialog.setLocationRelativeTo(jDesktopPane);
          switchDialog.setVisible(true);
      }
  }
  
  bool isVisible() {
    ret switchDialog != null && switchDialog.isVisible();
  }

  private void hideMenu() {
    if (isVisible()) {
      pcall {
        JInternalFrame selected = switchDialog.getSelected();
        if (selected != null) {
            switchDialog.unselect();
            selected.setSelected(true);
            selected.toFront();
        }
      }
      
      switchDialog.dispose();
    }
  }

  private void initMouseListener() {
      switchDialog.getList().addMouseListener(new MouseAdapter() {
          @Override
          public void mouseReleased(final MouseEvent e) {
              hideMenu();
          }
      });
  }
}

sclass SwitchDispatcher implements KeyEventDispatcher {
  private static final KeyStroke nextStroke = KeyStroke.getKeyStroke("ctrl TAB");
  private static final KeyStroke previousStroke = KeyStroke.getKeyStroke("ctrl shift TAB");

  DesktopSwitcher switcher;
  SwitchableComponentsListener desktopListener;
  F0<Bool> shouldSwitch; // check for additional focused components here
  bool instaSwitch = true; // actually switch to previous frame on first Ctrl+Tab (otherwise Ctrl+Tab just brings up the switcher list)

  public SwitchDispatcher(DesktopSwitcher switcher, SwitchableComponentsListener switchableComponentsListener) {
      this.switcher = switcher;
      this.desktopListener = switchableComponentsListener;
  }

  public void start() {
      KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(this);
  }

  public void stop() {
      KeyboardFocusManager.getCurrentKeyboardFocusManager().removeKeyEventDispatcher(this);
  }

  public boolean dispatchKeyEvent(KeyEvent e) {
      boolean _shouldSwitch = isDesktopPaneFocused(e.getComponent(), switcher.getDesktop())
        || switcher.hasFocus()
        || isTrue(callF(shouldSwitch));
        
      if (!_shouldSwitch) false;
      switcher.instaSwitch = instaSwitch; // copy config

      KeyStroke keyStrokeForEvent = KeyStroke.getKeyStrokeForEvent(e);
      boolean next = nextStroke.equals(keyStrokeForEvent);
      boolean previous = previousStroke.equals(keyStrokeForEvent);
      boolean dismiss = e.getKeyCode() == KeyEvent.VK_CONTROL && !e.isControlDown();

      if (installInternalFrameSwitcher_v3_debug) print("Have keystroke: " + keyStrokeForEvent + ", " + next + "/" + previous + "/" + dismiss);
      
      if (next) {
          switcher.next(desktopListener.getSwitchableComponentList());
      } else if (previous) {
          switcher.previous(desktopListener.getSwitchableComponentList());
      } else if (dismiss) {
          switcher.dismiss();
      }
      return next || previous || dismiss;
  }

  private boolean isDesktopPaneFocused(Component component, JDesktopPane desktop) {
      return getDesktopPaneParent(component) == desktop;
  }

  private JDesktopPane getDesktopPaneParent(Component component) {
      Component parent = component;
      while (parent != null && !(parent instanceof JDesktopPane)) {
          parent = parent.getParent();
      }
      return (JDesktopPane) parent;
  }
}

sinterface SwitchableComponentsListener {
  /**
   * Get a list of the components that the user can switch through.
   * Ideally, this would be sorted by the last focused order.
   *
   * @return - list of switchable components of type JInternalFrame
   */
  List<JInternalFrame> getSwitchableComponentList();
}

sclass SwitchDialog extends JDialog {
  private final JList<JInternalFrame> list;

  public SwitchDialog(Window owner, List<JInternalFrame> titles) {
    super(owner);
    setUndecorated(true);
    list = new JList<>(new Vector<>(titles));
    addFocusListener(focusLostListener);
    onWindowClosed(this, r {
      /* AWT keeps references to stale components around! e.g.
          last in java.util.LinkedList
           keyEventDispatchers in java.awt.DefaultKeyboardFocusManager
            value in sun.awt.MostRecentKeyValue
             shadowMostRecentKeyValue in sun.awt.AppContext
              appContext in java.awt.EventQueue
         So we free the links to JInternalFrames: */
      list.setModel(new DefaultListModel);
    });
    initList();
    setContentPane(jCenteredSection(" Frames ", list));
    pack();
  }

  private void initList() {
    list.setCellRenderer(new JInternalFrameCellRenderer);
    list.setSelectedIndex(0);
    list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
    list.addFocusListener(focusLostListener);
    list.setSelectionModel(new DefaultListSelectionModel() {
      public void removeSelectionInterval(int index0, int index1) {}
      public void addSelectionInterval(int index0, int index1) {
        super.setSelectionInterval(index0, index1);
      }
    });
  }

  public void selectNext() {
    int indexToSelect = list.getSelectedIndex() != list.getModel().getSize() - 1
            ? list.getSelectedIndex() + 1
            : 0;
    setSelectedIndexAndScroll(list, indexToSelect);
  }

  public void selectPrevious() {
    int indexToSelect = list.getSelectedIndex() != 0
            ? list.getSelectedIndex() - 1
            : list.getModel().getSize() - 1;
    setSelectedIndexAndScroll(list, indexToSelect > -1 ? indexToSelect : 0);
  }

  public JInternalFrame getSelected() {
    return list.getSelectedValue();
  }
  
  public void unselect() {
    list.setSelectedIndex(-1);
  }

  public void setSwitchableComponents(List<JInternalFrame> switchableComponents) {
    JInternalFrame oldSelection = list.getSelectedValue();
    list.setListData(new Vector<>(switchableComponents));
    list.setSelectedValue(oldSelection, false);
  }

  public JList<JInternalFrame> getList() {
    return list;
  }

  private final FocusAdapter focusLostListener = new FocusAdapter {
    public void focusLost(FocusEvent e) {
      dispose();
    }
  };
}

Author comment

Began life as a copy of #1016948

download  show line numbers  debug dex  old transpilations   

Travelled to 11 computer(s): bhatertpkbcr, cbybwowwnfue, cfunsshuasjs, gwrvuhgaqvyk, irmadwmeruwu, ishqpsrjomds, mqqgnosmbjvj, pyentgdyhuwx, pzhvpgtvlbxg, tvejysmllsmz, vouqrxazstgt

No comments. add comment

Snippet ID: #1019718
Snippet name: installInternalFrameSwitcher_v3
Eternal ID of this version: #1019718/19
Text MD5: 66ed36a75126910349e8cff717f752c5
Transpilation MD5: cfaaec6151b77f21a70f02310626506b
Author: stefan
Category: javax / gui
Type: JavaX fragment (include)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2020-10-06 14:03:30
Source code size: 10010 bytes / 298 lines
Pitched / IR pitched: No / No
Views / Downloads: 176 / 246
Version history: 18 change(s)
Referenced in: [show references]

Formerly at http://tinybrain.de/1019718 & http://1019718.tinybrain.de