diff --git a/src/main/java/com/weis/darklaf/decorators/MouseResponder.java b/src/main/java/com/weis/darklaf/decorators/MouseResponder.java new file mode 100644 index 00000000..069002ac --- /dev/null +++ b/src/main/java/com/weis/darklaf/decorators/MouseResponder.java @@ -0,0 +1,52 @@ +package com.weis.darklaf.decorators; + +import org.jetbrains.annotations.Contract; + +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.util.function.Consumer; + +public class MouseResponder implements MouseListener { + + private final Consumer consumer; + + @Contract(pure = true) + public MouseResponder(final Consumer consumer) { + this.consumer = consumer; + } + + @Override + public void mouseClicked(final MouseEvent e) { + if (consumer != null) { + consumer.accept(e); + } + } + + @Override + public void mousePressed(final MouseEvent e) { + if (consumer != null) { + consumer.accept(e); + } + } + + @Override + public void mouseReleased(final MouseEvent e) { + if (consumer != null) { + consumer.accept(e); + } + } + + @Override + public void mouseEntered(final MouseEvent e) { + if (consumer != null) { + consumer.accept(e); + } + } + + @Override + public void mouseExited(final MouseEvent e) { + if (consumer != null) { + consumer.accept(e); + } + } +} diff --git a/src/main/java/com/weis/darklaf/ui/toolbar/DarkToolBarBorder.java b/src/main/java/com/weis/darklaf/ui/toolbar/DarkToolBarBorder.java new file mode 100644 index 00000000..d88a9815 --- /dev/null +++ b/src/main/java/com/weis/darklaf/ui/toolbar/DarkToolBarBorder.java @@ -0,0 +1,72 @@ +package com.weis.darklaf.ui.toolbar; + +import org.jetbrains.annotations.Contract; + +import javax.swing.*; +import javax.swing.border.AbstractBorder; +import javax.swing.plaf.UIResource; +import java.awt.*; + +public class DarkToolBarBorder extends AbstractBorder implements UIResource, SwingConstants { + + public void paintBorder(final Component c, final Graphics g, + final int x, final int y, final int w, final int h) { + g.translate(x, y); + + if (isFloatable(c)) { + if (((JToolBar) c).getOrientation() == HORIZONTAL) { + var icon = getHorizontalGrip(); + int yIcon = h / 2 - icon.getIconHeight() / 2; + if (c.getComponentOrientation().isLeftToRight()) { + icon.paintIcon(c, g, 0, yIcon); + } else { + icon.paintIcon(c, g, w - icon.getIconWidth(), yIcon); + } + } else { + var icon = getVerticalGrip(); + int xIcon = w / 2 - icon.getIconWidth() / 2; + icon.paintIcon(c, g, xIcon, 0); + } + } + g.translate(-x, -y); + } + + @Contract("null -> false") + private boolean isFloatable(final Component c) { + return c instanceof JToolBar && ((JToolBar) c).isFloatable(); + } + + protected Icon getHorizontalGrip() { + return UIManager.getIcon("ToolBar.horizontalGrip.icon"); + } + + protected Icon getVerticalGrip() { + return UIManager.getIcon("ToolBar.verticalGrip.icon"); + } + + public Insets getBorderInsets(final Component c, final Insets newInsets) { + if (isFloatable(c)) { + if (((JToolBar) c).getOrientation() == HORIZONTAL) { + var icon = getHorizontalGrip(); + if (c.getComponentOrientation().isLeftToRight()) { + newInsets.left = icon.getIconWidth(); + } else { + newInsets.right = icon.getIconWidth(); + } + } else { + newInsets.top = getVerticalGrip().getIconHeight(); + } + } + + if (c instanceof JToolBar) { + Insets margin = ((JToolBar) c).getMargin(); + if (margin != null) { + newInsets.left += margin.left; + newInsets.top += margin.top; + newInsets.right += margin.right; + newInsets.bottom += margin.bottom; + } + } + return newInsets; + } +} \ No newline at end of file diff --git a/src/main/java/com/weis/darklaf/ui/toolbar/DarkToolBarUI.java b/src/main/java/com/weis/darklaf/ui/toolbar/DarkToolBarUI.java new file mode 100644 index 00000000..da47b0a7 --- /dev/null +++ b/src/main/java/com/weis/darklaf/ui/toolbar/DarkToolBarUI.java @@ -0,0 +1,309 @@ +package com.weis.darklaf.ui.toolbar; + +import com.weis.darklaf.decorators.MouseResponder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import javax.swing.plaf.ComponentUI; +import java.awt.*; +import java.awt.event.MouseEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +public class DarkToolBarUI extends DarkToolBarUIBridge { + + private static final Robot robot = createRobot(); + + private final DropPreviewPanel previewPanel = new DropPreviewPanel(); + private Timer timer = new Timer(5, e -> dragTo()); + + private Dimension verticalDim = new Dimension(0, 0); + private Dimension horizontalDim = new Dimension(0, 0); + + @NotNull + @Contract(value = "_ -> new", pure = true) + public static ComponentUI createUI(final JComponent c) { + return new DarkToolBarUI(); + } + + @Nullable + private static Robot createRobot() { + try { + return new Robot(); + } catch (AWTException e) { + return null; + } + } + + @Override + public void installUI(final JComponent c) { + super.installUI(c); + previewPanel.setToolBar(toolBar); + dragWindow = createDragWindow(toolBar); + floatingToolBar = createFloatingWindow(toolBar); + } + + @Override + protected void installListeners() { + super.installListeners(); + } + + @Override + protected void uninstallListeners() { + super.uninstallListeners(); + } + + public void paint(@NotNull final Graphics g, @NotNull final JComponent c) { + g.setColor(UIManager.getColor("ToolBar.background")); + g.fillRect(0, 0, c.getWidth(), c.getHeight()); + } + + + @Override + protected void dragTo() { + if (toolBar.isFloatable()) { + Point offset = dragWindow.getOffset(); + Point global = MouseInfo.getPointerInfo().getLocation(); + Point dragPoint = new Point(global.x - offset.x, global.y - offset.y); + ensureDockingSource(); + + Point dockingPosition = dockingSource.getLocationOnScreen(); + Point comparisonPoint = new Point(global.x - dockingPosition.x, global.y - dockingPosition.y); + + if (canDock(dockingSource, comparisonPoint)) { + String constraint = getDockingConstraint(dockingSource, comparisonPoint); + setOrientation(mapConstraintToOrientation(constraint)); + dockingSource.add(previewPanel, constraint); + } else { + setOrientation(mapConstraintToOrientation(constraintBeforeFloating)); + dockingSource.remove(previewPanel); + } + updateDockingSource(); + + dragWindow.setLocation(dragPoint.x, dragPoint.y); + startDrag(); + } + } + + protected void startDrag() { + if (!dragWindow.isVisible()) { + dragWindow.getContentPane().add(toolBar); + updateDockingSource(); + dragWindow.setVisible(true); + //Is needed to intercept ongoing drag. + SwingUtilities.invokeLater(() -> robot.mouseRelease(MouseEvent.BUTTON1_DOWN_MASK)); + + var oldOrientation = toolBar.getOrientation(); + toolBar.setOrientation(SwingConstants.VERTICAL); + verticalDim = toolBar.getPreferredSize(); + toolBar.setOrientation(SwingConstants.HORIZONTAL); + horizontalDim = toolBar.getPreferredSize(); + toolBar.setOrientation(oldOrientation); + timer.start(); + } + } + + protected void stopDrag() { + dragWindow.setVisible(false); + timer.stop(); + } + + private void ensureDockingSource() { + if (dockingSource == null) { + dockingSource = toolBar.getParent(); + } + } + + @Override + public void setFloating(final boolean b, final Point p) { + if (toolBar.isFloatable()) { + boolean visible = false; + + Window ancestor = SwingUtilities.getWindowAncestor(toolBar); + if (ancestor != null) { + visible = ancestor.isVisible(); + } + + if (dragWindow != null) { + stopDrag(); + } + + floating = b; + + if (b) { + constraintBeforeFloating = calculateConstraint(); + if (propertyListener != null) { + UIManager.addPropertyChangeListener(propertyListener); + } + + floatingToolBar.getContentPane().add(toolBar, BorderLayout.CENTER); + if (floatingToolBar instanceof Window) { + ((Window) floatingToolBar).pack(); + ((Window) floatingToolBar).setLocation(floatingX, floatingY); + if (visible) { + ((Window) floatingToolBar).setVisible(true); + } else { + if (ancestor != null) { + ancestor.addWindowListener(new WindowAdapter() { + public void windowOpened(final WindowEvent e) { + ((Window) floatingToolBar).setVisible(true); + } + }); + } + } + } + } else { + if (floatingToolBar instanceof Window) { + ((Window) floatingToolBar).setVisible(false); + } + floatingToolBar.getContentPane().remove(toolBar); + + String constraint = getDockingConstraint(dockingSource, p); + if (constraint == null) { + constraint = BorderLayout.NORTH; + } + setOrientation(mapConstraintToOrientation(constraint)); + dockingSource.add(toolBar, constraint); + updateDockingSource(); + } + } + } + + @Override + protected void floatAt() { + if (toolBar.isFloatable()) { + try { + Point offset = dragWindow.getOffset(); + Point global = MouseInfo.getPointerInfo().getLocation(); + setFloatingLocation(global.x - offset.x, global.y - offset.y); + + if (dockingSource != null) { + Point dockingPosition = dockingSource.getLocationOnScreen(); + Point comparisonPoint = new Point(global.x - dockingPosition.x, + global.y - dockingPosition.y); + if (canDock(dockingSource, comparisonPoint)) { + setFloating(false, comparisonPoint); + } else { + setFloating(true, null); + } + } else { + setFloating(true, null); + } + dockingSource.remove(previewPanel); + } catch (IllegalComponentStateException ignored) { + } + } + } + + protected void updateDockingSource() { + dockingSource.invalidate(); + Container dockingSourceParent = dockingSource.getParent(); + if (dockingSourceParent != null) { dockingSourceParent.validate(); } + dockingSource.repaint(); + } + + protected String getDockingConstraint(final Component c, final Point p) { + if (p == null) return constraintBeforeFloating; + if (c.contains(p)) { + //North + if (p.y < horizontalDim.height && !isBlocked(c, BorderLayout.NORTH)) { + return BorderLayout.NORTH; + } + //South + if (p.y >= c.getHeight() - horizontalDim.height && !isBlocked(c, BorderLayout.SOUTH)) { + return BorderLayout.SOUTH; + } + // East + if (p.x >= c.getWidth() - verticalDim.width && !isBlocked(c, BorderLayout.EAST)) { + return BorderLayout.EAST; + } + // West + if (p.x < verticalDim.width && !isBlocked(c, BorderLayout.WEST)) { + return BorderLayout.WEST; + } + } + return null; + } + + @Override + protected boolean isBlocked(final Component comp, final Object constraint) { + if (comp instanceof Container) { + Container cont = (Container) comp; + LayoutManager lm = cont.getLayout(); + if (lm instanceof BorderLayout) { + BorderLayout blm = (BorderLayout) lm; + Component c = blm.getLayoutComponent(cont, constraint); + return (c != null && c != toolBar && c != previewPanel); + } + } + return false; + } + + protected void setBorderToNonRollover(final Component c) { + } + + @Override + protected void paintDragWindow(@NotNull final Graphics g) { + g.setColor(dragWindow.getBackground()); + int w = dragWindow.getWidth(); + int h = dragWindow.getHeight(); + g.fillRect(0, 0, w, h); + g.setColor(dragWindow.getBorderColor()); + g.fillRect(0, 0, w, 1); + g.fillRect(0, 0, 1, h); + g.fillRect(w - 1, 0, 1, h); + g.fillRect(0, h - 1, w, 1); + } + + @Override + protected DragWindow createDragWindow(final JToolBar toolbar) { + Window frame = null; + if (toolBar != null) { + Container p = toolbar.getParent(); + while (p != null && !(p instanceof Window)) { + p = p.getParent(); + } + if (p != null) { + frame = (Window) p; + } + } + if (floatingToolBar instanceof Window) { frame = (Window) floatingToolBar; } + return new DarkDragWindow(frame); + } + + protected class DarkDragWindow extends DragWindow { + + protected DarkDragWindow(final Window w) { + super(w); + setLayout(new BorderLayout()); + setBackground(toolBar.getBackground()); + var glassPane = new JPanel(); + glassPane.setOpaque(false); + glassPane.addMouseListener(new MouseResponder(e -> { + e.consume(); + if (e.getID() == MouseEvent.MOUSE_RELEASED) { + floatAt(); + } + })); + setGlassPane(glassPane); + glassPane.setVisible(true); + } + + @Override + public void setOrientation(final int o) { + super.setOrientation(o); + var size = toolBar.getPreferredSize(); + size.width += 2; + size.height += 2; + setSize(size); + doLayout(); + } + + @Override + public Point getOffset() { + return new Point(getWidth() / 2, getHeight() / 2); + } + } +} diff --git a/src/main/java/com/weis/darklaf/ui/toolbar/DarkToolBarUIBridge.java b/src/main/java/com/weis/darklaf/ui/toolbar/DarkToolBarUIBridge.java new file mode 100644 index 00000000..07aff1ca --- /dev/null +++ b/src/main/java/com/weis/darklaf/ui/toolbar/DarkToolBarUIBridge.java @@ -0,0 +1,1207 @@ +package com.weis.darklaf.ui.toolbar; + +import com.weis.darklaf.util.DarkUIUtil; +import com.weis.darklaf.util.LazyActionMap; +import sun.swing.DefaultLookup; +import sun.swing.UIAction; + +import javax.swing.*; +import javax.swing.border.Border; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import javax.swing.event.MouseInputListener; +import javax.swing.plaf.ToolBarUI; +import javax.swing.plaf.UIResource; +import javax.swing.plaf.basic.BasicBorders; +import javax.swing.plaf.basic.BasicToolBarUI; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ContainerEvent; +import java.awt.event.ContainerListener; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.awt.event.MouseEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.awt.event.WindowListener; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Objects; + +/** + * A Basic L&F implementation of ToolBarUI. This implementation + * is a "combined" view/controller. + * + * @author Georges Saab + * @author Jeff Shapiro + */ +public abstract class DarkToolBarUIBridge extends ToolBarUI implements SwingConstants { + /** + * The instance of {@code JToolBar}. + */ + protected JToolBar toolBar; + protected boolean floating; + protected int floatingX; + protected int floatingY; + protected RootPaneContainer floatingToolBar; + /** + * The instance of {@code DragWindow}. + */ + protected DragWindow dragWindow; + protected Container dockingSource; + protected int dockingSensitivity = 0; + /** + * The index of the focused component. + */ + protected int focusedCompIndex = -1; + + /** + * The background color of the docking border. + */ + protected Color dockingColor = null; + /** + * The background color of the not docking border. + */ + protected Color floatingColor = null; + /** + * The color of the docking border. + */ + protected Color dockingBorderColor = null; + /** + * The color of the not docking border. + */ + protected Color floatingBorderColor = null; + + /** + * The instance of a {@code MouseInputListener}. + */ + protected MouseInputListener dockingListener; + /** + * The instance of a {@code PropertyChangeListener}. + */ + protected PropertyChangeListener propertyListener; + + /** + * The instance of a {@code ContainerListener}. + */ + protected ContainerListener toolBarContListener; + /** + * The instance of a {@code FocusListener}. + */ + protected FocusListener toolBarFocusListener; + protected Handler handler; + + /** + * The layout before floating. + */ + protected String constraintBeforeFloating = BorderLayout.NORTH; + + // Rollover button implementation. + protected static String IS_ROLLOVER = "JToolBar.isRollover"; + protected static Border rolloverBorder; + protected static Border nonRolloverBorder; + protected static Border nonRolloverToggleBorder; + protected boolean rolloverBorders = false; + + protected HashMap borderTable = new HashMap<>(); + protected Hashtable rolloverTable = new Hashtable<>(); + + + /** + * As of Java 2 platform v1.3 this previously undocumented field is no + * longer used. + * Key bindings are now defined by the LookAndFeel, please refer to + * the key bindings specification for further details. + * + * @deprecated As of Java 2 platform v1.3. + */ + @Deprecated + protected KeyStroke upKey; + /** + * As of Java 2 platform v1.3 this previously undocumented field is no + * longer used. + * Key bindings are now defined by the LookAndFeel, please refer to + * the key bindings specification for further details. + * + * @deprecated As of Java 2 platform v1.3. + */ + @Deprecated + protected KeyStroke downKey; + /** + * As of Java 2 platform v1.3 this previously undocumented field is no + * longer used. + * Key bindings are now defined by the LookAndFeel, please refer to + * the key bindings specification for further details. + * + * @deprecated As of Java 2 platform v1.3. + */ + @Deprecated + protected KeyStroke leftKey; + /** + * As of Java 2 platform v1.3 this previously undocumented field is no + * longer used. + * Key bindings are now defined by the LookAndFeel, please refer to + * the key bindings specification for further details. + * + * @deprecated As of Java 2 platform v1.3. + */ + @Deprecated + protected KeyStroke rightKey; + + + protected static String FOCUSED_COMP_INDEX = "JToolBar.focusedCompIndex"; + + public void installUI(final JComponent c) { + toolBar = (JToolBar) c; + + // Set defaults + installDefaults(); + installComponents(); + installListeners(); + installKeyboardActions(); + + // Initialize instance vars + dockingSensitivity = 0; + floating = false; + floatingX = floatingY = 0; + floatingToolBar = null; + + setOrientation(toolBar.getOrientation()); + LookAndFeel.installProperty(c, "opaque", Boolean.TRUE); + + if (c.getClientProperty(FOCUSED_COMP_INDEX) != null) { + focusedCompIndex = (Integer) (c.getClientProperty(FOCUSED_COMP_INDEX)); + } + } + + public void uninstallUI(final JComponent c) { + + // Clear defaults + uninstallDefaults(); + uninstallComponents(); + uninstallListeners(); + uninstallKeyboardActions(); + + // Clear instance vars + if (isFloating()) { setFloating(false, null); } + + floatingToolBar = null; + dragWindow = null; + dockingSource = null; + + c.putClientProperty(FOCUSED_COMP_INDEX, focusedCompIndex); + } + + /** + * Installs default properties. + */ + protected void installDefaults() { + LookAndFeel.installBorder(toolBar, "ToolBar.border"); + LookAndFeel.installColorsAndFont(toolBar, + "ToolBar.background", + "ToolBar.foreground", + "ToolBar.font"); + // Toolbar specific defaults + if (dockingColor == null || dockingColor instanceof UIResource) { + dockingColor = UIManager.getColor("ToolBar.dockingBackground"); + } + if (floatingColor == null || floatingColor instanceof UIResource) { + floatingColor = UIManager.getColor("ToolBar.floatingBackground"); + } + if (dockingBorderColor == null || + dockingBorderColor instanceof UIResource) { + dockingBorderColor = UIManager.getColor("ToolBar.dockingForeground"); + } + if (floatingBorderColor == null || + floatingBorderColor instanceof UIResource) { + floatingBorderColor = UIManager.getColor("ToolBar.floatingForeground"); + } + + // ToolBar rollover button borders + Object rolloverProp = toolBar.getClientProperty(IS_ROLLOVER); + if (rolloverProp == null) { + rolloverProp = UIManager.get("ToolBar.isRollover"); + } + if (rolloverProp != null) { + rolloverBorders = (Boolean) rolloverProp; + } + + if (rolloverBorder == null) { + rolloverBorder = createRolloverBorder(); + } + if (nonRolloverBorder == null) { + nonRolloverBorder = createNonRolloverBorder(); + } + if (nonRolloverToggleBorder == null) { + nonRolloverToggleBorder = createNonRolloverToggleBorder(); + } + + + setRolloverBorders(isRolloverBorders()); + } + + /** + * Uninstalls default properties. + */ + protected void uninstallDefaults() { + LookAndFeel.uninstallBorder(toolBar); + dockingColor = null; + floatingColor = null; + dockingBorderColor = null; + floatingBorderColor = null; + + installNormalBorders(toolBar); + + rolloverBorder = null; + nonRolloverBorder = null; + nonRolloverToggleBorder = null; + } + + /** + * Registers components. + */ + protected void installComponents() { + } + + /** + * Unregisters components. + */ + protected void uninstallComponents() { + } + + /** + * Registers listeners. + */ + protected void installListeners() { + dockingListener = createDockingListener(); + + if (dockingListener != null) { + toolBar.addMouseMotionListener(dockingListener); + toolBar.addMouseListener(dockingListener); + } + + propertyListener = createPropertyListener(); // added in setFloating + if (propertyListener != null) { + toolBar.addPropertyChangeListener(propertyListener); + } + + toolBarContListener = createToolBarContListener(); + if (toolBarContListener != null) { + toolBar.addContainerListener(toolBarContListener); + } + + toolBarFocusListener = createToolBarFocusListener(); + + if (toolBarFocusListener != null) { + // Put focus listener on all components in toolbar + Component[] components = toolBar.getComponents(); + + for (Component component : components) { + component.addFocusListener(toolBarFocusListener); + } + } + } + + /** + * Unregisters listeners. + */ + protected void uninstallListeners() { + if (dockingListener != null) { + toolBar.removeMouseMotionListener(dockingListener); + toolBar.removeMouseListener(dockingListener); + + dockingListener = null; + } + + if (propertyListener != null) { + toolBar.removePropertyChangeListener(propertyListener); + propertyListener = null; // removed in setFloating + } + + if (toolBarContListener != null) { + toolBar.removeContainerListener(toolBarContListener); + toolBarContListener = null; + } + + if (toolBarFocusListener != null) { + // Remove focus listener from all components in toolbar + Component[] components = toolBar.getComponents(); + + for (Component component : components) { + component.removeFocusListener(toolBarFocusListener); + } + + toolBarFocusListener = null; + } + handler = null; + } + + /** + * Registers keyboard actions. + */ + protected void installKeyboardActions() { + InputMap km = getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); + + SwingUtilities.replaceUIInputMap(toolBar, JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, km); + + LazyActionMap.installLazyActionMap(toolBar, BasicToolBarUI.class, "ToolBar.actionMap"); + } + + InputMap getInputMap(final int condition) { + if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) { + return (InputMap) DefaultLookup.get(toolBar, this, "ToolBar.ancestorInputMap"); + } + return null; + } + + static void loadActionMap(final LazyActionMap map) { + map.put(new Actions(Actions.NAVIGATE_RIGHT)); + map.put(new Actions(Actions.NAVIGATE_LEFT)); + map.put(new Actions(Actions.NAVIGATE_UP)); + map.put(new Actions(Actions.NAVIGATE_DOWN)); + } + + /** + * Unregisters keyboard actions. + */ + protected void uninstallKeyboardActions() { + SwingUtilities.replaceUIActionMap(toolBar, null); + SwingUtilities.replaceUIInputMap(toolBar, JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, null); + } + + /** + * Navigates the focused component. + * + * @param direction a direction + */ + @SuppressWarnings("deprecation") + protected void navigateFocusedComp(final int direction) { + int nComp = toolBar.getComponentCount(); + int j; + switch (direction) { + case EAST: + case SOUTH: + if (focusedCompIndex < 0 || focusedCompIndex >= nComp) { break; } + j = focusedCompIndex + 1; + while (j != focusedCompIndex) { + if (j >= nComp) { j = 0; } + Component comp = toolBar.getComponentAtIndex(j++); + if (comp != null && comp.isFocusTraversable() && comp.isEnabled()) { + comp.requestFocus(); + break; + } + } + break; + case WEST: + case NORTH: + if (focusedCompIndex < 0 || focusedCompIndex >= nComp) { break; } + j = focusedCompIndex - 1; + while (j != focusedCompIndex) { + if (j < 0) { j = nComp - 1; } + Component comp = toolBar.getComponentAtIndex(j--); + + if (comp != null && comp.isFocusTraversable() && comp.isEnabled()) { + comp.requestFocus(); + break; + } + } + break; + default: + break; + } + } + + /** + * Creates a rollover border for toolbar components. The + * rollover border will be installed if rollover borders are + * enabled. + *

+ * Override this method to provide an alternate rollover border. + * + * @return a rollover border for toolbar components + * @since 1.4 + */ + protected Border createRolloverBorder() { + Object border = UIManager.get("ToolBar.rolloverBorder"); + if (border != null) { + return (Border) border; + } + UIDefaults table = UIManager.getLookAndFeelDefaults(); + return new CompoundBorder(new BasicBorders.RolloverButtonBorder( + table.getColor("controlShadow"), + table.getColor("controlDkShadow"), + table.getColor("controlHighlight"), + table.getColor("controlLtHighlight")), + new EmptyBorder(0, 0, 0, 0)); + } + + /** + * Creates the non rollover border for toolbar components. This + * border will be installed as the border for components added + * to the toolbar if rollover borders are not enabled. + *

+ * Override this method to provide an alternate rollover border. + * + * @return the non rollover border for toolbar components + * @since 1.4 + */ + protected Border createNonRolloverBorder() { + Object border = UIManager.get("ToolBar.nonrolloverBorder"); + if (border != null) { + return (Border) border; + } + UIDefaults table = UIManager.getLookAndFeelDefaults(); + return new CompoundBorder(new BasicBorders.ButtonBorder( + table.getColor("Button.shadow"), + table.getColor("Button.darkShadow"), + table.getColor("Button.light"), + table.getColor("Button.highlight")), + new EmptyBorder(0, 0, 0, 0)); + } + + /** + * Creates a non rollover border for Toggle buttons in the toolbar. + */ + protected Border createNonRolloverToggleBorder() { + UIDefaults table = UIManager.getLookAndFeelDefaults(); + return new CompoundBorder(new BasicBorders.RadioButtonBorder( + table.getColor("ToggleButton.shadow"), + table.getColor("ToggleButton.darkShadow"), + table.getColor("ToggleButton.light"), + table.getColor("ToggleButton.highlight")), + new EmptyBorder(0, 0, 0, 0)); + } + + /** + * Creates a window which contains the toolbar after it has been + * dragged out from its container + * + * @param toolbar an instance of {@code JToolBar} + * @return a {@code RootPaneContainer} object, containing the toolbar + * @since 1.4 + */ + protected RootPaneContainer createFloatingWindow(final JToolBar toolbar) { + @SuppressWarnings("serial") + // Superclass is not serializable across versions + class ToolBarDialog extends JDialog { + public ToolBarDialog(final Frame owner, final String title, final boolean modal) { + super(owner, title, modal); + } + + public ToolBarDialog(final Dialog owner, final String title, final boolean modal) { + super(owner, title, modal); + } + + // Override createRootPane() to automatically resize + // the frame when contents change + protected JRootPane createRootPane() { + @SuppressWarnings("serial") // anonymous class + JRootPane rootPane = new JRootPane() { + protected boolean packing = false; + + public void validate() { + super.validate(); + if (!packing) { + packing = true; + pack(); + packing = false; + } + } + }; + rootPane.setOpaque(true); + return rootPane; + } + } + + JDialog dialog; + Window window = SwingUtilities.getWindowAncestor(toolbar); + if (window instanceof Frame) { + dialog = new ToolBarDialog((Frame) window, toolbar.getName(), false); + } else if (window instanceof Dialog) { + dialog = new ToolBarDialog((Dialog) window, toolbar.getName(), false); + } else { + dialog = new ToolBarDialog((Frame) null, toolbar.getName(), false); + } + + dialog.getRootPane().setName("ToolBar.FloatingWindow"); + dialog.setTitle(toolbar.getName()); + dialog.setResizable(false); + WindowListener wl = createFrameListener(); + dialog.addWindowListener(wl); + return dialog; + } + + protected abstract DragWindow createDragWindow(final JToolBar toolbar); + + /** + * Returns a flag to determine whether rollover button borders + * are enabled. + * + * @return true if rollover borders are enabled; false otherwise + * @see #setRolloverBorders + * @since 1.4 + */ + public boolean isRolloverBorders() { + return rolloverBorders; + } + + /** + * Sets the flag for enabling rollover borders on the toolbar and it will + * also install the appropriate border depending on the state of the flag. + * + * @param rollover if true, rollover borders are installed. + * Otherwise non-rollover borders are installed + * @see #isRolloverBorders + * @since 1.4 + */ + public void setRolloverBorders(final boolean rollover) { + rolloverBorders = rollover; + + if (rolloverBorders) { + installRolloverBorders(toolBar); + } else { + installNonRolloverBorders(toolBar); + } + } + + /** + * Installs rollover borders on all the child components of the JComponent. + *

+ * This is a convenience method to call setBorderToRollover + * for each child component. + * + * @param c container which holds the child components (usually a JToolBar) + * @see #setBorderToRollover + * @since 1.4 + */ + protected void installRolloverBorders(final JComponent c) { + // Put rollover borders on buttons + Component[] components = c.getComponents(); + + for (Component component : components) { + if (component instanceof JComponent) { + ((JComponent) component).updateUI(); + setBorderToRollover(component); + } + } + } + + /** + * Installs non-rollover borders on all the child components of the JComponent. + * A non-rollover border is the border that is installed on the child component + * while it is in the toolbar. + *

+ * This is a convenience method to call setBorderToNonRollover + * for each child component. + * + * @param c container which holds the child components (usually a JToolBar) + * @see #setBorderToNonRollover + * @since 1.4 + */ + protected void installNonRolloverBorders(final JComponent c) { + // Put non-rollover borders on buttons. These borders reduce the margin. + Component[] components = c.getComponents(); + + for (Component component : components) { + if (component instanceof JComponent) { + ((JComponent) component).updateUI(); + setBorderToNonRollover(component); + } + } + } + + /** + * Installs normal borders on all the child components of the JComponent. + * A normal border is the original border that was installed on the child + * component before it was added to the toolbar. + *

+ * This is a convenience method to call setBorderNormal + * for each child component. + * + * @param c container which holds the child components (usually a JToolBar) + * @see #setBorderToNonRollover + * @since 1.4 + */ + protected void installNormalBorders(final JComponent c) { + // Put back the normal borders on buttons + Component[] components = c.getComponents(); + + for (Component component : components) { + setBorderToNormal(component); + } + } + + /** + * Sets the border of the component to have a rollover border which + * was created by the {@link #createRolloverBorder} method. + * + * @param c component which will have a rollover border installed + * @see #createRolloverBorder + * @since 1.4 + */ + protected void setBorderToRollover(final Component c) { + if (c instanceof AbstractButton) { + AbstractButton b = (AbstractButton) c; + + Border border = borderTable.get(b); + if (border == null || border instanceof UIResource) { + borderTable.put(b, b.getBorder()); + } + + // Only set the border if its the default border + if (b.getBorder() instanceof UIResource) { + b.setBorder(getRolloverBorder(b)); + } + + rolloverTable.put(b, b.isRolloverEnabled() ? + Boolean.TRUE : Boolean.FALSE); + b.setRolloverEnabled(true); + } + } + + /** + * Returns a rollover border for the button. + * + * @param b the button to calculate the rollover border for + * @return the rollover border + * @see #setBorderToRollover + * @since 1.6 + */ + protected Border getRolloverBorder(final AbstractButton b) { + return rolloverBorder; + } + + /** + * Sets the border of the component to have a non-rollover border which + * was created by the {@link #createNonRolloverBorder} method. + * + * @param c component which will have a non-rollover border installed + * @see #createNonRolloverBorder + * @since 1.4 + */ + protected void setBorderToNonRollover(final Component c) { + if (c instanceof AbstractButton) { + AbstractButton b = (AbstractButton) c; + + Border border = borderTable.get(b); + if (border == null || border instanceof UIResource) { + borderTable.put(b, b.getBorder()); + } + + // Only set the border if its the default border + if (b.getBorder() instanceof UIResource) { + b.setBorder(getNonRolloverBorder(b)); + } + rolloverTable.put(b, b.isRolloverEnabled() ? + Boolean.TRUE : Boolean.FALSE); + b.setRolloverEnabled(false); + } + } + + /** + * Returns a non-rollover border for the button. + * + * @param b the button to calculate the non-rollover border for + * @return the non-rollover border + * @see #setBorderToNonRollover + * @since 1.6 + */ + protected Border getNonRolloverBorder(final AbstractButton b) { + if (b instanceof JToggleButton) { + return nonRolloverToggleBorder; + } else { + return nonRolloverBorder; + } + } + + /** + * Sets the border of the component to have a normal border. + * A normal border is the original border that was installed on the child + * component before it was added to the toolbar. + * + * @param c component which will have a normal border re-installed + * @see #createNonRolloverBorder + * @since 1.4 + */ + protected void setBorderToNormal(final Component c) { + if (c instanceof AbstractButton) { + AbstractButton b = (AbstractButton) c; + + Border border = borderTable.remove(b); + b.setBorder(border); + + Boolean value = rolloverTable.remove(b); + if (value != null) { + b.setRolloverEnabled(value); + } + } + } + + /** + * Sets the floating location. + * + * @param x an X coordinate + * @param y an Y coordinate + */ + public void setFloatingLocation(final int x, final int y) { + floatingX = x; + floatingY = y; + } + + /** + * Returns {@code true} if the {@code JToolBar} is floating + * + * @return {@code true} if the {@code JToolBar} is floating + */ + public boolean isFloating() { + return floating; + } + + public abstract void setFloating(final boolean b, final Point p); + + protected int mapConstraintToOrientation(final String constraint) { + int orientation = toolBar.getOrientation(); + + if (constraint != null) { + if (constraint.equals(BorderLayout.EAST) || constraint.equals(BorderLayout.WEST)) { + orientation = JToolBar.VERTICAL; + } else if (constraint.equals(BorderLayout.NORTH) || constraint.equals(BorderLayout.SOUTH)) { + orientation = JToolBar.HORIZONTAL; + } + } + + return orientation; + } + + /** + * Sets the tool bar's orientation. + * + * @param orientation the new orientation + */ + public void setOrientation(final int orientation) { + toolBar.setOrientation(orientation); + + if (dragWindow != null) { dragWindow.setOrientation(orientation); } + } + + /** + * Gets the color displayed when over a docking area + * + * @return the color displayed when over a docking area + */ + public Color getDockingColor() { + return dockingColor; + } + + /** + * Sets the color displayed when over a docking area + * + * @param c the new color + */ + public void setDockingColor(final Color c) { + this.dockingColor = c; + } + + /** + * Gets the color displayed when over a floating area + * + * @return the color displayed when over a floating area + */ + public Color getFloatingColor() { + return floatingColor; + } + + /** + * Sets the color displayed when over a floating area + * + * @param c the new color + */ + public void setFloatingColor(final Color c) { + this.floatingColor = c; + } + + protected abstract boolean isBlocked(final Component comp, final Object constraint); + + /** + * Returns {@code true} if the {@code JToolBar} can dock at the given position. + * + * @param c a component + * @param p a position + * @return {@code true} if the {@code JToolBar} can dock at the given position + */ + public boolean canDock(final Component c, final Point p) { + return (p != null && getDockingConstraint(c, p) != null); + } + + protected String calculateConstraint() { + String constraint = null; + LayoutManager lm = dockingSource.getLayout(); + if (lm instanceof BorderLayout) { + constraint = (String) ((BorderLayout) lm).getConstraints(toolBar); + } + return (constraint != null) ? constraint : constraintBeforeFloating; + } + + + protected abstract String getDockingConstraint(final Component c, final Point p); + + /** + * The method is used to drag {@code DragWindow} during the {@code JToolBar} + * is being dragged. + * + */ + protected abstract void dragTo(); + + /** + * The method is called at end of dragging to place the frame in either + * its original place or in its floating frame. + * + */ + protected abstract void floatAt(); + + protected Handler getHandler() { + if (handler == null) { + handler = new Handler(); + } + return handler; + } + + /** + * Returns an instance of {@code ContainerListener}. + * + * @return an instance of {@code ContainerListener} + */ + protected ContainerListener createToolBarContListener() { + return getHandler(); + } + + /** + * Returns an instance of {@code FocusListener}. + * + * @return an instance of {@code FocusListener} + */ + protected FocusListener createToolBarFocusListener() { + return getHandler(); + } + + /** + * Returns an instance of {@code PropertyChangeListener}. + * + * @return an instance of {@code PropertyChangeListener} + */ + protected PropertyChangeListener createPropertyListener() { + return getHandler(); + } + + /** + * Returns an instance of {@code MouseInputListener}. + * + * @return an instance of {@code MouseInputListener} + */ + protected MouseInputListener createDockingListener() { + getHandler().tb = toolBar; + return getHandler(); + } + + /** + * Constructs a new instance of {@code WindowListener}. + * + * @return a new instance of {@code WindowListener} + */ + protected WindowListener createFrameListener() { + return new FrameListener(); + } + + /** + * Paints the contents of the window used for dragging. + * + * @param g Graphics to paint to. + * @throws NullPointerException is g is null + * @since 1.5 + */ + protected void paintDragWindow(final Graphics g) { + g.setColor(dragWindow.getBackground()); + int w = dragWindow.getWidth(); + int h = dragWindow.getHeight(); + g.fillRect(0, 0, w, h); + g.setColor(dragWindow.getBorderColor()); + g.drawRect(0, 0, w - 1, h - 1); + } + + + protected static class Actions extends UIAction { + protected static final String NAVIGATE_RIGHT = "navigateRight"; + protected static final String NAVIGATE_LEFT = "navigateLeft"; + protected static final String NAVIGATE_UP = "navigateUp"; + protected static final String NAVIGATE_DOWN = "navigateDown"; + + public Actions(final String name) { + super(name); + } + + public void actionPerformed(final ActionEvent evt) { + String key = getName(); + JToolBar toolBar = (JToolBar) evt.getSource(); + DarkToolBarUIBridge ui = (DarkToolBarUIBridge) DarkUIUtil.getUIOfType(toolBar.getUI(), + DarkToolBarUIBridge.class); + + if (ui == null) return; + if (Objects.equals(NAVIGATE_RIGHT, key)) { + ui.navigateFocusedComp(EAST); + } else if (Objects.equals(NAVIGATE_LEFT, key)) { + ui.navigateFocusedComp(WEST); + } else if (Objects.equals(NAVIGATE_UP, key)) { + ui.navigateFocusedComp(NORTH); + } else if (Objects.equals(NAVIGATE_DOWN, key)) { + ui.navigateFocusedComp(SOUTH); + } + } + } + + + protected class Handler implements ContainerListener, + FocusListener, MouseInputListener, PropertyChangeListener { + + // + // ContainerListener + // + public void componentAdded(final ContainerEvent evt) { + Component c = evt.getChild(); + + if (toolBarFocusListener != null) { + c.addFocusListener(toolBarFocusListener); + } + + if (isRolloverBorders()) { + setBorderToRollover(c); + } else { + setBorderToNonRollover(c); + } + } + + public void componentRemoved(final ContainerEvent evt) { + Component c = evt.getChild(); + + if (toolBarFocusListener != null) { + c.removeFocusListener(toolBarFocusListener); + } + + // Revert the button border + setBorderToNormal(c); + } + + + // + // FocusListener + // + public void focusGained(final FocusEvent evt) { + Component c = evt.getComponent(); + focusedCompIndex = toolBar.getComponentIndex(c); + } + + public void focusLost(final FocusEvent evt) { + } + + + // + // MouseInputListener (DockingListener) + // + JToolBar tb; + boolean isDragging = false; + + public void mousePressed(final MouseEvent evt) { + if (!tb.isEnabled()) { + return; + } + isDragging = false; + } + + public void mouseReleased(final MouseEvent evt) { + if (!tb.isEnabled()) { + return; + } + if (isDragging) { + floatAt(); + } + isDragging = false; + } + + public void mouseDragged(final MouseEvent evt) { + if (!tb.isEnabled()) { + return; + } + isDragging = true; + dragTo(); + } + + public void mouseClicked(final MouseEvent evt) { + } + + public void mouseEntered(final MouseEvent evt) { + } + + public void mouseExited(final MouseEvent evt) { + } + + public void mouseMoved(final MouseEvent evt) { + } + + + // + // PropertyChangeListener + // + public void propertyChange(final PropertyChangeEvent evt) { + String propertyName = evt.getPropertyName(); + if (Objects.equals(propertyName, "lookAndFeel")) { + toolBar.updateUI(); + } else if (Objects.equals(propertyName, "orientation")) { + // Search for JSeparator components and change it's orientation + // to match the toolbar and flip it's orientation. + Component[] components = toolBar.getComponents(); + int orientation = (Integer) evt.getNewValue(); + JToolBar.Separator separator; + + for (Component component : components) { + if (component instanceof JToolBar.Separator) { + separator = (JToolBar.Separator) component; + if ((orientation == JToolBar.HORIZONTAL)) { + separator.setOrientation(JSeparator.VERTICAL); + } else { + separator.setOrientation(JSeparator.HORIZONTAL); + } + Dimension size = separator.getSeparatorSize(); + if (size != null && size.width != size.height) { + // Flip the orientation. + Dimension newSize = new Dimension(size.height, size.width); + separator.setSeparatorSize(newSize); + } + } + } + } else if (Objects.equals(propertyName, IS_ROLLOVER)) { + installNormalBorders(toolBar); + setRolloverBorders((Boolean) evt.getNewValue()); + } + } + } + + /** + * The class listens for window events. + */ + protected class FrameListener extends WindowAdapter { + public void windowClosing(final WindowEvent w) { + if (toolBar.isFloatable()) { + if (dragWindow != null) { dragWindow.setVisible(false); } + floating = false; + if (floatingToolBar == null) { floatingToolBar = createFloatingWindow(toolBar); } + if (floatingToolBar instanceof Window) { ((Window) floatingToolBar).setVisible(false); } + floatingToolBar.getContentPane().remove(toolBar); + String constraint = constraintBeforeFloating; + if (toolBar.getOrientation() == JToolBar.HORIZONTAL) { + if (Objects.equals(constraint, "West") || Objects.equals(constraint, "East")) { + constraint = "North"; + } + } else { + if (Objects.equals(constraint, "North") || Objects.equals(constraint, "South")) { + constraint = "West"; + } + } + if (dockingSource == null) { dockingSource = toolBar.getParent(); } + if (propertyListener != null) { UIManager.removePropertyChangeListener(propertyListener); } + dockingSource.add(toolBar, constraint); + dockingSource.invalidate(); + Container dockingSourceParent = dockingSource.getParent(); + if (dockingSourceParent != null) { dockingSourceParent.validate(); } + dockingSource.repaint(); + } + } + + } + + /** + * The window which appears during dragging the {@code JToolBar}. + */ + @SuppressWarnings("serial") // Same-version serialization only + protected class DragWindow extends JWindow { + Color borderColor = Color.gray; + int orientation = toolBar.getOrientation(); + Point offset; // offset of the mouse cursor inside the DragWindow + + DragWindow(final Window w) { + super(w); + } + + /** + * Sets the orientation. + * + * @param o the new orientation + */ + public void setOrientation(final int o) { + if (isShowing()) { + if (o == this.orientation) { return; } + this.orientation = o; + Dimension size = getSize(); + setSize(new Dimension(size.height, size.width)); + if (offset != null) { + if (toolBar.getComponentOrientation().isLeftToRight()) { + setOffset(new Point(offset.y, offset.x)); + } else if (o == JToolBar.HORIZONTAL) { + setOffset(new Point(size.height - offset.y, offset.x)); + } else { + setOffset(new Point(offset.y, size.width - offset.x)); + } + } + repaint(); + } + } + + /** + * Returns the offset. + * + * @return the offset + */ + public Point getOffset() { + return offset; + } + + /** + * Sets the offset. + * + * @param p the new offset + */ + public void setOffset(final Point p) { + this.offset = p; + } + + /** + * Sets the border color. + * + * @param c the new border color + */ + public void setBorderColor(final Color c) { + if (this.borderColor == c) { return; } + this.borderColor = c; + repaint(); + } + + /** + * Returns the border color. + * + * @return the border color + */ + public Color getBorderColor() { + return this.borderColor; + } + + public void paint(final Graphics g) { + paintDragWindow(g); + // Paint the children + super.paint(g); + } + + public Insets getInsets() { + return new Insets(1, 1, 1, 1); + } + } +} diff --git a/src/main/java/com/weis/darklaf/ui/toolbar/DropPreviewPanel.java b/src/main/java/com/weis/darklaf/ui/toolbar/DropPreviewPanel.java new file mode 100644 index 00000000..db14c074 --- /dev/null +++ b/src/main/java/com/weis/darklaf/ui/toolbar/DropPreviewPanel.java @@ -0,0 +1,50 @@ +package com.weis.darklaf.ui.toolbar; + +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import java.awt.*; + +public class DropPreviewPanel extends JComponent { + + private JToolBar toolBar; + + public void setToolBar(final JToolBar toolBar) { + this.toolBar = toolBar; + } + + @Override + public void paint(@NotNull final Graphics g) { + g.setColor(getBackgroundColor()); + g.fillRect(0, 0, getWidth(), getHeight()); + } + + protected Color getBackgroundColor() { + var useToolbar = Boolean.TRUE.equals(toolBar.getClientProperty("JToolBar.drag.useToolbarBackground")); + if (!useToolbar) { + var c = UIManager.getColor("ToolBar.dropColor"); + if (c == null) { + return toolBar.getBackground(); + } + } + return toolBar.getBackground(); + } + + @Override + public Dimension getPreferredSize() { + if (toolBar != null) { return toolBar.getPreferredSize(); } + return super.getPreferredSize(); + } + + @Override + public Dimension getMinimumSize() { + if (toolBar != null) { return toolBar.getMinimumSize(); } + return super.getMinimumSize(); + } + + @Override + public Dimension getMaximumSize() { + if (toolBar != null) { return toolBar.getMaximumSize(); } + return super.getMinimumSize(); + } +} diff --git a/src/main/java/com/weis/darklaf/ui/tooltip/DarkTooltipBorder.java b/src/main/java/com/weis/darklaf/ui/tooltip/DarkTooltipBorder.java index 4dd014da..c78f4ec4 100644 --- a/src/main/java/com/weis/darklaf/ui/tooltip/DarkTooltipBorder.java +++ b/src/main/java/com/weis/darklaf/ui/tooltip/DarkTooltipBorder.java @@ -23,7 +23,7 @@ public class DarkTooltipBorder implements Border, UIResource { bubbleBorder.setThickness(1); bubbleBorder.setPointerSize(8); bubbleBorder.setPointerWidth(12); - bubbleBorder.setPointerSide(Alignment.EAST); + bubbleBorder.setPointerSide(Alignment.CENTER); } diff --git a/src/main/resources/com/weis/darklaf/darcula.properties b/src/main/resources/com/weis/darklaf/darcula.properties index 0b9eca65..5b986a6e 100644 --- a/src/main/resources/com/weis/darklaf/darcula.properties +++ b/src/main/resources/com/weis/darklaf/darcula.properties @@ -86,15 +86,8 @@ OptionPane.errorIcon = dialog/errorDialog.s ColorChooser.pipette.icon = misc/pipette.svg[aware] ColorChooser.pipetteRollover.icon = misc/pipette_rollover.svg[aware] -#Shadows Experimental -#Shadow.Top.icon = shadow/top.svg(4,7) -#Shadow.TopRight.icon = shadow/topRight.svg(18,14) -#Shadow.Right.icon = shadow/right.svg(11,4) -#Shadow.BottomRight.icon = shadow/bottomRight.svg(18,22) -#Shadow.Bottom.icon = shadow/bottom.svg(4,14) -#Shadow.BottomLeft.icon = shadow/bottomLeft.svg(18,22) -#Shadow.Left.icon = shadow/left.svg(11,4) -#Shadow.TopLeft.icon = shadow/topLeft.svg(18,14) +ToolBar.horizontalGrip.icon = navigation/horizontalGrip.svg[aware](5,20) +ToolBar.verticalGrip.icon = navigation/verticalGrip.svg[aware](20,5) #Darcula Colors darcula.background = 3c3f41 @@ -415,6 +408,11 @@ Tooltip.background = 4B4D4D Tooltip.borderColor = 5B5D5F ToolTip.border = com.weis.darklaf.ui.tooltip.DarkTooltipBorder +#ToolBar +ToolBarUI = com.weis.darklaf.ui.toolbar.DarkToolBarUI +ToolBar.border = com.weis.darklaf.ui.toolbar.DarkToolBarBorder +ToolBar.dropColor = 4B4B4B + #InternalFrame InternalFrameUI = com.bulenkov.darcula.ui.DarculaInternalFrameUI InternalFrame.border = com.bulenkov.darcula.ui.DarculaInternalFrameBorder diff --git a/src/main/resources/com/weis/darklaf/darcula_windows.properties b/src/main/resources/com/weis/darklaf/darcula_windows.properties index b6988f1b..b24b6281 100644 --- a/src/main/resources/com/weis/darklaf/darcula_windows.properties +++ b/src/main/resources/com/weis/darklaf/darcula_windows.properties @@ -60,7 +60,7 @@ darcula.selectionForeground = bbbbbb PopupMenu.border = com.weis.darklaf.ui.menu.DarkPopupMenuBorder #Toolbar -ToolBarUI = com.bulenkov.darcula.ui.DarculaToolBarUI +ToolBarUI = com.weis.darklaf.ui.toolbar.DarkToolBarUI ToolBar.floatingBackground = 3c3f41 ToolBar.floatingForeground = bbbbbb ToolBar.borderColor = 555555 diff --git a/src/main/resources/com/weis/darklaf/icons/dark/navigation/horizontalGrip.svg b/src/main/resources/com/weis/darklaf/icons/dark/navigation/horizontalGrip.svg new file mode 100644 index 00000000..806b1474 --- /dev/null +++ b/src/main/resources/com/weis/darklaf/icons/dark/navigation/horizontalGrip.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/main/resources/com/weis/darklaf/icons/dark/navigation/verticalGrip.svg b/src/main/resources/com/weis/darklaf/icons/dark/navigation/verticalGrip.svg new file mode 100644 index 00000000..b6b2b307 --- /dev/null +++ b/src/main/resources/com/weis/darklaf/icons/dark/navigation/verticalGrip.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/main/resources/com/weis/darklaf/icons/light/navigation/horizontalGrip.svg b/src/main/resources/com/weis/darklaf/icons/light/navigation/horizontalGrip.svg new file mode 100644 index 00000000..e706b715 --- /dev/null +++ b/src/main/resources/com/weis/darklaf/icons/light/navigation/horizontalGrip.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/main/resources/com/weis/darklaf/icons/light/navigation/verticalGrip.svg b/src/main/resources/com/weis/darklaf/icons/light/navigation/verticalGrip.svg new file mode 100644 index 00000000..58223339 --- /dev/null +++ b/src/main/resources/com/weis/darklaf/icons/light/navigation/verticalGrip.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/test/java/ToolBarDemo.java b/src/test/java/ToolBarDemo.java new file mode 100644 index 00000000..47cc34b3 --- /dev/null +++ b/src/test/java/ToolBarDemo.java @@ -0,0 +1,180 @@ +/* + * Copyright (c) 1995, 2008, Oracle and/or its affiliates. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Oracle or the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * ToolBarDemo.java requires the following addditional files: + * images/Back24.gif + * images/Forward24.gif + * images/Up24.gif + */ + +import com.weis.darklaf.LafManager; +import org.jetbrains.annotations.NotNull; + +import javax.swing.JToolBar; +import javax.swing.JButton; +import javax.swing.ImageIcon; + +import javax.swing.JFrame; +import javax.swing.JTextArea; +import javax.swing.JScrollPane; +import javax.swing.JPanel; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; + +import java.net.URL; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +public class ToolBarDemo extends JPanel implements ActionListener { + private JTextArea textArea; + private static final String PREVIOUS = "previous"; + private static final String UP = "up"; + private static final String NEXT = "next"; + + private ToolBarDemo() { + super(new BorderLayout()); + + //Create the toolbar. + JToolBar toolBar = new JToolBar("Still draggable"); + addButtons(toolBar); + + //Create the text area used for output. Request + //enough space for 5 rows and 30 columns. + textArea = new JTextArea(5, 30); + textArea.setEditable(false); + JScrollPane scrollPane = new JScrollPane(textArea); + + //Lay out the main panel. + setPreferredSize(new Dimension(450, 130)); + add(toolBar, BorderLayout.PAGE_START); + add(scrollPane, BorderLayout.CENTER); + } + + private void addButtons(@NotNull final JToolBar toolBar) { + JButton button; + + //first button + button = makeNavigationButton("Back24", PREVIOUS, + "Back to previous something-or-other", "Previous"); + toolBar.add(button); + + //second button + button = makeNavigationButton("Up24", UP, + "Up to something-or-other", "Up"); + toolBar.add(button); + + //third button + button = makeNavigationButton("Forward24", NEXT, + "Forward to something-or-other", "Next"); + toolBar.add(button); + } + + @NotNull + private JButton makeNavigationButton(final String imageName, + final String actionCommand, + final String toolTipText, + final String altText) { + //Look for the image. + String imgLocation = "images/" + imageName + ".gif"; + URL imageURL = ToolBarDemo.class.getResource(imgLocation); + + //Create and initialize the button. + JButton button = new JButton(); + button.setActionCommand(actionCommand); + button.setToolTipText(toolTipText); + button.addActionListener(this); + + if (imageURL != null) { //image found + button.setIcon(new ImageIcon(imageURL, altText)); + } else { //no image found + button.setText(altText); + System.err.println("Resource not found: " + imgLocation); + } + + return button; + } + + public void actionPerformed(@NotNull final ActionEvent e) { + String cmd = e.getActionCommand(); + String description = null; + + // Handle each button. + if (PREVIOUS.equals(cmd)) { //first button clicked + description = "taken you to the previous ."; + } else if (UP.equals(cmd)) { // second button clicked + description = "taken you up one level to ."; + } else if (NEXT.equals(cmd)) { // third button clicked + description = "taken you to the next ."; + } + + displayResult("If this were a real app, it would have " + + description); + } + + private void displayResult(final String actionDescription) { + String newline = "\n"; + textArea.append(actionDescription + newline); + textArea.setCaretPosition(textArea.getDocument().getLength()); + } + + /** + * Create the GUI and show it. For thread safety, + * this method should be invoked from the + * event dispatch thread. + */ + private static void createAndShowGUI() { + //Create and set up the window. + JFrame frame = new JFrame("ToolBarDemo"); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + + //Add content to the window. + frame.add(new ToolBarDemo()); + + //Display the window. + frame.pack(); + frame.setVisible(true); + } + + public static void main(final String[] args) { + //Schedule a job for the event dispatch thread: + //creating and showing this application's GUI. + SwingUtilities.invokeLater(() -> { + LafManager.loadLaf(LafManager.Theme.Dark); + //Turn off metal's use of bold fonts + UIManager.put("swing.boldMetal", Boolean.FALSE); + createAndShowGUI(); + }); + } +} \ No newline at end of file diff --git a/src/test/java/UIDemo.java b/src/test/java/UIDemo.java index ebe5761c..01627ecb 100644 --- a/src/test/java/UIDemo.java +++ b/src/test/java/UIDemo.java @@ -1,7 +1,10 @@ +import com.bulenkov.darcula.ui.DarculaToolBarUI; import com.weis.darklaf.LafManager; import com.weis.darklaf.components.TextFieldHistory; import com.weis.darklaf.icons.IconLoader; import org.jdesktop.swingx.JXStatusBar; +import org.jdesktop.swingx.JXTaskPane; +import org.jdesktop.swingx.JXTaskPaneContainer; import javax.swing.*; import java.awt.*; @@ -21,6 +24,11 @@ public final class UIDemo { LafManager.loadLaf(LafManager.Theme.Dark); JFrame.setDefaultLookAndFeelDecorated(true); + JXTaskPaneContainer taskpanecontainer = new JXTaskPaneContainer(); + JXTaskPane taskpane = new JXTaskPane(); + taskpane.setTitle("My Tasks"); + taskpanecontainer.add(taskpane); + JFrame frame = new JFrame("UIDemo"); Icon folderIcon = IconLoader.get().getUIAwareIcon("files/folder.svg"); @@ -134,7 +142,7 @@ public final class UIDemo { putClientProperty("JButton.buttonType", "square"); }}); }}); - panel.add(new JPanel()); + panel.add(taskpanecontainer); panel.add(new JPanel()); panel.add(new JPanel() {{ setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); diff --git a/src/test/java/UIManagerDefaults.java b/src/test/java/UIManagerDefaults.java index 174b4ebb..98383108 100644 --- a/src/test/java/UIManagerDefaults.java +++ b/src/test/java/UIManagerDefaults.java @@ -45,7 +45,7 @@ public class UIManagerDefaults implements ItemListener { /* * Constructor */ - public UIManagerDefaults() { + private UIManagerDefaults() { items = new TreeMap<>(); models = new HashMap<>(); @@ -83,7 +83,7 @@ public class UIManagerDefaults implements ItemListener { * The content pane should be added to a high level container */ @NotNull - public JComponent getContentPane() { + private JComponent getContentPane() { return contentPane; } @@ -194,8 +194,7 @@ public class UIManagerDefaults implements ItemListener { * The item map will contain items for each component or * items for each attribute type. */ - @NotNull - private TreeMap> buildItemsMap() { + private void buildItemsMap() { final UIDefaults defaults = UIManager.getLookAndFeelDefaults(); // Build of Map of items and a Map of attributes for each item for (final Object key : new HashSet<>(defaults.keySet())) { @@ -211,7 +210,6 @@ public class UIManagerDefaults implements ItemListener { // Add the attribute to the map for this componenent attributeMap.put(key.toString(), value); } - return items; } /* @@ -462,7 +460,7 @@ public class UIManagerDefaults implements ItemListener { try { wrappee.paintIcon(c, g, x, y); } catch (@NotNull final ClassCastException e) { - createStandIn(e, x, y); + createStandIn(e); standIn.paintIcon(c, g, x, y); } } @@ -478,20 +476,20 @@ public class UIManagerDefaults implements ItemListener { return wrappee.getIconHeight(); } - private void createStandIn(@NotNull final ClassCastException e, final int x, final int y) { + private void createStandIn(@NotNull final ClassCastException e) { try { final Class clazz = getClass(e); final JComponent standInComponent = getSubstitute(clazz); - standIn = createImageIcon(standInComponent, x, y); + standIn = createImageIcon(standInComponent); } catch (@NotNull final Exception e1) { // something went wrong - fallback to this painting standIn = this; } } - @Contract("_, _, _ -> new") + @Contract("_ -> new") @NotNull - private Icon createImageIcon(final JComponent standInComponent, final int x, final int y) { + private Icon createImageIcon(final JComponent standInComponent) { final BufferedImage image = new BufferedImage(getIconWidth(), getIconHeight(), BufferedImage.TYPE_INT_ARGB); final Graphics g = image.createGraphics();