Browse Source

Added DnD support to JTabFrame.

pull/15/head
weisj 5 years ago
parent
commit
ae53ad6e4e
  1. 10
      src/main/java/com/weis/darklaf/components/ScrollPopupMenu.java
  2. 100
      src/main/java/com/weis/darklaf/components/TextFieldHistory.java
  3. 80
      src/main/java/com/weis/darklaf/components/tabframe/JTabFrame.java
  4. 8
      src/main/java/com/weis/darklaf/components/tabframe/PanelPopup.java
  5. 2
      src/main/java/com/weis/darklaf/components/tabframe/TabFrameContentPane.java
  6. 30
      src/main/java/com/weis/darklaf/components/tabframe/TabFramePopup.java
  7. 4
      src/main/java/com/weis/darklaf/components/tabframe/TabFrameTab.java
  8. 8
      src/main/java/com/weis/darklaf/components/tabframe/TabFrameTabContainer.java
  9. 12
      src/main/java/com/weis/darklaf/components/tabframe/TabFrameTabLabel.java
  10. 29
      src/main/java/com/weis/darklaf/components/tabframe/TabFrameUI.java
  11. 2
      src/main/java/com/weis/darklaf/components/tabframe/TabbedPopup.java
  12. 40
      src/main/java/com/weis/darklaf/components/text/SearchEvent.java
  13. 31
      src/main/java/com/weis/darklaf/components/text/SearchListener.java
  14. 119
      src/main/java/com/weis/darklaf/components/text/SearchTextField.java
  15. 174
      src/main/java/com/weis/darklaf/components/text/SearchTextFieldWithHistory.java
  16. 157
      src/main/java/com/weis/darklaf/components/text/TextFieldHistoryPopup.java
  17. 1
      src/main/java/com/weis/darklaf/components/tooltip/ToolTipContext.java
  18. 2
      src/main/java/com/weis/darklaf/components/uiresource/Insets2D.java
  19. 2
      src/main/java/com/weis/darklaf/components/uiresource/JLabelUIResource.java
  20. 2
      src/main/java/com/weis/darklaf/components/uiresource/JPanelUIResource.java
  21. 2
      src/main/java/com/weis/darklaf/components/uiresource/UIResourceWrapper.java
  22. 2
      src/main/java/com/weis/darklaf/ui/colorchooser/DarkColorChooserPanel.java
  23. 13
      src/main/java/com/weis/darklaf/ui/tabbedpane/DarkTabbedPaneUI.java
  24. 4
      src/main/java/com/weis/darklaf/ui/tabbedpane/TabbedPaneTransferHandler.java
  25. 24
      src/main/java/com/weis/darklaf/ui/tabframe/DarkPanelPopupUI.java
  26. 3
      src/main/java/com/weis/darklaf/ui/tabframe/DarkTabFrameComponentPopupMenu.java
  27. 130
      src/main/java/com/weis/darklaf/ui/tabframe/DarkTabFrameTabContainerUI.java
  28. 90
      src/main/java/com/weis/darklaf/ui/tabframe/DarkTabFrameTabLabelUI.java
  29. 437
      src/main/java/com/weis/darklaf/ui/tabframe/DarkTabFrameUI.java
  30. 2
      src/main/java/com/weis/darklaf/ui/tabframe/DarkTabbedPopupUI.java
  31. 60
      src/main/java/com/weis/darklaf/ui/tabframe/TabDragListener.java
  32. 232
      src/main/java/com/weis/darklaf/ui/tabframe/TabFrameLayout.java
  33. 508
      src/main/java/com/weis/darklaf/ui/tabframe/TabFrameTransferHandler.java
  34. 3
      src/main/java/com/weis/darklaf/ui/tooltip/DarkTooltipBorder.java
  35. 35
      src/main/java/com/weis/darklaf/util/PropertyLoader.java
  36. 48
      src/main/java/com/weis/darklaf/util/SwingXUtilities.java
  37. 10
      src/main/java/org/pbjar/jxlayer/plaf/ext/MouseEventUI.java
  38. 1
      src/main/java/org/pbjar/jxlayer/repaint/RepaintManagerUtils.java
  39. 2
      src/main/resources/com/weis/darklaf/properties/ui/tabFrame.properties
  40. 2
      src/main/resources/com/weis/darklaf/theme/intellij/intellij_defaults.properties
  41. 5
      src/test/java/TabFrameDemo.java
  42. 2
      src/test/java/ToolBarDemo.java
  43. 13
      src/test/java/UIDemo.java

10
src/main/java/com/weis/darklaf/components/ScrollPopupMenu.java

@ -93,6 +93,14 @@ public class ScrollPopupMenu extends JPopupMenu {
return overlayScrollPane;
}
/**
* Set the maximum height of the popup. If the size is larger than the specified maximum height the content will be
* wrapped inside a scroll pane.
* <p>
* Note: A value of <= 0 indicates that the height should not be limited.
*
* @param maxHeight the max height to use.
*/
public void setMaxHeight(final int maxHeight) {
this.maxHeight = maxHeight;
}
@ -109,7 +117,7 @@ public class ScrollPopupMenu extends JPopupMenu {
return;
}
final Dimension prefSize = getPreferredSize();
if (maxHeight == 0 || prefSize.height <= maxHeight) {
if (maxHeight <= 0 || prefSize.height <= maxHeight) {
setBounds(0, 0, prefSize.width, prefSize.height);
popWin.setContentPane(this);
setBorderPainted(true);

100
src/main/java/com/weis/darklaf/components/TextFieldHistory.java

@ -1,100 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2019 Jannis Weis
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.weis.darklaf.components;
import com.weis.darklaf.decorators.PlainAction;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
/**
* @author Jannis Weis
*/
public class TextFieldHistory extends ScrollPopupMenu implements ActionListener {
private final LinkedHashSet<String> history;
private JTextField textField;
/**
* Create Scroll Popup Menu.
*
* @param textField the text field.
* @param length the length of the history.
* @param maxH maximum height.
*/
public TextFieldHistory(final JTextField textField, final int length, final int maxH) {
super(maxH);
this.history = new LinkedHashSet<>(length);
this.textField = textField;
if (textField != null) {
textField.addActionListener(this);
}
}
public void setTextField(final JTextField textField) {
if (this.textField != null) {
textField.removeActionListener(this);
}
this.textField = textField;
if (this.textField != null) {
textField.addActionListener(this);
}
}
@Override
public void actionPerformed(final ActionEvent e) {
var text = textField.getText();
if (!text.isBlank()) {
history.remove(text);
history.add(text);
}
}
@Override
public void show(final Component invoker, final int x, final int y) {
if (history.size() == 0) return;
super.show(invoker, x, y);
}
@Override
protected void showPopup() {
if (history.size() == 0) {
firePopupMenuCanceled();
return;
}
this.removeAll();
LinkedList<String> list = new LinkedList<>(history);
Iterator<String> itr = list.descendingIterator();
while (itr.hasNext()) {
String item = itr.next();
add(new JMenuItem(new PlainAction(item, () -> textField.setText(item))));
}
super.showPopup();
}
}

80
src/main/java/com/weis/darklaf/components/tabframe/TabFrame.java → src/main/java/com/weis/darklaf/components/tabframe/JTabFrame.java

@ -24,6 +24,7 @@
package com.weis.darklaf.components.tabframe;
import com.weis.darklaf.components.alignment.Alignment;
import com.weis.darklaf.ui.tabframe.TabFrameTransferHandler;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
@ -39,7 +40,7 @@ import java.util.Objects;
*
* @author Jannis Weis
*/
public class TabFrame extends JComponent {
public class JTabFrame extends JComponent {
private final JComponent bottomTabs = createTabContainer();
private final JComponent topTabs = createTabContainer();
@ -54,13 +55,17 @@ public class TabFrame extends JComponent {
private int tabSize = -1;
private int maxTabWidth = -1;
private boolean inTransfer;
private Alignment transferAlign;
private int transferIndex;
private boolean dndEnabled;
/**
* Creates new {@link TabFrame}.
* Creates new {@link JTabFrame}.
* A TabFrame displays one center component and multiple popups around
* that can be toggles with a TabbedPane like tabArea along the border.
*/
public TabFrame() {
public JTabFrame() {
super();
updateUI();
add(content.getComponent());
@ -75,6 +80,7 @@ public class TabFrame extends JComponent {
popupLists[i] = new ArrayList<>();
}
selectedIndices = new int[count];
setDndEnabled(true);
}
@Override
@ -850,12 +856,48 @@ public class TabFrame extends JComponent {
return a;
}
public boolean isInTransfer() {
return inTransfer;
}
public void initTransfer(final Alignment a, final int index) {
getContentPane().getComponent().setEnabled(false);
this.inTransfer = true;
this.transferAlign = a;
this.transferIndex = index;
}
public void endTransfer() {
getContentPane().getComponent().setEnabled(true);
inTransfer = false;
transferAlign = null;
transferIndex = -10;
}
public TabFramePosition getTransferInfo() {
return new TabFramePosition(transferAlign, transferIndex);
}
public boolean isDndEnabled() {
return dndEnabled && getTransferHandler() instanceof TabFrameTransferHandler;
}
public void setDndEnabled(final boolean dndEnabled) {
var old = this.dndEnabled;
this.dndEnabled = dndEnabled;
if (getDropTarget() != null) {
getDropTarget().setActive(dndEnabled);
}
firePropertyChange("dndEnabled", old, dndEnabled);
}
/**
* This class represents a position inside the tabFrame.
*/
public static class TabFramePosition {
private final Alignment a;
private final int index;
private Alignment a;
private int index;
private Point point;
@Contract(pure = true)
public TabFramePosition(final Alignment a, final int index) {
@ -863,6 +905,29 @@ public class TabFrame extends JComponent {
this.index = index;
}
@Contract(pure = true)
public TabFramePosition(final Alignment a, final int index, final Point p) {
this.a = a;
this.index = index;
this.point = p;
}
public void setAlignment(final Alignment a) {
this.a = a;
}
public void setIndex(final int index) {
this.index = index;
}
public Point getPoint() {
return point;
}
public void setPoint(final Point point) {
this.point = point;
}
/**
* The alignment position.
* This specifies at what location the tab is placed.
@ -881,5 +946,10 @@ public class TabFrame extends JComponent {
public int getIndex() {
return index;
}
@Override
public String toString() {
return "[" + a + "," + index + "]";
}
}
}

8
src/main/java/com/weis/darklaf/components/tabframe/PanelPopup.java

@ -30,7 +30,7 @@ import javax.swing.*;
import java.awt.*;
/**
* Popup Component for {@link TabFrame}.
* Popup Component for {@link JTabFrame}.
*
* @author Jannis Weis
* @since 2019
@ -39,7 +39,7 @@ public class PanelPopup extends JPanel implements TabFramePopup {
private Component content;
private boolean open;
private TabFrame parent;
private JTabFrame parent;
private String title;
private Icon icon;
private Alignment alignment;
@ -137,12 +137,12 @@ public class PanelPopup extends JPanel implements TabFramePopup {
}
@Override
public TabFrame getTabFrame() {
public JTabFrame getTabFrame() {
return parent;
}
@Override
public void setTabFrame(final TabFrame parent) {
public void setTabFrame(final JTabFrame parent) {
var old = this.parent;
this.parent = parent;
firePropertyChange("tabFrame", old, parent);

2
src/main/java/com/weis/darklaf/components/tabframe/TabFrameContentPane.java

@ -34,7 +34,7 @@ import java.awt.*;
import java.util.function.BiConsumer;
/**
* Content pane for {@link TabFrame}.
* Content pane for {@link JTabFrame}.
*
* @author Jannis Weis
*/

30
src/main/java/com/weis/darklaf/components/tabframe/TabFramePopup.java

@ -62,21 +62,21 @@ public interface TabFramePopup {
}
/**
* Get the {{@link TabFrame}} this popup belongs to.
* Get the {{@link JTabFrame}} this popup belongs to.
*
* @return the {{@link TabFrame}}.
* @return the {{@link JTabFrame}}.
*/
TabFrame getTabFrame();
JTabFrame getTabFrame();
/**
* Sets the {{@link TabFrame}} this popup belongs to.
* Sets the {{@link JTabFrame}} this popup belongs to.
*
* @param tabFrame the {{@link TabFrame}}.
* @param tabFrame the {{@link JTabFrame}}.
*/
void setTabFrame(TabFrame tabFrame);
void setTabFrame(JTabFrame tabFrame);
/**
* Gets the alignment position in the {{@link TabFrame}}.
* Gets the alignment position in the {{@link JTabFrame}}.
*
* @return the alignment position.
*/
@ -90,20 +90,20 @@ public interface TabFramePopup {
int getIndex();
/**
* Set the index of the popup.
* This method should only be called from {{@link TabFrame}}.
* Sets the alignment position in the {{@link JTabFrame}}. This method should only be called from {{@link
* JTabFrame}}.
*
* @param index the index.
* @param alignment the alignment position.
*/
void setIndex(int index);
void setAlignment(Alignment alignment);
/**
* Sets the alignment position in the {{@link TabFrame}}.
* This method should only be called from {{@link TabFrame}}.
* Set the index of the popup.
* This method should only be called from {{@link JTabFrame}}.
*
* @param alignment the alignment position.
* @param index the index.
*/
void setAlignment(Alignment alignment);
void setIndex(int index);
/**
* Open the popup.

4
src/main/java/com/weis/darklaf/components/tabframe/TabFrameTab.java

@ -97,13 +97,13 @@ public interface TabFrameTab {
*
* @return the TabFrame.
*/
TabFrame getTabFrame();
JTabFrame getTabFrame();
/**
* Set the tab frame this tab currently belongs to.
*
* @param tabFrame the TabFrame.
*/
void setTabFrame(TabFrame tabFrame);
void setTabFrame(JTabFrame tabFrame);
}

8
src/main/java/com/weis/darklaf/components/tabframe/TabFrameTabContainer.java

@ -31,14 +31,14 @@ import java.awt.*;
public class TabFrameTabContainer extends JPanel implements TabFrameTab {
protected final TabFrameTab oldTab;
private TabFrame parent;
private JTabFrame parent;
private Component content;
private Alignment orientation;
private boolean selected;
private int index;
private int accelerator;
public TabFrameTabContainer(final TabFrame parent, final JComponent content, final TabFrameTab oldTab,
public TabFrameTabContainer(final JTabFrame parent, final JComponent content, final TabFrameTab oldTab,
final Alignment alignment, final int index) {
super(new BorderLayout());
this.parent = parent;
@ -140,12 +140,12 @@ public class TabFrameTabContainer extends JPanel implements TabFrameTab {
}
@Override
public TabFrame getTabFrame() {
public JTabFrame getTabFrame() {
return parent;
}
@Override
public void setTabFrame(final TabFrame parent) {
public void setTabFrame(final JTabFrame parent) {
var old = this.parent;
this.parent = parent;
firePropertyChange("tabFrame", old, parent);

12
src/main/java/com/weis/darklaf/components/tabframe/TabFrameTabLabel.java

@ -34,13 +34,13 @@ import java.awt.*;
import java.util.Objects;
/**
* Tab Component for {@link TabFrame}.
* Tab Component for {@link JTabFrame}.
*
* @author Jannis Weis
*/
public class TabFrameTabLabel extends JLabel implements TabFrameTab {
private TabFrame parent;
private JTabFrame parent;
private Alignment orientation;
private String title;
private boolean selected;
@ -48,7 +48,7 @@ public class TabFrameTabLabel extends JLabel implements TabFrameTab {
private int index;
/**
* Create new TabComponent for the frame of {@link TabFrame}.
* Create new TabComponent for the frame of {@link JTabFrame}.
*
* @param title the title.
* @param icon the icon.
@ -57,7 +57,7 @@ public class TabFrameTabLabel extends JLabel implements TabFrameTab {
* @param parent the parent layout manager.
*/
public TabFrameTabLabel(final String title, final Icon icon, final Alignment orientation,
final int index, @NotNull final TabFrame parent) {
final int index, @NotNull final JTabFrame parent) {
this.index = index;
this.accelerator = -1;
this.parent = parent;
@ -131,12 +131,12 @@ public class TabFrameTabLabel extends JLabel implements TabFrameTab {
}
@Override
public TabFrame getTabFrame() {
public JTabFrame getTabFrame() {
return parent;
}
@Override
public void setTabFrame(final TabFrame parent) {
public void setTabFrame(final JTabFrame parent) {
var old = this.parent;
this.parent = parent;
firePropertyChange("tabFrame", old, parent);

29
src/main/java/com/weis/darklaf/components/tabframe/TabFrameUI.java

@ -23,9 +23,36 @@
*/
package com.weis.darklaf.components.tabframe;
import com.weis.darklaf.components.alignment.Alignment;
import javax.swing.plaf.ComponentUI;
import java.awt.*;
public abstract class TabFrameUI extends ComponentUI {
public abstract int getTabSize(TabFrame tabFrame);
public abstract int getTabSize(JTabFrame tabFrame);
public abstract void clearTargetIndicator();
public abstract void clearSourceIndicator();
public abstract Color getDragBorderColor();
public abstract void setSourceIndicator(Alignment a, int tabIndex);
public abstract void setTargetIndicator(Alignment a, int tabIndex);
public abstract JTabFrame.TabFramePosition getTabIndexAt(JTabFrame tabFrame, Point p);
public abstract JTabFrame.TabFramePosition getNearestTabIndexAt(JTabFrame tabFrame, Point p);
public abstract void setDropSize(final int width, final int height);
public abstract int getTabWidth(JTabFrame tabFrame, Alignment a, int index);
public abstract int getTabHeight(JTabFrame tabFrame, Alignment a, int index);
public abstract Rectangle getTabContainerBounds(JTabFrame tabFrame, Alignment a);
public abstract JTabFrame.TabFramePosition getDropPosition(JTabFrame tabFrame, Point p);
}

2
src/main/java/com/weis/darklaf/components/tabframe/TabbedPopup.java

@ -30,7 +30,7 @@ import java.util.Collection;
import java.util.List;
/**
* Tabbed Popup Component for {@link TabFrame}.
* Tabbed Popup Component for {@link JTabFrame}.
*
* @author Jannis Weis
*/

40
src/main/java/com/weis/darklaf/components/text/SearchEvent.java

@ -0,0 +1,40 @@
/*
* MIT License
*
* Copyright (c) 2019 Jannis Weis
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.weis.darklaf.components.text;
import java.awt.event.ActionEvent;
public class SearchEvent extends ActionEvent {
private final String text;
public SearchEvent(final Object source, final int id, final String command, final String text) {
super(source, id, command);
this.text = text;
}
public String getText() {
return text;
}
}

31
src/main/java/com/weis/darklaf/components/text/SearchListener.java

@ -0,0 +1,31 @@
/*
* MIT License
*
* Copyright (c) 2019 Jannis Weis
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.weis.darklaf.components.text;
import java.util.EventListener;
public interface SearchListener extends EventListener {
void searchPerformed(final SearchEvent e);
}

119
src/main/java/com/weis/darklaf/components/text/SearchTextField.java

@ -0,0 +1,119 @@
/*
* MIT License
*
* Copyright (c) 2019 Jannis Weis
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.weis.darklaf.components.text;
import javax.swing.*;
import javax.swing.text.Document;
public class SearchTextField extends JTextField {
public static final int SEARCH = 0;
/**
* Constructs a new <code>TextField</code>. A default model is created, the initial string is <code>null</code>,
* and the number of columns is set to 0.
*/
public SearchTextField() {
this(null, null, 0);
}
/**
* Constructs a new <code>JTextField</code> that uses the given text storage model and the given number of columns.
* This is the constructor through which the other constructors feed. If the document is <code>null</code>, a
* default model is created.
*
* @param doc the text storage to use; if this is <code>null</code>, a default will be provided by calling the
* <code>createDefaultModel</code> method
* @param text the initial string to display, or <code>null</code>
* @param columns the number of columns to use to calculate the preferred width &gt;= 0; if <code>columns</code> is
* set to zero, the preferred width will be whatever naturally results from the component
* implementation
* @throws IllegalArgumentException if <code>columns</code> &lt; 0
*/
public SearchTextField(final Document doc, final String text, final int columns) {
super(doc, text, columns);
putClientProperty("JTextField.variant", "search");
addActionListener(e -> {
var list = listenerList.getListeners(SearchListener.class);
var evt = new SearchEvent(SearchTextField.this, SEARCH, "search", getText());
for (var listener : list) {
if (listener != null) {
listener.searchPerformed(evt);
}
}
});
}
/**
* Constructs a new <code>TextField</code> initialized with the specified text. A default model is created and the
* number of columns is 0.
*
* @param text the text to be displayed, or <code>null</code>
*/
public SearchTextField(final String text) {
this(null, text, 0);
}
/**
* Constructs a new empty <code>TextField</code> with the specified number of columns. A default model is created
* and the initial string is set to
* <code>null</code>.
*
* @param columns the number of columns to use to calculate the preferred width; if columns is set to zero, the
* preferred width will be whatever naturally results from the component implementation
*/
public SearchTextField(final int columns) {
this(null, null, columns);
}
/**
* Constructs a new <code>TextField</code> initialized with the specified text and columns. A default model is
* created.
*
* @param text the text to be displayed, or <code>null</code>
* @param columns the number of columns to use to calculate the preferred width; if columns is set to zero, the
* preferred width will be whatever naturally results from the component implementation
*/
public SearchTextField(final String text, final int columns) {
this(null, text, columns);
}
/**
* Add a {@link SearchListener} to this search text field.
*
* @param listener the listen to add.
*/
public void addSearchListener(final SearchListener listener) {
listenerList.add(SearchListener.class, listener);
}
/**
* Remove a {@link SearchListener} from this search text field.
*
* @param listener the listener to remove
*/
public void removeSearchListener(final SearchListener listener) {
listenerList.remove(SearchListener.class, listener);
}
}

174
src/main/java/com/weis/darklaf/components/text/SearchTextFieldWithHistory.java

@ -0,0 +1,174 @@
/*
* MIT License
*
* Copyright (c) 2019 Jannis Weis
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.weis.darklaf.components.text;
import javax.swing.text.Document;
import java.util.List;
/**
* {@link SearchTextField} that has a popup that displays the search history. A search entry is added
*
* @author Jannis Weis
*/
public class SearchTextFieldWithHistory extends SearchTextField {
protected TextFieldHistoryPopup history;
/**
* Constructs a new <code>TextField</code>. A default model is created, the initial string is <code>null</code>,
* and the number of columns is set to 0.
*/
public SearchTextFieldWithHistory() {
this(null, null, 0);
}
/**
* Constructs a new <code>JTextField</code> that uses the given text storage model and the given number of columns.
* This is the constructor through which the other constructors feed. If the document is <code>null</code>, a
* default model is created.
*
* @param doc the text storage to use; if this is <code>null</code>, a default will be provided by calling the
* <code>createDefaultModel</code> method
* @param text the initial string to display, or <code>null</code>
* @param columns the number of columns to use to calculate the preferred width &gt;= 0; if <code>columns</code> is
* set to zero, the preferred width will be whatever naturally results from the component
* implementation
* @throws IllegalArgumentException if <code>columns</code> &lt; 0
*/
public SearchTextFieldWithHistory(final Document doc, final String text, final int columns) {
super(doc, text, columns);
history = new TextFieldHistoryPopup(this, 100, 800);
putClientProperty("JTextField.Search.FindPopup", history);
}
/**
* Constructs a new <code>TextField</code> initialized with the specified text. A default model is created and the
* number of columns is 0.
*
* @param text the text to be displayed, or <code>null</code>
*/
public SearchTextFieldWithHistory(final String text) {
this(null, text, 0);
}
/**
* Constructs a new empty <code>TextField</code> with the specified number of columns. A default model is created
* and the initial string is set to
* <code>null</code>.
*
* @param columns the number of columns to use to calculate the preferred width; if columns is set to zero, the
* preferred width will be whatever naturally results from the component implementation
*/
public SearchTextFieldWithHistory(final int columns) {
this(null, null, columns);
}
/**
* Constructs a new <code>TextField</code> initialized with the specified text and columns. A default model is
* created.
*
* @param text the text to be displayed, or <code>null</code>
* @param columns the number of columns to use to calculate the preferred width; if columns is set to zero, the
* preferred width will be whatever naturally results from the component implementation
*/
public SearchTextFieldWithHistory(final String text, final int columns) {
this(null, text, columns);
}
/**
* Set the maximum height of the popup. If the size is larger than the specified maximum height the content will be
* wrapped inside a scroll pane.
* <p>
* Note: A value of <= 0 indicates that the height should not be limited.
*
* @param maximumHeight the max height to use.
*/
public void setMaximumHeight(final int maximumHeight) {
history.setMaxHeight(maximumHeight);
}
/**
* Get the history as a list.
*
* @return the history.
*/
public List<String> getHistory() {
return history.getHistory();
}
/**
* Clear all entries from the history.
*/
public void clearHistory() {
history.clearHistory();
}
/**
* Add entry to the history. If the size is greater than the capacity the oldest entry will be deleted.
*
* @param entry the entry to add.
* @see #getLength() getLength
* @see #setCapacity(int) setCapacity
* @see #getCapacity() getCapacity
*/
public void addEntry(final String entry) {
history.addEntry(entry);
}
/**
* Get the capacity of the history.
*
* @return the capacity.
* @see #setCapacity(int) setCapacity()
*/
public int getCapacity() {
return history.getCapacity();
}
/**
* Set the capacity of the history. If the size grows larger than the capacity the oldest entry will be deleted.
*
* @param capacity the capacity.
* @throws IllegalArgumentException if capacity < 0
*/
public void setCapacity(final int capacity) throws IllegalArgumentException {
history.setCapacity(capacity);
}
/**
* Get the current length of the history.
*
* @return the current length of the history.
*/
public int getLength() {
return history.getLength();
}
public void setHistoryLength(final int length) {
if (length < 0) throw new IllegalArgumentException("History can't have negative size");
if (length == 0) {
putClientProperty("JTextField.Search.FindPopup", null);
}
}
}

157
src/main/java/com/weis/darklaf/components/text/TextFieldHistoryPopup.java

@ -0,0 +1,157 @@
/*
* MIT License
*
* Copyright (c) 2019 Jannis Weis
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.weis.darklaf.components.text;
import com.weis.darklaf.components.ScrollPopupMenu;
import com.weis.darklaf.decorators.PlainAction;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.awt.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @author Jannis Weis
*/
public class TextFieldHistoryPopup extends ScrollPopupMenu implements SearchListener {
private final Set<String> history;
private final JTextField textField;
private int capacity;
/**
* Create a search popup Menu.
*
* @param textField the text field.
* @param capacity the length of the history.
* @param maxH maximum height.
*/
public TextFieldHistoryPopup(@NotNull final SearchTextField textField, final int capacity, final int maxH) {
super(maxH);
this.textField = textField;
textField.addSearchListener(this);
setCapacity(capacity);
this.history = Collections.newSetFromMap(new LinkedHashMap<>() {
protected boolean removeEldestEntry(final Map.Entry<String, Boolean> eldest) {
return size() > capacity;
}
});
}
/**
* Get the history as a list.
*
* @return the history.
*/
public List<String> getHistory() {
return new ArrayList<>(history);
}
/**
* Get the capacity of the history.
*
* @return the capacity.
* @see #setCapacity(int) setCapacity()
*/
public int getCapacity() {
return capacity;
}
/**
* Set the capacity of the history. If the size grows larger than the capacity the oldest entry will be deleted.
*
* @param capacity the capacity.
* @throws IllegalArgumentException if capacity < 0
*/
public void setCapacity(final int capacity) throws IllegalArgumentException {
if (capacity < 0) throw new IllegalArgumentException("Negative history size is not supported");
this.capacity = capacity;
}
/**
* Get the current length of the history.
*
* @return the current length of the history.
*/
public int getLength() {
return history.size();
}
@Override
public void searchPerformed(@NotNull final SearchEvent e) {
var text = e.getText();
if (!text.isBlank()) {
addEntry(text);
}
}
@Override
public void show(final Component invoker, final int x, final int y) {
if (history.size() == 0) return;
super.show(invoker, x, y);
}
@Override
protected void showPopup() {
if (history.size() == 0) {
firePopupMenuCanceled();
return;
}
this.removeAll();
LinkedList<String> list = new LinkedList<>(history);
Iterator<String> itr = list.descendingIterator();
while (itr.hasNext()) {
String item = itr.next();
add(new JMenuItem(new PlainAction(item, () -> textField.setText(item))));
}
super.showPopup();
}
/**
* Add entry to the history. If the size is greater than the capacity the oldest entry will be deleted.
*
* @param entry the entry to add.
* @see #getLength() getLength
* @see #setCapacity(int) setCapacity
* @see #getCapacity() getCapacity
*/
public void addEntry(final String entry) {
history.remove(entry);
history.add(entry);
}
/**
* Clear all entries from the history.
*/
public void clearHistory() {
history.clear();
}
}

1
src/main/java/com/weis/darklaf/components/tooltip/ToolTipContext.java

@ -429,6 +429,7 @@ public class ToolTipContext implements ToolTipListener {
if (valid && !updatePosition
&& lastPos != null
&& !Objects.equals(rect, lastRect)) {
System.out.println("here");
return lastPos;
}
getToolTip().setTipText(c.getToolTipText(event));

2
src/main/java/com/weis/darklaf/components/Insets2D.java → src/main/java/com/weis/darklaf/components/uiresource/Insets2D.java

@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.weis.darklaf.components;
package com.weis.darklaf.components.uiresource;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;

2
src/main/java/com/weis/darklaf/components/JLabelUIResource.java → src/main/java/com/weis/darklaf/components/uiresource/JLabelUIResource.java

@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.weis.darklaf.components;
package com.weis.darklaf.components.uiresource;
import javax.swing.*;
import javax.swing.plaf.UIResource;

2
src/main/java/com/weis/darklaf/components/JPanelUIResource.java → src/main/java/com/weis/darklaf/components/uiresource/JPanelUIResource.java

@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.weis.darklaf.components;
package com.weis.darklaf.components.uiresource;
import javax.swing.*;
import javax.swing.plaf.UIResource;

2
src/main/java/com/weis/darklaf/components/UIResourceWrapper.java → src/main/java/com/weis/darklaf/components/uiresource/UIResourceWrapper.java

@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.weis.darklaf.components;
package com.weis.darklaf.components.uiresource;
import javax.swing.border.Border;
import java.awt.*;

2
src/main/java/com/weis/darklaf/ui/colorchooser/DarkColorChooserPanel.java

@ -201,7 +201,6 @@ public class DarkColorChooserPanel extends AbstractColorChooserPanel implements
protected void update() {
try {
if (isChanging) return;
System.out.println("here");
var hexStr = String.format("%1$-" + 8 + "s", field.getText()).replaceAll(" ", "F");
var alpha = isColorTransparencySelectionEnabled()
? Integer.valueOf(hexStr.substring(6, 8), 16) : 255;
@ -210,7 +209,6 @@ public class DarkColorChooserPanel extends AbstractColorChooserPanel implements
Integer.valueOf(hexStr.substring(2, 4), 16),
Integer.valueOf(hexStr.substring(4, 6), 16),
alpha);
System.out.println(c);
colorWheelPanel.setColor(c, textHex);
} catch (NumberFormatException | IndexOutOfBoundsException ignore) {}
}

13
src/main/java/com/weis/darklaf/ui/tabbedpane/DarkTabbedPaneUI.java

@ -1,6 +1,6 @@
package com.weis.darklaf.ui.tabbedpane;
import com.weis.darklaf.components.UIResourceWrapper;
import com.weis.darklaf.components.uiresource.UIResourceWrapper;
import com.weis.darklaf.util.DarkUIUtil;
import com.weis.darklaf.util.GraphicsContext;
import org.jetbrains.annotations.Contract;
@ -20,7 +20,7 @@ import java.util.TooManyListenersException;
*/
public class DarkTabbedPaneUI extends DarkTabbedPaneUIBridge {
protected static final TabbedPaneTransferHandler TRANSFER_HANDLER = new TabbedPaneTransferHandler();
protected static final TabbedPaneTransferHandler TRANSFER_HANDLER = new TabbedPaneTransferHandler.UIResource();
protected final FocusListener focusListener = new FocusListener() {
@Override
public void focusGained(final FocusEvent e) {
@ -110,8 +110,15 @@ public class DarkTabbedPaneUI extends DarkTabbedPaneUIBridge {
@Override
public void uninstallUI(final JComponent c) {
super.uninstallUI(c);
scrollableTabSupport = null;
if (tabPane.getTransferHandler() instanceof TabbedPaneTransferHandler.UIResource) {
tabPane.setTransferHandler(null);
if (tabPane.getDropTarget() != null) {
tabPane.getDropTarget().removeDropTargetListener(TRANSFER_HANDLER);
tabPane.getDropTarget().setActive(false);
}
}
super.uninstallUI(c);
}
@Override

4
src/main/java/com/weis/darklaf/ui/tabbedpane/TabbedPaneTransferHandler.java

@ -450,4 +450,8 @@ public class TabbedPaneTransferHandler extends TransferHandler implements DropTa
currentTransferable = null;
}
}
public static class UIResource extends TabbedPaneTransferHandler {
}
}

24
src/main/java/com/weis/darklaf/ui/tabframe/DarkPanelPopupUI.java

@ -23,12 +23,12 @@
*/
package com.weis.darklaf.ui.tabframe;
import com.weis.darklaf.components.JLabelUIResource;
import com.weis.darklaf.components.alignment.Alignment;
import com.weis.darklaf.components.border.MutableLineBorder;
import com.weis.darklaf.components.tabframe.JTabFrame;
import com.weis.darklaf.components.tabframe.PanelPopup;
import com.weis.darklaf.components.tabframe.TabFrame;
import com.weis.darklaf.components.tooltip.ToolTipContext;
import com.weis.darklaf.components.uiresource.JLabelUIResource;
import com.weis.darklaf.ui.panel.DarkPanelUI;
import com.weis.darklaf.util.DarkUIUtil;
import org.jetbrains.annotations.Contract;
@ -250,19 +250,19 @@ public class DarkPanelPopupUI extends DarkPanelUI implements PropertyChangeListe
label.setIcon((Icon) evt.getNewValue());
label.repaint();
} else if ("visibleTab".equals(key)) {
if (evt.getNewValue() instanceof TabFrame.TabFramePosition) {
if (((TabFrame.TabFramePosition) evt.getNewValue()).getAlignment() == popupComponent.getAlignment()) {
if (evt.getNewValue() instanceof JTabFrame.TabFramePosition) {
if (((JTabFrame.TabFramePosition) evt.getNewValue()).getAlignment() == popupComponent.getAlignment()) {
updateBorder(true);
}
}
} else if ("tabFrame".equals(key)) {
var oldVal = evt.getOldValue();
var newVal = evt.getNewValue();
if (oldVal instanceof TabFrame) {
((TabFrame) oldVal).removePropertyChangeListener(this);
if (oldVal instanceof JTabFrame) {
((JTabFrame) oldVal).removePropertyChangeListener(this);
}
if (newVal instanceof TabFrame) {
((TabFrame) newVal).addPropertyChangeListener(this);
if (newVal instanceof JTabFrame) {
((JTabFrame) newVal).addPropertyChangeListener(this);
}
} else if ("peerInsets".equals(key)) {
updateBorder(false);
@ -289,8 +289,10 @@ public class DarkPanelPopupUI extends DarkPanelUI implements PropertyChangeListe
popupComponent.doLayout();
popupComponent.repaint();
if (notifyPeer) {
var peer = tabFrame.getPopupComponentAt(tabFrame.getPeer(popupComponent.getAlignment()));
peer.firePropertyChange("peerInsets", 0, 1);
try {
var peer = tabFrame.getPopupComponentAt(tabFrame.getPeer(popupComponent.getAlignment()));
peer.firePropertyChange("peerInsets", 0, 1);
} catch (IndexOutOfBoundsException ignored) {/*may happen during transfer*/}
}
}
}
@ -353,7 +355,7 @@ public class DarkPanelPopupUI extends DarkPanelUI implements PropertyChangeListe
public void eventDispatched(@NotNull final AWTEvent event) {
if (event.getID() == FocusEvent.FOCUS_GAINED) {
var focusOwner = FocusManager.getCurrentManager().getFocusOwner();
if (focusOwner instanceof TabFrame) return;
if (focusOwner instanceof JTabFrame) return;
if (focusOwner instanceof JRootPane) return;
boolean focus = DarkUIUtil.hasFocus(popupComponent);
setHeaderBackground(focus);

3
src/main/java/com/weis/darklaf/ui/tabframe/DarkTabFrameComponentPopupMenu.java

@ -30,10 +30,11 @@ import com.weis.darklaf.icons.EmptyIcon;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.plaf.UIResource;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
public class DarkTabFrameComponentPopupMenu extends JXPopupMenu implements PropertyChangeListener {
public class DarkTabFrameComponentPopupMenu extends JXPopupMenu implements PropertyChangeListener, UIResource {
private final TabFrameTab tab;
private JMenuItem[] actions;

130
src/main/java/com/weis/darklaf/ui/tabframe/DarkTabFrameTabContainerUI.java

@ -23,7 +23,7 @@
*/
package com.weis.darklaf.ui.tabframe;
import com.weis.darklaf.components.tabframe.TabFrame;
import com.weis.darklaf.components.tabframe.JTabFrame;
import com.weis.darklaf.components.tabframe.TabFrameTabContainer;
import com.weis.darklaf.decorators.HoverListener;
import com.weis.darklaf.ui.panel.DarkPanelUI;
@ -38,6 +38,7 @@ import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
@ -53,6 +54,7 @@ public class DarkTabFrameTabContainerUI extends DarkPanelUI implements PropertyC
}
}
};
private MouseMotionListener dragListener;
private HoverListener hoverListener;
private Color selectedColor;
private Color hoverColor;
@ -73,90 +75,50 @@ public class DarkTabFrameTabContainerUI extends DarkPanelUI implements PropertyC
}
protected void installListeners() {
dragListener = new TabDragListener(tabContainer);
hoverListener = new HoverListener(tabContainer);
tabContainer.addMouseListener(hoverListener);
tabContainer.addPropertyChangeListener(this);
tabContainer.addMouseListener(mouseListener);
tabContainer.addMouseMotionListener(dragListener);
var cont = tabContainer.getContent();
if (cont != null) {
cont.addMouseListener(hoverListener);
cont.addMouseListener(mouseListener);
cont.addMouseMotionListener(dragListener);
}
}
protected void installAccelerator(final TabFrame tabFrame) {
if (tabFrame == null) return;
int acc = tabContainer.getAccelerator();
if (acc < 0) return;
tabFrame.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
.put(KeyStroke.getKeyStroke(UIManager.getString("TabFrame.acceleratorKeyCode") + " " + acc),
"accelerator_" + acc);
tabFrame.getActionMap().put("accelerator_" + acc, createAcceleratorAction(tabFrame));
}
protected Action createAcceleratorAction(final TabFrame tabFrame) {
return new AbstractAction() {
@Override
public void actionPerformed(final ActionEvent e) {
var a = tabContainer.getOrientation();
var index = tabContainer.getIndex();
if (!tabContainer.isSelected()) {
tabFrame.toggleTab(a, index, true);
} else {
var popup = tabFrame.getPopupComponentAt(a, index);
if (!DarkUIUtil.hasFocus(popup)) {
popup.requestFocus();
} else {
tabFrame.toggleTab(a, index, false);
}
}
}
};
}
@Override
public void uninstallUI(final JComponent c) {
super.uninstallUI(c);
uninstallListeners();
uninstallAccelerator(tabContainer.getTabFrame());
tabContainer = null;
}
protected void uninstallListeners() {
tabContainer.removeMouseListener(hoverListener);
tabContainer.removeMouseListener(mouseListener);
tabContainer.removePropertyChangeListener(this);
tabContainer.removeMouseMotionListener(dragListener);
var cont = tabContainer.getContent();
if (cont != null) {
cont.removeMouseListener(hoverListener);
cont.removeMouseListener(mouseListener);
cont.removeMouseMotionListener(dragListener);
}
dragListener = null;
hoverListener = null;
uninstallAccelerator(tabContainer.getTabFrame());
tabContainer = null;
}
protected void installDefaults(final JPanel p) {
super.installDefaults(p);
tabContainer.setOpaque(true);
selectedColor = UIManager.getColor("TabFrameTab.selectedBackground");
hoverColor = UIManager.getColor("TabFrameTab.hoverBackground");
}
protected void uninstallAccelerator(final TabFrame tabFrame) {
protected void uninstallAccelerator(final JTabFrame tabFrame) {
if (tabFrame == null) return;
int acc = tabContainer.getAccelerator();
String accAction = "accelerator_" + acc;
tabFrame.getActionMap().remove(accAction);
}
@Override
public void paint(@NotNull final Graphics g, @NotNull final JComponent c) {
g.setColor(getBackground(tabContainer));
g.fillRect(0, 0, c.getWidth(), c.getHeight());
super.paint(g, c);
}
public Color getBackground(@NotNull final TabFrameTabContainer tab) {
return tab.isSelected()
? selectedColor
: hoverListener.isHover() ? hoverColor : tab.getBackground();
}
@Override
public void propertyChange(@NotNull final PropertyChangeEvent evt) {
var key = evt.getPropertyName();
@ -166,10 +128,12 @@ public class DarkTabFrameTabContainerUI extends DarkPanelUI implements PropertyC
if (oldVal instanceof Component) {
((Component) oldVal).removeMouseListener(mouseListener);
((Component) oldVal).removeMouseListener(hoverListener);
((Component) oldVal).removeMouseMotionListener(dragListener);
}
if (newVal instanceof Component) {
((Component) newVal).addMouseListener(mouseListener);
((Component) newVal).addMouseListener(hoverListener);
((Component) newVal).addMouseMotionListener(dragListener);
}
} else if ("selected".equals(key)) {
if (tabContainer == null) return;
@ -179,12 +143,62 @@ public class DarkTabFrameTabContainerUI extends DarkPanelUI implements PropertyC
uninstallAccelerator(tabContainer.getTabFrame());
installAccelerator(tabContainer.getTabFrame());
} else if ("tabFrame".equals(key)) {
if (evt.getOldValue() instanceof TabFrame) {
uninstallAccelerator((TabFrame) evt.getOldValue());
if (evt.getOldValue() instanceof JTabFrame) {
uninstallAccelerator((JTabFrame) evt.getOldValue());
}
if (evt.getNewValue() instanceof TabFrame) {
installAccelerator((TabFrame) evt.getNewValue());
if (evt.getNewValue() instanceof JTabFrame) {
installAccelerator((JTabFrame) evt.getNewValue());
}
}
}
protected void installDefaults(final JPanel p) {
super.installDefaults(p);
tabContainer.setOpaque(true);
selectedColor = UIManager.getColor("TabFrameTab.selectedBackground");
hoverColor = UIManager.getColor("TabFrameTab.hoverBackground");
}
protected void installAccelerator(final JTabFrame tabFrame) {
if (tabFrame == null) return;
int acc = tabContainer.getAccelerator();
if (acc < 0) return;
tabFrame.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
.put(KeyStroke.getKeyStroke(UIManager.getString("TabFrame.acceleratorKeyCode") + " " + acc),
"accelerator_" + acc);
tabFrame.getActionMap().put("accelerator_" + acc, createAcceleratorAction(tabFrame));
}
@Override
public void paint(@NotNull final Graphics g, @NotNull final JComponent c) {
g.setColor(getBackground(tabContainer));
g.fillRect(0, 0, c.getWidth(), c.getHeight());
super.paint(g, c);
}
public Color getBackground(@NotNull final TabFrameTabContainer tab) {
return tab.isSelected()
? selectedColor
: hoverListener.isHover() ? hoverColor : tab.getBackground();
}
protected Action createAcceleratorAction(final JTabFrame tabFrame) {
return new AbstractAction() {
@Override
public void actionPerformed(final ActionEvent e) {
var a = tabContainer.getOrientation();
var index = tabContainer.getIndex();
if (!tabContainer.isSelected()) {
tabFrame.toggleTab(a, index, true);
} else {
var popup = tabFrame.getPopupComponentAt(a, index);
if (!DarkUIUtil.hasFocus(popup)) {
popup.requestFocus();
} else {
tabFrame.toggleTab(a, index, false);
}
}
}
};
}
}

90
src/main/java/com/weis/darklaf/ui/tabframe/DarkTabFrameTabLabelUI.java

@ -24,7 +24,7 @@
package com.weis.darklaf.ui.tabframe;
import com.weis.darklaf.components.alignment.Alignment;
import com.weis.darklaf.components.tabframe.TabFrame;
import com.weis.darklaf.components.tabframe.JTabFrame;
import com.weis.darklaf.components.tabframe.TabFrameTabLabel;
import com.weis.darklaf.decorators.HoverListener;
import com.weis.darklaf.icons.RotatableIcon;
@ -36,6 +36,7 @@ import sun.swing.SwingUtilities2;
import javax.swing.*;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicHTML;
import javax.swing.text.View;
import java.awt.*;
@ -43,6 +44,7 @@ import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
@ -51,13 +53,14 @@ public class DarkTabFrameTabLabelUI extends DarkLabelUI implements PropertyChang
private TabFrameTabLabel tabComponent;
private final MouseListener mouseListener = new MouseAdapter() {
@Override
public void mousePressed(final MouseEvent e) {
public void mouseClicked(final MouseEvent e) {
if (SwingUtilities.isLeftMouseButton(e)) {
tabComponent.getTabFrame().toggleTab(tabComponent.getOrientation(), tabComponent.getIndex(),
!tabComponent.isSelected());
}
}
};
private MouseMotionListener dragListener;
private HoverListener hoverListener;
private Color defaultFontColor;
private Color selectedFontColor;
@ -66,6 +69,7 @@ public class DarkTabFrameTabLabelUI extends DarkLabelUI implements PropertyChang
private RotatableIcon rotatableIcon = new RotatableIcon();
private Rectangle paintIconR = new Rectangle();
private Rectangle paintTextR = new Rectangle();
private boolean printing;
@NotNull
@Contract("_ -> new")
@ -119,9 +123,10 @@ public class DarkTabFrameTabLabelUI extends DarkLabelUI implements PropertyChang
@Override
public void uninstallUI(final JComponent c) {
super.uninstallUI(c);
uninstallAccelerator(tabComponent.getTabFrame());
tabComponent.removeMouseListener(hoverListener);
tabComponent.removeMouseListener(mouseListener);
uninstallListeners(tabComponent);
if (tabComponent.getComponentPopupMenu() instanceof UIResource) {
tabComponent.setComponentPopupMenu(null);
}
hoverListener = null;
tabComponent = null;
}
@ -142,13 +147,54 @@ public class DarkTabFrameTabLabelUI extends DarkLabelUI implements PropertyChang
@Override
protected void installListeners(final JLabel c) {
super.installListeners(c);
dragListener = new TabDragListener(tabComponent);
hoverListener = new HoverListener(tabComponent);
tabComponent.addMouseListener(hoverListener);
tabComponent.addMouseListener(mouseListener);
installAccelerator(tabComponent.getTabFrame());
tabComponent.addMouseMotionListener(dragListener);
}
@Override
protected void uninstallListeners(final JLabel c) {
super.uninstallListeners(c);
tabComponent.removeMouseListener(hoverListener);
tabComponent.removeMouseListener(mouseListener);
uninstallAccelerator(tabComponent.getTabFrame());
tabComponent.removeMouseMotionListener(dragListener);
dragListener = null;
}
@Override
public void propertyChange(final PropertyChangeEvent e) {
super.propertyChange(e);
var key = e.getPropertyName();
if ("selected".equals(key)) {
tabComponent.setForeground(Boolean.TRUE.equals(e.getNewValue())
? selectedFontColor : defaultFontColor);
tabComponent.repaint();
} else if ("title".equals(key)) {
updateText();
} else if ("accelerator".equals(key)) {
updateText();
if (tabComponent == null) return;
uninstallAccelerator(tabComponent.getTabFrame());
installAccelerator(tabComponent.getTabFrame());
} else if ("orientation".equals(key)) {
rotatableIcon.setOrientation(mapOrientation(tabComponent.getOrientation()));
} else if ("tabFrame".equals(key)) {
if (e.getOldValue() instanceof JTabFrame) {
uninstallAccelerator((JTabFrame) e.getOldValue());
}
if (e.getNewValue() instanceof JTabFrame) {
installAccelerator((JTabFrame) e.getNewValue());
}
} else if ("paintingForPrint".equals(key)) {
printing = Boolean.TRUE.equals(e.getNewValue());
}
}
protected void installAccelerator(final TabFrame tabFrame) {
protected void installAccelerator(final JTabFrame tabFrame) {
if (tabFrame == null) return;
int acc = tabComponent.getAccelerator();
if (acc < 0) return;
@ -158,7 +204,7 @@ public class DarkTabFrameTabLabelUI extends DarkLabelUI implements PropertyChang
tabFrame.getActionMap().put("accelerator_" + acc, createAcceleratorAction(tabFrame));
}
protected Action createAcceleratorAction(final TabFrame tabFrame) {
protected Action createAcceleratorAction(final JTabFrame tabFrame) {
return new AbstractAction() {
@Override
public void actionPerformed(final ActionEvent e) {
@ -178,33 +224,6 @@ public class DarkTabFrameTabLabelUI extends DarkLabelUI implements PropertyChang
};
}
@Override
public void propertyChange(final PropertyChangeEvent e) {
super.propertyChange(e);
var key = e.getPropertyName();
if ("selected".equals(key)) {
tabComponent.setForeground(Boolean.TRUE.equals(e.getNewValue())
? selectedFontColor : defaultFontColor);
tabComponent.repaint();
} else if ("title".equals(key)) {
updateText();
} else if ("accelerator".equals(key)) {
updateText();
if (tabComponent == null) return;
uninstallAccelerator(tabComponent.getTabFrame());
installAccelerator(tabComponent.getTabFrame());
} else if ("orientation".equals(key)) {
rotatableIcon.setOrientation(mapOrientation(tabComponent.getOrientation()));
} else if ("tabFrame".equals(key)) {
if (e.getOldValue() instanceof TabFrame) {
uninstallAccelerator((TabFrame) e.getOldValue());
}
if (e.getNewValue() instanceof TabFrame) {
installAccelerator((TabFrame) e.getNewValue());
}
}
}
protected void updateText() {
var title = tabComponent.getTitle();
title = title == null ? "" : title;
@ -218,7 +237,7 @@ public class DarkTabFrameTabLabelUI extends DarkLabelUI implements PropertyChang
}
}
protected void uninstallAccelerator(final TabFrame tabFrame) {
protected void uninstallAccelerator(final JTabFrame tabFrame) {
if (tabFrame == null) return;
int acc = tabComponent.getAccelerator();
String accAction = "accelerator_" + acc;
@ -226,6 +245,7 @@ public class DarkTabFrameTabLabelUI extends DarkLabelUI implements PropertyChang
}
public Color getBackground(@NotNull final TabFrameTabLabel tab) {
if (printing || tab.getTabFrame().isInTransfer()) return tab.getBackground();
return tab.isSelected()
? selectedColor
: hoverListener.isHover() ? hoverColor : tab.getBackground();

437
src/main/java/com/weis/darklaf/ui/tabframe/DarkTabFrameUI.java

@ -23,11 +23,14 @@
*/
package com.weis.darklaf.ui.tabframe;
import com.weis.darklaf.components.alignment.Alignment;
import com.weis.darklaf.components.border.MutableLineBorder;
import com.weis.darklaf.components.tabframe.JTabFrame;
import com.weis.darklaf.components.tabframe.PopupContainer;
import com.weis.darklaf.components.tabframe.TabFrame;
import com.weis.darklaf.components.tabframe.TabFramePopup;
import com.weis.darklaf.components.tabframe.TabFrameTab;
import com.weis.darklaf.components.tabframe.TabFrameUI;
import com.weis.darklaf.components.uiresource.JPanelUIResource;
import com.weis.darklaf.util.DarkUIUtil;
import org.jdesktop.jxlayer.JXLayer;
import org.jetbrains.annotations.Contract;
@ -40,27 +43,39 @@ import javax.swing.plaf.ComponentUI;
import java.awt.*;
import java.awt.event.AWTEventListener;
import java.awt.event.MouseEvent;
import java.util.TooManyListenersException;
/**
* UI class for {@link TabFrame}.
* UI class for {@link JTabFrame}.
*
* @author Jannis Weis
* @since 2018
*/
public class DarkTabFrameUI extends TabFrameUI implements AWTEventListener {
private TabFrame tabFrame;
protected static final TabFrameTransferHandler TRANSFER_HANDLER = new TabFrameTransferHandler.UIResource();
private final Rectangle calcRect = new Rectangle();
private JXLayer<JComponent> rotatePaneLeft;
private JXLayer<JComponent> rotatePaneRight;
private LayoutManager layout;
private JTabFrame tabFrame;
private JComponent dropComponentTop;
private JComponent dropComponentBottom;
private JComponent dropComponentRight;
private JComponent dropComponentLeft;
private MutableLineBorder topBorder;
private MutableLineBorder bottomBorder;
private MutableLineBorder leftBorder;
private MutableLineBorder rightBorder;
private Color lineColor;
private TabFrameLayout layout;
private int tabHeight;
private Color dragBorderColor;
private Dimension dropSize = new Dimension();
private Alignment sourceAlign;
private int sourceIndex;
private Alignment destAlign;
private int destIndex;
@NotNull
@Contract("_ -> new")
@ -70,10 +85,11 @@ public class DarkTabFrameUI extends TabFrameUI implements AWTEventListener {
@Override
public void installUI(@NotNull final JComponent c) {
tabFrame = (TabFrame) c;
tabFrame = (JTabFrame) c;
installDefaults();
installComponents();
installListeners();
installDnD();
}
protected void installDefaults() {
@ -86,6 +102,19 @@ public class DarkTabFrameUI extends TabFrameUI implements AWTEventListener {
rightBorder = new MutableLineBorder.UIResource(0, 0, 1, 0, lineColor);
leftBorder = new MutableLineBorder.UIResource(0, 0, 1, 0, lineColor);
dropComponentTop = new JPanelUIResource();
dropComponentBottom = new JPanelUIResource();
dropComponentLeft = new JPanelUIResource();
dropComponentRight = new JPanelUIResource();
dragBorderColor = UIManager.getColor("TabFrame.dragBorderColor");
Color dropColor = UIManager.getColor("TabFrame.dropBackground");
dropComponentTop.setBackground(dropColor);
dropComponentBottom.setBackground(dropColor);
dropComponentLeft.setBackground(dropColor);
dropComponentRight.setBackground(dropColor);
tabFrame.getTopTabContainer().setBorder(topBorder);
tabFrame.getBottomTabContainer().setBorder(bottomBorder);
tabFrame.getRightTabContainer().setBorder(rightBorder);
@ -107,13 +136,28 @@ public class DarkTabFrameUI extends TabFrameUI implements AWTEventListener {
tabFrame.add(tabFrame.getBottomTabContainer());
tabFrame.add(rotatePaneLeft);
tabFrame.add(rotatePaneRight);
tabFrame.getTopTabContainer().add(dropComponentTop);
tabFrame.getBottomTabContainer().add(dropComponentBottom);
tabFrame.getLeftTabContainer().add(dropComponentLeft);
tabFrame.getRightTabContainer().add(dropComponentRight);
}
protected void installDnD() {
tabFrame.setTransferHandler(TRANSFER_HANDLER);
try {
tabFrame.getDropTarget().addDropTargetListener(TRANSFER_HANDLER);
tabFrame.getDropTarget().setActive(tabFrame.isDndEnabled());
} catch (TooManyListenersException e) {
e.printStackTrace();
}
}
protected void installListeners() {
Toolkit.getDefaultToolkit().addAWTEventListener(this, AWTEvent.MOUSE_EVENT_MASK);
}
protected LayoutManager createLayout() {
protected TabFrameLayout createLayout() {
return new TabFrameLayout(tabFrame, this);
}
@ -132,22 +176,344 @@ public class DarkTabFrameUI extends TabFrameUI implements AWTEventListener {
rotatePaneLeft = null;
rotatePaneRight = null;
uninstallListeners();
if (tabFrame.getTransferHandler() instanceof TabFrameTransferHandler.UIResource) {
tabFrame.setTransferHandler(null);
if (tabFrame.getDropTarget() != null) {
tabFrame.getDropTarget().removeDropTargetListener(TRANSFER_HANDLER);
tabFrame.getDropTarget().setActive(false);
}
}
}
protected void uninstallListeners() {
Toolkit.getDefaultToolkit().removeAWTEventListener(this);
}
public void updateBorders(final int topCount, final int bottomCount,
final int leftCount, final int rightCount) {
leftBorder.setRight(topCount > 0 ? 0 : 1);
leftBorder.setLeft(bottomCount > 0 ? 0 : 1);
rightBorder.setLeft(topCount > 0 ? 0 : 1);
rightBorder.setRight(bottomCount > 0 ? 0 : 1);
public int getTabSize(final JTabFrame tabFrame) {
return tabHeight;
}
public int getTabSize(final TabFrame tabFrame) {
return tabHeight;
@Override
public void clearTargetIndicator() {
destIndex = -10;
destAlign = null;
dropSize.setSize(0, 0);
tabFrame.doLayout();
}
@Override
public void clearSourceIndicator() {
sourceIndex = -10;
sourceAlign = null;
tabFrame.doLayout();
}
@Override
public Color getDragBorderColor() {
return dragBorderColor;
}
@Override
public void setSourceIndicator(final Alignment a, final int tabIndex) {
sourceAlign = a;
sourceIndex = tabIndex;
tabFrame.doLayout();
}
@Override
public void setTargetIndicator(final Alignment a, final int tabIndex) {
destAlign = a;
destIndex = tabIndex;
tabFrame.doLayout();
}
@Override
public JTabFrame.TabFramePosition getTabIndexAt(final JTabFrame tabFrame, @NotNull final Point p) {
Component tabComp = null;
Alignment a = null;
Point pos = null;
if (!layout.isDraggedOver(Alignment.NORTH)) {
getTopContainer().getBounds(calcRect);
if (p.y >= calcRect.y && p.y <= calcRect.y + calcRect.height) {
tabComp = getTopContainer();
a = Alignment.NORTH;
pos = SwingUtilities.convertPoint(tabFrame, p, tabComp);
if (pos.x > tabComp.getWidth() / 2) {
a = Alignment.NORTH_EAST;
}
}
}
if (tabComp == null && !layout.isDraggedOver(Alignment.SOUTH)) {
getBottomContainer().getBounds(calcRect);
if (p.y >= calcRect.y && p.y <= calcRect.y + calcRect.height) {
tabComp = getBottomContainer();
a = Alignment.SOUTH;
pos = SwingUtilities.convertPoint(tabFrame, p, tabComp);
if (pos.x <= tabComp.getWidth() / 2) {
a = Alignment.SOUTH_WEST;
}
}
}
if (tabComp == null && !layout.isDraggedOver(Alignment.WEST)) {
getLeftContainer().getBounds(calcRect);
if (p.x >= calcRect.x && p.x <= calcRect.x + calcRect.width) {
tabComp = getLeftContainer();
a = Alignment.WEST;
pos = SwingUtilities.convertPoint(tabFrame, p, tabComp);
if (pos.y < tabComp.getHeight() / 2) {
a = Alignment.NORTH_WEST;
}
int tmp = pos.x;
pos.x = tabComp.getHeight() - pos.y;
pos.y = tmp;
}
}
if (tabComp == null && !layout.isDraggedOver(Alignment.EAST)) {
getRightContainer().getBounds(calcRect);
if (p.x >= calcRect.x && p.x <= calcRect.x + calcRect.width) {
tabComp = getRightContainer();
a = Alignment.EAST;
pos = SwingUtilities.convertPoint(tabFrame, p, tabComp);
if (pos.y > tabComp.getHeight() / 2) {
a = Alignment.SOUTH_EAST;
}
int tmp = pos.x;
//noinspection SuspiciousNameCombination
pos.x = pos.y;
pos.y = tmp;
}
}
if (tabComp == null) {
var tab = maybeRestoreTabContainer(tabFrame, p);
if (tab.getAlignment() != null) {
return tab;
}
} else {
layout.setDraggedOver(false);
}
if (tabComp == null) {
return new JTabFrame.TabFramePosition(null, -1);
}
var tabs = tabFrame.tabsForAlignment(a);
for (var tab : tabs) {
var rect = getTabRect(tab, a, tabComp, true);
if (rect.contains(pos)) {
return new JTabFrame.TabFramePosition(a, tab.getIndex(), pos);
}
}
return new JTabFrame.TabFramePosition(a, -1, pos);
}
@Override
public JTabFrame.TabFramePosition getNearestTabIndexAt(final JTabFrame tabFrame, final Point pos) {
var tab = getTabIndexAt(tabFrame, pos);
if (tab.getAlignment() != null && tab.getIndex() == -1) {
var p = tab.getPoint();
var a = tab.getAlignment();
if (tabFrame.getTabCountAt(a) == 0) {
tab.setIndex(-1);
return tab;
}
int w = a == destAlign && destIndex == -1 ? dropSize.width : 0;
var comp = getTabContainer(a);
switch (a) {
case NORTH:
case SOUTH_WEST:
if (p.x > getLeftContainer().getWidth() + w) {
tab.setIndex(tabFrame.getTabCountAt(a) - 1);
}
break;
case NORTH_EAST:
case SOUTH:
if (p.x < comp.getWidth() - getRightContainer().getWidth() - w) {
tab.setIndex(tabFrame.getTabCountAt(a) - 1);
}
break;
case EAST:
case WEST:
if (p.x > w) {
tab.setIndex(tabFrame.getTabCountAt(a) - 1);
}
break;
case SOUTH_EAST:
case NORTH_WEST:
if (p.x < comp.getHeight() - w) {
tab.setIndex(tabFrame.getTabCountAt(a) - 1);
}
break;
}
}
return tab;
}
public void setDropSize(final int width, final int height) {
dropSize.setSize(width, height);
}
@Override
public int getTabWidth(@NotNull final JTabFrame tabFrame, final Alignment a, final int index) {
return layout.getTabWidth(tabFrame.getTabComponentAt(a, index).getComponent());
}
@Override
public int getTabHeight(@NotNull final JTabFrame tabFrame, final Alignment a, final int index) {
return tabFrame.getTabSize();
}
public Rectangle getTabContainerBounds(final JTabFrame tabFrame, @NotNull final Alignment a) {
switch (a) {
case NORTH:
case NORTH_EAST:
var rect = getTopContainer().getBounds();
rect.x = 0;
rect.width = tabFrame.getWidth();
return rect;
case EAST:
case SOUTH_EAST:
return getRightContainer().getBounds();
case SOUTH:
case SOUTH_WEST:
var rect2 = getTopContainer().getBounds();
rect2.x = 0;
rect2.width = tabFrame.getWidth();
return rect2;
case WEST:
case NORTH_WEST:
return getLeftContainer().getBounds();
default:
case CENTER:
return tabFrame.getContentPane().getComponent().getBounds();
}
}
public JTabFrame.TabFramePosition getDropPosition(final JTabFrame tabFrame, final Point p) {
var tab = getNearestTabIndexAt(tabFrame, p);
if (tab.getAlignment() != null) {
var a = tab.getAlignment();
int index = tab.getIndex();
if (index >= 0) {
var rect = getTabRect(tabFrame.getTabComponentAt(a, index), a, tabFrame.getTabContainer(a), false);
var pos = tab.getPoint();
if (isForward(a)) {
if (pos.x <= rect.x + rect.width / 2 && pos.x >= rect.x) {
tab.setIndex(tab.getIndex() - 1);
}
} else {
if (pos.x >= rect.x + rect.width / 2) {
tab.setIndex(tab.getIndex() - 1);
}
}
}
}
return tab;
}
public Alignment getSourceAlign() {
return sourceAlign;
}
public int getSourceIndex() {
return sourceIndex;
}
public Alignment getDestAlign() {
return destAlign;
}
public int getDestIndex() {
return destIndex;
}
protected JTabFrame.TabFramePosition maybeRestoreTabContainer(@NotNull final JTabFrame tabFrame, final Point p) {
Alignment a = null;
int size = tabFrame.getTabSize();
int threshold = size;
int index = -10;
if (tabFrame.getTopTabCount() == 0) {
if (layout.isDraggedOver(Alignment.NORTH)) threshold *= 2;
if (p.y < threshold) {
a = p.x >= tabFrame.getWidth() / 2 ? Alignment.NORTH_EAST : Alignment.NORTH;
}
if (p.y < size) index = -1;
}
if (a == null && tabFrame.getBottomTabCount() == 0) {
if (layout.isDraggedOver(Alignment.SOUTH)) threshold *= 2;
if (p.y > tabFrame.getHeight() - threshold) {
a = p.x >= tabFrame.getWidth() / 2 ? Alignment.SOUTH : Alignment.SOUTH_WEST;
}
if (p.y > tabFrame.getHeight() - size) index = -1;
}
if (a == null && tabFrame.getLeftTabCount() == 0) {
if (layout.isDraggedOver(Alignment.WEST)) threshold *= 2;
if (p.x < threshold) {
a = p.y >= tabFrame.getHeight() / 2 ? Alignment.WEST : Alignment.NORTH_WEST;
}
if (p.x < size) index = -1;
}
if (a == null && tabFrame.getRightTabCount() == 0) {
if (layout.isDraggedOver(Alignment.EAST)) threshold *= 2;
if (p.x > tabFrame.getWidth() - threshold) {
a = p.y >= tabFrame.getHeight() / 2 ? Alignment.SOUTH_EAST : Alignment.EAST;
}
if (p.x > tabFrame.getWidth() - size) index = -1;
}
layout.setDraggedOver(false);
if (a != null) {
layout.setDraggedOver(a, true);
}
return new JTabFrame.TabFramePosition(a, index);
}
protected Rectangle getTabRect(@NotNull final TabFrameTab tab, final Alignment a, final Component tabComp,
final boolean includeDropRect) {
tab.getComponent().getBounds(calcRect);
SwingUtilities.convertRectangle(tab.getComponent(), calcRect, tabComp);
if (includeDropRect && a == destAlign) {
if (tab.getIndex() == destIndex && destIndex >= 0) {
if (isForward(a)) {
calcRect.width += dropSize.width;
} else {
calcRect.x -= dropSize.width;
calcRect.width += dropSize.width;
}
}
}
return calcRect;
}
protected boolean isForward(@NotNull final Alignment a) {
switch (a) {
case NORTH:
case EAST:
case WEST:
case SOUTH_WEST:
return true;
case NORTH_WEST:
case SOUTH:
case NORTH_EAST:
case SOUTH_EAST:
default:
return false;
}
}
protected Component getTabContainer(@NotNull final Alignment a) {
switch (a) {
case NORTH:
case NORTH_EAST:
return getTopContainer();
case EAST:
case SOUTH_EAST:
return getRightContainer();
case SOUTH:
case SOUTH_WEST:
return getBottomContainer();
case WEST:
case NORTH_WEST:
return getLeftContainer();
}
return null;
}
public JComponent getLeftContainer() {
@ -181,10 +547,49 @@ public class DarkTabFrameUI extends TabFrameUI implements AWTEventListener {
parent2.getPopup().requestFocus();
return;
}
var parent3 = DarkUIUtil.getParentOfType(TabFrame.class, comp);
var parent3 = DarkUIUtil.getParentOfType(JTabFrame.class, comp);
if (parent3 != null && comp != null && !comp.hasFocus()) {
parent3.requestFocus();
}
}
}
public Dimension getDropSize() {
return dropSize;
}
public JComponent getDropComponent(@NotNull final Alignment a) {
switch (a) {
default:
case CENTER:
case NORTH:
case NORTH_EAST:
return getDropComponentTop();
case EAST:
case SOUTH_EAST:
return getDropComponentRight();
case SOUTH:
case SOUTH_WEST:
return getDropComponentBottom();
case WEST:
case NORTH_WEST:
return getDropComponentLeft();
}
}
public JComponent getDropComponentTop() {
return dropComponentTop;
}
public JComponent getDropComponentRight() {
return dropComponentRight;
}
public JComponent getDropComponentBottom() {
return dropComponentBottom;
}
public JComponent getDropComponentLeft() {
return dropComponentLeft;
}
}

2
src/main/java/com/weis/darklaf/ui/tabframe/DarkTabbedPopupUI.java

@ -23,9 +23,9 @@
*/
package com.weis.darklaf.ui.tabframe;
import com.weis.darklaf.components.JPanelUIResource;
import com.weis.darklaf.components.border.MutableLineBorder;
import com.weis.darklaf.components.tabframe.TabbedPopup;
import com.weis.darklaf.components.uiresource.JPanelUIResource;
import com.weis.darklaf.ui.tabbedpane.DarkTabbedPaneUI;
import com.weis.darklaf.ui.tabbedpane.MoreTabsButton;
import com.weis.darklaf.ui.tabbedpane.NewTabButton;

60
src/main/java/com/weis/darklaf/ui/tabframe/TabDragListener.java

@ -0,0 +1,60 @@
/*
* MIT License
*
* Copyright (c) 2019 Jannis Weis
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.weis.darklaf.ui.tabframe;
import com.weis.darklaf.components.tabframe.TabFrameTab;
import com.weis.darklaf.util.DarkUIUtil;
import com.weis.darklaf.util.SwingXUtilities;
import org.jdesktop.jxlayer.JXLayer;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
public class TabDragListener extends MouseAdapter {
private final TabFrameTab tabComponent;
public TabDragListener(final TabFrameTab tabComponent) {
this.tabComponent = tabComponent;
}
@Override
public void mouseDragged(@NotNull final MouseEvent e) {
var th = tabComponent.getTabFrame().getTransferHandler();
if (th != null && tabComponent.getTabFrame().isDndEnabled()) {
var p = e.getPoint();
p = SwingXUtilities.convertPointToParent(tabComponent.getComponent(), p);
JXLayer layer = DarkUIUtil.getParentOfType(JXLayer.class, tabComponent.getComponent());
p = SwingUtilities.convertPoint(layer != null ? layer : tabComponent.getComponent().getParent(),
p, tabComponent.getTabFrame());
tabComponent.getTabFrame().initTransfer(tabComponent.getOrientation(), tabComponent.getIndex());
th.exportAsDrag(tabComponent.getTabFrame(),
new MouseEvent(tabComponent.getTabFrame(), e.getID(), e.getWhen(), e.getModifiersEx(),
p.x, p.y, e.getClickCount(), e.isPopupTrigger(), e.getButton()),
TransferHandler.MOVE);
}
}
}

232
src/main/java/com/weis/darklaf/ui/tabframe/TabFrameLayout.java

@ -24,11 +24,12 @@
package com.weis.darklaf.ui.tabframe;
import com.weis.darklaf.components.alignment.Alignment;
import com.weis.darklaf.components.tabframe.TabFrame;
import com.weis.darklaf.components.tabframe.JTabFrame;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import java.awt.*;
import java.util.Arrays;
/**
* @author Jannis Weis
@ -36,15 +37,21 @@ import java.awt.*;
*/
public class TabFrameLayout implements LayoutManager {
private final TabFrame tabFrame;
private final JTabFrame tabFrame;
private DarkTabFrameUI ui;
private int maxTabHeight;
private int maxTabWidth;
private int[] shift;
private boolean[] draggedOver;
private int topHeight;
private int leftHeight;
private int rightHeight;
private int bottomHeight;
@Contract(pure = true)
public TabFrameLayout(@NotNull final TabFrame tabFrame, final DarkTabFrameUI ui) {
public TabFrameLayout(@NotNull final JTabFrame tabFrame, final DarkTabFrameUI ui) {
this.tabFrame = tabFrame;
this.ui = ui;
shift = new int[4];
draggedOver = new boolean[4];
}
@Override
@ -82,13 +89,27 @@ public class TabFrameLayout implements LayoutManager {
int bottomSize = tabFrame.getBottomTabCount();
int leftSize = tabFrame.getLeftTabCount();
int rightSize = tabFrame.getRightTabCount();
if (isDraggedOver(Alignment.NORTH)) topSize++;
if (isDraggedOver(Alignment.SOUTH)) bottomSize++;
if (isDraggedOver(Alignment.EAST)) rightSize++;
if (isDraggedOver(Alignment.WEST)) leftSize++;
ui.getDropComponentBottom().setSize(0, 0);
ui.getDropComponentLeft().setSize(0, 0);
ui.getDropComponentRight().setSize(0, 0);
ui.getDropComponentTop().setSize(0, 0);
topHeight = calculateMaxTabSize(Alignment.NORTH);
bottomHeight = calculateMaxTabSize(Alignment.SOUTH);
leftHeight = calculateMaxTabSize(Alignment.WEST);
rightHeight = calculateMaxTabSize(Alignment.EAST);
layoutTopTab(dim, topSize, leftSize, rightSize);
layoutBottomTab(dim, bottomSize, leftSize, rightSize);
layoutLeftTab(dim, leftSize);
layoutRightTab(dim, rightSize);
ui.updateBorders(topSize, bottomSize, leftSize, rightSize);
var leftPane = ui.getLeftContainer();
var rightPane = ui.getRightContainer();
var topPane = ui.getTopContainer();
@ -99,33 +120,63 @@ public class TabFrameLayout implements LayoutManager {
}
protected void layoutTopTab(final Dimension dim, final int topSize, final int leftSize, final int rightSize) {
var topComp = tabFrame.getTopTabContainer();
if (topSize > 0) {
tabFrame.getTopTabContainer().setBounds(0, 0, dim.width, tabFrame.getTabSize());
layoutHorizontal(dim, Alignment.NORTH, Alignment.NORTH_EAST, 0, leftSize, rightSize);
topComp.setBounds(0, 0, dim.width, tabFrame.getTabSize());
layoutHorizontal(dim, Alignment.NORTH, Alignment.NORTH_EAST, 0, leftSize, rightSize, topHeight);
} else if (draggedOver[getIndex(Alignment.NORTH)]) {
int size = tabFrame.getTabSize();
topComp.setBounds(0, 0, dim.width, size);
if (ui.getDestIndex() >= -1) {
layoutHorizontalDrop(Alignment.NORTH, leftSize, rightSize, size, 0);
}
} else {
tabFrame.getTopTabContainer().setBounds(0, 0, 0, 0);
topComp.setBounds(0, 0, 0, 0);
}
}
protected void layoutBottomTab(final Dimension dim, final int bottomSize, final int leftSize, final int rightSize) {
var bottomComp = tabFrame.getBottomTabContainer();
if (bottomSize > 0) {
bottomComp.setBounds(0, dim.height - bottomHeight, dim.width, bottomHeight);
layoutHorizontal(dim, Alignment.SOUTH_WEST, Alignment.SOUTH, 1, leftSize, rightSize, bottomHeight);
} else if (draggedOver[getIndex(Alignment.SOUTH)]) {
int size = tabFrame.getTabSize();
tabFrame.getBottomTabContainer().setBounds(0, dim.height - size, dim.width, size);
layoutHorizontal(dim, Alignment.SOUTH_WEST, Alignment.SOUTH, 1, leftSize, rightSize);
bottomComp.setBounds(0, dim.height - size, dim.width, size);
if (ui.getDestIndex() >= -1) {
layoutHorizontalDrop(Alignment.SOUTH_WEST, leftSize, rightSize, size, 1);
}
} else {
bottomComp.setBounds(0, 0, 0, 0);
}
}
protected void layoutHorizontalDrop(final Alignment left, final int leftSize, final int rightSize,
final int size, final int yOff) {
var a = ui.getDestAlign();
var dropSize = ui.getDropSize();
var dropComp = ui.getDropComponent(left);
var tabComp = ui.getTabContainer(left);
if (a == left) {
int x = leftSize > 0 ? leftHeight : 0;
dropComp.setBounds(x, yOff, dropSize.width, size);
} else {
tabFrame.getBottomTabContainer().setBounds(0, 0, 0, 0);
int x = rightSize > 0 ? tabComp.getWidth() - rightHeight : tabComp.getWidth();
dropComp.setBounds(x - dropSize.width, yOff, dropSize.width, size);
}
}
protected void layoutHorizontal(final Dimension dim, final Alignment left, final Alignment right,
final int yOff, final int leftSize, final int rightSize) {
int tabHeight = calculateMaxTabSize(left);
var start = new Point(leftSize > 0 ? tabHeight : 0, yOff);
final int yOff, final int leftSize, final int rightSize, final int tabHeight) {
var start = new Point(leftSize > 0 ? leftHeight : 0, yOff);
int leftEnd = layoutTabArea(start, left, true, tabHeight - 1);
start.x = rightSize > 0 ? dim.width - tabHeight : dim.width;
start.x = rightSize > 0 ? dim.width - rightHeight : dim.width;
int rightStart = layoutTabArea(start, right, false, tabHeight - 1);
if (rightStart < leftEnd) {
shift[getIndex(left)] = leftEnd - rightStart;
shift(leftEnd - rightStart, right);
} else {
shift[getIndex(left)] = 0;
}
}
@ -133,19 +184,26 @@ public class TabFrameLayout implements LayoutManager {
var leftPane = ui.getLeftContainer();
var topPane = tabFrame.getTopTabContainer();
var bottomPane = tabFrame.getBottomTabContainer();
int tabWidth = calculateMaxTabSize(Alignment.WEST);
if (leftSize > 0) {
if (leftSize > 0 || draggedOver[getIndex(Alignment.WEST)]) {
int size = leftSize > 0 ? leftHeight : tabFrame.getTabSize();
int height = dim.height - topPane.getHeight() - bottomPane.getHeight();
leftPane.setBounds(0, topPane.getHeight(), tabWidth, height + (height % 2));
leftPane.setBounds(0, topPane.getHeight(), size, height + (height % 2));
tabFrame.getLeftTabContainer().setPreferredSize(new Dimension(leftPane.getHeight(),
leftPane.getWidth()));
tabFrame.getLeftTabContainer().setSize(tabFrame.getLeftTabContainer().getPreferredSize());
var start = new Point(leftPane.getHeight(), 0);
int topStart = layoutTabArea(start, Alignment.NORTH_WEST, false, tabWidth - 1);
start.x = 0;
int bottomEnd = layoutTabArea(start, Alignment.WEST, true, tabWidth - 1);
if (bottomEnd > topStart) {
shift(topStart - bottomEnd, Alignment.WEST);
if (leftSize > 0) {
var start = new Point(leftPane.getHeight(), 0);
int topStart = layoutTabArea(start, Alignment.NORTH_WEST, false, size - 1);
start.x = 0;
int bottomEnd = layoutTabArea(start, Alignment.WEST, true, size - 1);
if (bottomEnd > topStart) {
shift[getIndex(Alignment.WEST)] = topStart - bottomEnd;
shift(topStart - bottomEnd, Alignment.WEST);
} else {
shift[getIndex(Alignment.WEST)] = 0;
}
} else if (ui.getDestIndex() >= -1) {
layoutVerticalDrop(Alignment.WEST, size);
}
} else {
tabFrame.getLeftTabContainer().setBounds(0, 0, 0, 0);
@ -153,26 +211,45 @@ public class TabFrameLayout implements LayoutManager {
}
}
protected void layoutVerticalDrop(final Alignment left, final int size) {
var comp = ui.getDropComponent(left);
var a = ui.getDestAlign();
var dropSize = ui.getDropSize();
var tabComp = tabFrame.getTabContainer(left);
if (a == left) {
comp.setBounds(0, 0, dropSize.width, size);
} else {
comp.setBounds(tabComp.getWidth() - dropSize.width, 0, dropSize.width, size);
}
}
protected void layoutRightTab(final Dimension dim, final int rightSize) {
var rightPane = ui.getRightContainer();
var topPane = tabFrame.getTopTabContainer();
var bottomPane = tabFrame.getBottomTabContainer();
int tabWidth = calculateMaxTabSize(Alignment.EAST);
if (rightSize > 0) {
if (rightSize > 0 || draggedOver[getIndex(Alignment.EAST)]) {
int size = rightSize > 0 ? rightHeight : tabFrame.getTabSize();
int height = dim.height - topPane.getHeight() - bottomPane.getHeight();
rightPane.setBounds(dim.width - tabWidth, topPane.getHeight(), tabWidth, height + (height % 2));
rightPane.setBounds(dim.width - rightHeight, topPane.getHeight(), size, height + (height % 2));
tabFrame.getRightTabContainer().setPreferredSize(new Dimension(rightPane.getHeight(), rightPane.getWidth()));
tabFrame.getRightTabContainer().setSize(tabFrame.getRightTabContainer().getPreferredSize());
var start = new Point(0, 0);
int topEnd = layoutTabArea(start, Alignment.EAST, true, tabWidth - 1);
start.x = tabFrame.getRightTabContainer().getWidth();
var bottomStart = layoutTabArea(start, Alignment.SOUTH_EAST, false, tabWidth - 1);
if (bottomStart < topEnd) {
shift(topEnd - bottomStart, Alignment.SOUTH_EAST);
if (rightSize > 0) {
var start = new Point(0, 0);
int topEnd = layoutTabArea(start, Alignment.EAST, true, size - 1);
start.x = tabFrame.getRightTabContainer().getWidth();
var bottomStart = layoutTabArea(start, Alignment.SOUTH_EAST, false, size - 1);
if (bottomStart < topEnd) {
shift[getIndex(Alignment.EAST)] = topEnd - bottomStart;
shift(topEnd - bottomStart, Alignment.SOUTH_EAST);
} else {
shift[getIndex(Alignment.EAST)] = 0;
}
} else if (ui.getDestIndex() >= -1) {
layoutVerticalDrop(Alignment.EAST, size);
}
} else {
tabFrame.getRightTabContainer().setBounds(0, 0, 0, 0);
topPane.setBounds(0, 0, 0, 0);
rightPane.setBounds(0, 0, 0, 0);
}
}
@ -182,31 +259,102 @@ public class TabFrameLayout implements LayoutManager {
pos.x += shift;
c.getComponent().setLocation(pos);
}
if (a == ui.getDestAlign()) {
var dropComp = ui.getDropComponent(a);
var pos = dropComp.getLocation();
pos.x += shift;
dropComp.setLocation(pos);
}
}
public int getShift(@NotNull final Alignment a) {
switch (a) {
case NORTH:
case EAST:
case SOUTH_WEST:
case NORTH_WEST:
return 0;
}
return shift[getIndex(a)];
}
protected int getIndex(@NotNull final Alignment a) {
switch (a) {
case NORTH:
case NORTH_EAST:
return 0;
case EAST:
case SOUTH_EAST:
return 1;
case SOUTH:
case SOUTH_WEST:
return 2;
case WEST:
case NORTH_WEST:
return 3;
}
return 0;
}
public void setDraggedOver(final Alignment a, final boolean b) {
draggedOver[getIndex(a)] = b;
}
protected int layoutTabArea(@NotNull final Point start, @NotNull final Alignment a,
final boolean forward, final int size) {
int x = start.x;
int y = start.y;
int sourceIndex = a == ui.getSourceAlign() ? ui.getSourceIndex() : -10;
int destIndex = a == ui.getDestAlign() ? ui.getDestIndex() : -10;
var bounds = new Rectangle(0, 0, 0, 0);
int index = 0;
var dropComp = ui.getDropComponent(a);
if (destIndex == -1) {
if (forward) {
dropComp.setBounds(x, y, ui.getDropSize().width, size);
x += ui.getDropSize().width;
} else {
x -= ui.getDropSize().width;
dropComp.setBounds(x, y, ui.getDropSize().width, size);
}
}
for (var c : tabFrame.tabsForAlignment(a)) {
bounds.width = getTabWidth(c.getComponent());
index = c.getIndex();
bounds.width = index == sourceIndex ? 0 : getTabWidth(c.getComponent());
bounds.height = size;
if (forward) {
bounds.x = x;
bounds.y = y;
x += bounds.width;
if (index == destIndex) {
dropComp.setBounds(x, y, ui.getDropSize().width, size);
x += ui.getDropSize().width;
}
} else {
x -= bounds.width;
bounds.x = x;
bounds.y = y;
if (index == destIndex) {
x -= ui.getDropSize().width;
dropComp.setBounds(x, y, ui.getDropSize().width, size);
}
}
c.getComponent().setBounds(bounds);
}
if (destIndex == index + 1) {
if (forward) {
dropComp.setBounds(x, y, ui.getDropSize().width, size);
x += ui.getDropSize().width;
} else {
x -= ui.getDropSize().width;
dropComp.setBounds(x, y, ui.getDropSize().width, size);
}
}
return x;
}
protected int getTabWidth(@NotNull final Component c) {
public int getTabWidth(@NotNull final Component c) {
int maxWidth = tabFrame.getMaxTabWidth();
int width = c.getPreferredSize().width;
if (maxWidth < 0) {
@ -226,4 +374,12 @@ public class TabFrameLayout implements LayoutManager {
}
return max;
}
public void setDraggedOver(final boolean b) {
Arrays.fill(draggedOver, b);
}
public boolean isDraggedOver(final Alignment a) {
return draggedOver[getIndex(a)];
}
}

508
src/main/java/com/weis/darklaf/ui/tabframe/TabFrameTransferHandler.java

@ -0,0 +1,508 @@
/*
* MIT License
*
* Copyright (c) 2019 Jannis Weis
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.weis.darklaf.ui.tabframe;
import com.weis.darklaf.components.alignment.Alignment;
import com.weis.darklaf.components.tabframe.JTabFrame;
import com.weis.darklaf.components.tabframe.TabFramePopup;
import com.weis.darklaf.components.tabframe.TabFrameTab;
import com.weis.darklaf.components.tabframe.TabFrameUI;
import com.weis.darklaf.util.ImageUtil;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.awt.*;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.DragGestureListener;
import java.awt.dnd.DragGestureRecognizer;
import java.awt.dnd.DragSource;
import java.awt.dnd.DragSourceContext;
import java.awt.dnd.DragSourceDragEvent;
import java.awt.dnd.DragSourceDropEvent;
import java.awt.dnd.DragSourceEvent;
import java.awt.dnd.DragSourceListener;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.awt.event.InputEvent;
import java.awt.event.MouseEvent;
/**
* @author Robert Futrell
* @author Jannis Weis
*/
public class TabFrameTransferHandler extends TransferHandler implements DropTargetListener, SwingConstants {
private static final String MIME_TYPE = DataFlavor.javaJVMLocalObjectMimeType
+ ";class=com.weis.darklaf.components.tabframe.TabFrame";
private static TabbedPaneDragGestureRecognizer recognizer = null;
private final Timer timer;
private final Timer startTimer;
/**
* The location of the mouse cursor throughout the drag-and-drop. This is here because of a deficiency in
* TransferHandler's design; you have no way of knowing the exact drop location in the component with a plain
* TransferHandler unless you implement DropTargetListener and get it that way.
*/
protected Point mouseLocation;
private DataFlavor tabFlavor;
private TabTransferable currentTransferable;
private JTabFrame lastTabFrame;
public TabFrameTransferHandler() {
try {
tabFlavor = new DataFlavor(MIME_TYPE);
} catch (ClassNotFoundException ignored) {
}
timer = new Timer(100, e -> {
if (lastTabFrame != null) {
var p = MouseInfo.getPointerInfo().getLocation();
SwingUtilities.convertPointFromScreen(p, lastTabFrame);
var evt = new DropTargetDragEvent(lastTabFrame.getDropTarget().getDropTargetContext(), p, MOVE, MOVE);
dragOver(evt);
}
});
timer.setRepeats(true);
startTimer = new Timer(200, e -> {
/*
* Drag Exit can be funky. Ensure that the timer is really running.
*/
if (!timer.isRunning()) {
timer.start();
}
});
startTimer.setRepeats(false);
}
@Contract("null -> null")
private TabFrameUI getUI(final Component c) {
if (c instanceof JTabFrame) return ((JTabFrame) c).getUI();
return null;
}
protected JTabFrame.TabFramePosition getDropPosition(final Point p, final JTabFrame tabFrame) {
return getUI(tabFrame).getDropPosition(tabFrame, p);
}
public void exportAsDrag(final JComponent comp, final InputEvent e, final int a) {
int srcActions = getSourceActions(comp);
int action = a;
// only mouse events supported for drag operations
if (!(e instanceof MouseEvent)
// only support known actions
|| !(action == COPY || action == MOVE || action == LINK)
// only support valid source actions
|| (srcActions & action) == 0) {
action = NONE;
}
if (action != NONE && !GraphicsEnvironment.isHeadless()) {
if (recognizer == null) {
recognizer = new TabbedPaneDragGestureRecognizer(new TabbedPaneDragHandler());
}
recognizer.gestured(comp, (MouseEvent) e, srcActions, action);
} else {
exportDone(comp, null, NONE);
}
}
/**
* Called when the drag-and-drop operation has just completed. This creates a new tab identical to the one
* "dragged" and places it in the destination <code>JTabbedPane</code>.
*
* @param c The component receiving the "drop" (the instance of
* <code>JTabbedPane</code>).
* @param t The data being transfered (information about the tab and the component contained by the tab).
* @return Whether or not the import was successful.
*/
@Override
public boolean importData(final JComponent c, @NotNull final Transferable t) {
boolean successful = false;
if (hasTabFlavor(t.getTransferDataFlavors()) && mouseLocation != null) {
try {
JTabFrame tabFrame = (JTabFrame) c;
var tab = getDropPosition(mouseLocation, tabFrame);
Alignment a = tab.getAlignment();
int index = tab.getIndex();
TabTransferable.TabTransferData td = (TabTransferable.TabTransferData) t.getTransferData(tabFlavor);
if (tabFrame == td.sourceTabFrame && td.tabAlignment == a) {
if (index >= td.tabIndex) {
index--;
}
}
index++;
if (tabFrame == td.sourceTabFrame && a == td.tabAlignment && index == td.tabIndex) {
//Nothing to do. Just select the tab to be sure.
if (td.wasSelected) {
selectTab(td.sourceTabFrame, a, index);
}
return false;
}
if (a == null || index < 0 || index > tabFrame.getTabCountAt(a)) {
return false;
}
var tabComp = td.sourceTabFrame.getTabComponentAt(td.tabAlignment, td.tabIndex);
var popupComp = td.sourceTabFrame.getPopupComponentAt(td.tabAlignment, td.tabIndex);
td.sourceTabFrame.removeTab(td.tabAlignment, td.tabIndex);
tabFrame.insertTab((TabFramePopup) popupComp, tabComp, a, index);
tabFrame.toggleTab(a, index, td.wasSelected);
SwingUtilities.invokeLater(() -> td.tab.getComponent().repaint());
successful = true;
var ui = getUI(c);
if (ui != null) {
ui.clearTargetIndicator();
}
} catch (Exception e) {
e.printStackTrace();
}
}
return successful;
}
/**
* Overridden to include a check for a TabData flavor.
*/
@Override
public boolean canImport(final JComponent c, final DataFlavor[] flavors) {
return hasTabFlavor(flavors);
}
/**
* We can only move tabs, we cannot copy them.
*
* @param c This parameter is ignored.
* @return <code>TransferHandler.MOVE</code>, as we can only move tabs.
*/
@Override
public int getSourceActions(final JComponent c) {
return MOVE;
}
protected boolean hasTabFlavor(final DataFlavor[] flavors) {
if (tabFlavor == null) {
return false;
}
for (DataFlavor flavor : flavors) {
if (tabFlavor.equals(flavor)) {
return true;
}
}
return false;
}
protected void selectTab(final JTabFrame tabbedPane, final Alignment a, final int index) {
SwingUtilities.invokeLater(() -> tabbedPane.toggleTab(a, index, true));
}
protected Transferable createTransferable(final JComponent c, @NotNull final DragGestureEvent dge) {
JTabFrame tabFrame = (JTabFrame) c;
if (tabFrame.isInTransfer()) {
currentTransferable = new TabTransferable(tabFrame, tabFrame.getTransferInfo());
} else {
var ind = getUI(tabFrame).getTabIndexAt(tabFrame, dge.getDragOrigin());
tabFrame.initTransfer(ind.getAlignment(), ind.getIndex());
currentTransferable = new TabTransferable(tabFrame, ind);
}
var ui = getUI(c);
createDragImage(ui);
var a = currentTransferable.transferData.tabAlignment;
int index = currentTransferable.transferData.tabIndex;
ui.setSourceIndicator(a, index);
startTimer.start();
lastTabFrame = currentTransferable.transferData.sourceTabFrame;
return currentTransferable;
}
protected void createDragImage(@NotNull final TabFrameUI ui) {
var comp = currentTransferable.transferData.tab.getComponent();
Image tabImage = ImageUtil.scaledImageFromComponent(comp, new Rectangle(0, 0, comp.getWidth(),
comp.getHeight()));
int w = tabImage.getWidth(null);
int h = tabImage.getHeight(null);
var g = tabImage.getGraphics();
g.setColor(ui.getDragBorderColor());
int lw = 2;
g.fillRect(0, 0, w, lw);
g.fillRect(0, 0, lw, h);
g.fillRect(w - lw, 0, lw, h);
g.fillRect(0, h - lw, w, lw);
g.dispose();
setDragImageOffset(new Point(w / 2, h / 2));
setDragImage(tabImage);
}
@Override
public void dragEnter(final DropTargetDragEvent e) {
timer.stop();
startTimer.stop();
}
@Override
public void dragOver(@NotNull final DropTargetDragEvent e) {
e.getDropTargetContext().getComponent().setCursor(Cursor.getDefaultCursor());
mouseLocation = e.getLocation();
Component c = e.getDropTargetContext().getComponent();
JTabFrame destTabFrame = (JTabFrame) c;
var ui = getUI(destTabFrame);
if (ui != null) {
TabTransferable t = currentTransferable;
if (t != null) {
var tab = getDropPosition(mouseLocation, destTabFrame);
if (tab.getAlignment() == null) {
ui.clearTargetIndicator();
} else {
try {
var sourceTab = currentTransferable.transferData.sourceTabFrame;
var sourceIndex = currentTransferable.transferData.tabIndex;
var sourceAlign = currentTransferable.transferData.tabAlignment;
int w = getUI(sourceTab).getTabWidth(sourceTab, sourceAlign, sourceIndex);
int h = getUI(sourceTab).getTabHeight(sourceTab, sourceAlign, sourceIndex);
ui.setDropSize(w, h);
ui.setTargetIndicator(tab.getAlignment(), tab.getIndex());
} catch (IndexOutOfBoundsException ex) {
ui.clearTargetIndicator();
}
}
}
}
lastTabFrame = destTabFrame;
startTimer.restart();
}
@Override
public void dropActionChanged(final DropTargetDragEvent e) {
}
@Override
public void dragExit(@NotNull final DropTargetEvent e) {
Component c = e.getDropTargetContext().getComponent();
var ui = getUI(c);
if (ui != null) {
ui.clearTargetIndicator();
}
lastTabFrame = (JTabFrame) c;
startTimer.start();
}
@Override
public void drop(@NotNull final DropTargetDropEvent e) {
Component c = e.getDropTargetContext().getComponent();
var ui = getUI(c);
if (ui != null) {
ui.clearTargetIndicator();
}
timer.stop();
startTimer.stop();
}
protected static class TabbedPaneDragGestureRecognizer extends DragGestureRecognizer {
protected TabbedPaneDragGestureRecognizer(final DragGestureListener dgl) {
super(DragSource.getDefaultDragSource(), null, NONE, dgl);
}
void gestured(final JComponent c, final MouseEvent e, final int srcActions, final int action) {
setComponent(c);
setSourceActions(srcActions);
appendEvent(e);
fireDragGestureRecognized(action, e.getPoint());
}
/**
* register this DragGestureRecognizer's Listeners with the Component
*/
protected void registerListeners() {
}
/**
* unregister this DragGestureRecognizer's Listeners with the Component
* <p>
* subclasses must override this method
*/
protected void unregisterListeners() {
}
}
public static class UIResource extends TabFrameTransferHandler {
}
/**
* Transferable representing a tab from a tabbed pane and its contents.
*/
public class TabTransferable implements Transferable {
private final TabTransferData transferData;
public TabTransferable(@NotNull final JTabFrame tabFrame, @NotNull final JTabFrame.TabFramePosition ind) {
transferData = new TabTransferData(tabFrame, ind.getAlignment(), ind.getIndex());
}
public TabFrameTab getTab() {
return transferData.tab;
}
@Override
public DataFlavor[] getTransferDataFlavors() {
return new DataFlavor[]{tabFlavor};
}
@Override
public boolean isDataFlavorSupported(final DataFlavor flavor) {
return tabFlavor.equals(flavor);
}
@NotNull
@Override
public Object getTransferData(final DataFlavor flavor) throws UnsupportedFlavorException {
if (!isDataFlavorSupported(flavor)) {
throw new UnsupportedFlavorException(flavor);
}
return transferData;
}
/**
* The data remembered about the tab.
*/
public class TabTransferData {
private final JTabFrame sourceTabFrame;
private final int tabIndex;
private final Alignment tabAlignment;
private final TabFrameTab tab;
private final boolean wasSelected;
@Contract(pure = true)
public TabTransferData(@NotNull final JTabFrame tabbedPane, final Alignment tabAlignment,
final int tabIndex) {
this.sourceTabFrame = tabbedPane;
this.tabAlignment = tabAlignment;
this.tabIndex = tabIndex;
this.tab = tabbedPane.getTabComponentAt(tabAlignment, tabIndex);
this.wasSelected = tab.isSelected();
}
}
}
protected class TabbedPaneDragHandler implements DragGestureListener, DragSourceListener {
private boolean scrolls;
// --- DragGestureListener methods -----------------------------------
/**
* a Drag gesture has been recognized
*/
public void dragGestureRecognized(@NotNull final DragGestureEvent dge) {
JComponent c = (JComponent) dge.getComponent();
TabFrameTransferHandler th = (TabFrameTransferHandler) c.getTransferHandler();
Transferable t = th.createTransferable(c, dge);
if (t != null) {
scrolls = c.getAutoscrolls();
c.setAutoscrolls(false);
try {
Image im = th.getDragImage();
if (im == null) {
dge.startDrag(Cursor.getDefaultCursor(), t, this);
} else {
dge.startDrag(Cursor.getDefaultCursor(), im, th.getDragImageOffset(), t, this);
}
return;
} catch (RuntimeException re) {
c.setAutoscrolls(scrolls);
}
}
th.exportDone(c, t, NONE);
}
// --- DragSourceListener methods -----------------------------------
/**
* as the hotspot enters a platform dependent drop site
*/
public void dragEnter(final DragSourceDragEvent dsde) {
}
/**
* as the hotspot moves over a platform dependent drop site
*/
public void dragOver(final DragSourceDragEvent dsde) {
}
public void dropActionChanged(final DragSourceDragEvent dsde) {
}
/**
* as the hotspot exits a platform dependent drop site
*/
public void dragExit(final DragSourceEvent dsde) {
}
/**
* as the operation completes
*/
public void dragDropEnd(@NotNull final DragSourceDropEvent dsde) {
DragSourceContext dsc = dsde.getDragSourceContext();
JComponent c = (JComponent) dsc.getComponent();
if (dsde.getDropSuccess()) {
((TabFrameTransferHandler) c.getTransferHandler()).exportDone(c, dsc.getTransferable(),
dsde.getDropAction());
} else {
((TabFrameTransferHandler) c.getTransferHandler()).exportDone(c, dsc.getTransferable(), NONE);
}
c.setAutoscrolls(scrolls);
var ui = getUI(currentTransferable.transferData.sourceTabFrame);
if (ui != null) {
ui.clearSourceIndicator();
}
if (!dsde.getDropSuccess() && currentTransferable.transferData.wasSelected) {
selectTab(currentTransferable.transferData.sourceTabFrame,
currentTransferable.transferData.tabAlignment,
currentTransferable.transferData.tabIndex);
}
currentTransferable.transferData.sourceTabFrame.endTransfer();
currentTransferable = null;
}
}
}

3
src/main/java/com/weis/darklaf/ui/tooltip/DarkTooltipBorder.java

@ -55,8 +55,7 @@ public class DarkTooltipBorder implements Border, UIResource {
return;
}
if (c instanceof JToolTip && ((JToolTip) c).getTipText() == null) return;
g.setColor(Color.RED);
g.drawRect(x, y, width, height);
System.out.println(bubbleBorder.getPointerSide());
var ins = shadowBorder.getBorderInsets(c);
adjustInsets(ins);
var bubbleArea = bubbleBorder.getInnerArea(x + ins.left, y + ins.top,

35
src/main/java/com/weis/darklaf/util/PropertyLoader.java

@ -40,6 +40,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@ -62,13 +63,15 @@ public final class PropertyLoader {
private static final Collection<ObjectRequest> objectsToLoad = new HashSet<>();
public static void finish() {
var cache = new HashMap<String, Object>();
for (var request : objectsToLoad) {
try {
request.resolve();
request.resolve(cache);
} catch (RuntimeException e) {
LOGGER.log(Level.SEVERE, "Could not load" + request, e.getMessage());
}
}
cache.clear();
reset();
}
@ -268,20 +271,26 @@ public final class PropertyLoader {
this.value = value;
}
private void resolve() {
Object obj = parseObject(value);
private void resolve(@NotNull final Map<String, Object> cache) {
var defaults = UIManager.getLookAndFeelDefaults();
if (obj == null) {
obj = parseValue(key, value, true, defaults);
if (obj instanceof ObjectRequest) {
LOGGER.severe("Failed to resolve object. " + this);
return;
}
}
if (obj == null) {
defaults.remove(key);
if (cache.containsKey(value)) {
defaults.put(key, cache.get(value));
} else {
defaults.put(key, obj);
Object obj = parseObject(value);
if (obj == null) {
obj = parseValue(key, value, true, defaults);
if (obj instanceof ObjectRequest) {
LOGGER.severe("Failed to resolve object. " + this);
return;
}
} else {
cache.put(value, obj);
}
if (obj == null) {
defaults.remove(key);
} else {
defaults.put(key, obj);
}
}
}

48
src/main/java/com/weis/darklaf/util/SwingXUtilities.java

@ -0,0 +1,48 @@
/*
* MIT License
*
* Copyright (c) 2019 Jannis Weis
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.weis.darklaf.util;
import org.jdesktop.jxlayer.JXLayer;
import org.jetbrains.annotations.NotNull;
import org.pbjar.jxlayer.plaf.ext.TransformUI;
import javax.swing.*;
import java.awt.*;
public class SwingXUtilities {
@NotNull
public static Point convertPointToParent(final Component source, final Point p) {
JXLayer layer = DarkUIUtil.getParentOfType(JXLayer.class, source);
if (layer != null && layer.getUI() instanceof TransformUI) {
var ui = (TransformUI) layer.getUI();
var pos = SwingUtilities.convertPoint(source, p, layer);
//noinspection unchecked
var transform = ui.getPreferredTransform(layer.getSize(), layer);
transform.transform(pos, pos);
return pos;
}
return SwingUtilities.convertPoint(source, p, source.getParent());
}
}

10
src/main/java/org/pbjar/jxlayer/plaf/ext/MouseEventUI.java

@ -244,16 +244,6 @@ public class MouseEventUI<V extends JComponent> extends AbstractLayerUI<V> {
if (mouseEvent != null) {
Component target = mouseEvent.getComponent();
target.dispatchEvent(mouseEvent);
/*
* Used to check the re dispatching behavior
*/
// switch (mouseEvent.getID()) {
// case (MouseEvent.MOUSE_PRESSED):
// System.out.println();
// case (MouseEvent.MOUSE_RELEASED):
// case (MouseEvent.MOUSE_CLICKED):
// System.out.println("Dispatched mouse event " + mouseEvent);
// }
}
}

1
src/main/java/org/pbjar/jxlayer/repaint/RepaintManagerUtils.java

@ -133,7 +133,6 @@ public final class RepaintManagerUtils {
@NotNull final Class<? extends RepaintManager> clazz, final RepaintManager delegate) {
try {
RepaintManager newManager = clazz.getConstructor(RepaintManager.class).newInstance(delegate);
System.out.println("Created " + newManager.getClass().getName());
return newManager;
} catch (Throwable t) {
throw new RuntimeException("Cannot instantiate " + clazz.getName(), t);

2
src/main/resources/com/weis/darklaf/properties/ui/tabFrame.properties

@ -59,6 +59,8 @@ TabFrameUI = com.weis.darklaf.ui.tabframe.
TabFrame.line = %borderSecondary
TabFrame.tabHeight = 24
TabFrame.acceleratorKeyCode = alt pressed
TabFrame.dropBackground = %dropBackground
TabFrame.dragBorderColor = %borderTertiary
#Icons
TabFramePopup.close.icon = navigation/collapse.svg[aware]

2
src/main/resources/com/weis/darklaf/theme/intellij/intellij_defaults.properties

@ -38,7 +38,7 @@
%backgroundHoverColorful = CCCFD5
%backgroundSelectedColorful = c5cddc
%dropBackground = F7F6F0
%dropBackground = FCFAED
%dropForeground = C0C0C0
####Border####

5
src/test/java/TabFrameDemo.java

@ -1,7 +1,7 @@
import com.weis.darklaf.LafManager;
import com.weis.darklaf.components.OverlayScrollPane;
import com.weis.darklaf.components.alignment.Alignment;
import com.weis.darklaf.components.tabframe.TabFrame;
import com.weis.darklaf.components.tabframe.JTabFrame;
import com.weis.darklaf.components.tabframe.TabbedPopup;
import com.weis.darklaf.icons.IconLoader;
@ -42,7 +42,7 @@ public class TabFrameDemo {
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
var tabFrame = new TabFrame();
var tabFrame = new JTabFrame();
for (var o : Alignment.values()) {
if (o != Alignment.CENTER) {
for (int i = 0; i < 2; i++) {
@ -61,7 +61,6 @@ public class TabFrameDemo {
panel.add(label);
tabbedPopup.getTabbedPane().addTab("Tab " + i, panel);
}
/* Activate for custom tab demo.
tabFrame.setUserTabComponentAt(new JLabel("NORTH (custom tab)") {{
setBorder(new EmptyBorder(0, 5, 0, 5));

2
src/test/java/ToolBarDemo.java

@ -37,7 +37,6 @@
*/
import com.weis.darklaf.LafManager;
import com.weis.darklaf.theme.Theme;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
@ -109,7 +108,6 @@ public class ToolBarDemo extends JPanel implements ActionListener {
button.setIcon(new ImageIcon(imageURL, altText));
} else { //no image found
button.setText(altText);
System.err.println("Resource not found: " + imgLocation);
}
return button;

13
src/test/java/UIDemo.java

@ -1,5 +1,6 @@
import com.weis.darklaf.LafManager;
import com.weis.darklaf.components.TextFieldHistory;
import com.weis.darklaf.components.text.SearchTextField;
import com.weis.darklaf.components.text.SearchTextFieldWithHistory;
import com.weis.darklaf.components.tristate.TristateCheckBox;
import com.weis.darklaf.icons.IconLoader;
import org.jdesktop.swingx.JXStatusBar;
@ -141,14 +142,8 @@ public final class UIDemo {
add(new JTextField("TextField") {{
putClientProperty("JTextField.alternativeArc", Boolean.TRUE);
}});
add(new JTextField("SearchField") {{
putClientProperty("JTextField.variant", "search");
}});
add(new JTextField("SearchFieldWithHistory") {{
putClientProperty("JTextField.variant", "search");
putClientProperty("JTextField.Search.FindPopup",
new TextFieldHistory(this, 50, 100));
}});
add(new SearchTextField("SearchField"));
add(new SearchTextFieldWithHistory("SearchFieldWithHistory"));
add(new JPasswordField("Password"));
add(new JPasswordField("VeryStrongPassword") {{
putClientProperty("JTextField.alternativeArc", Boolean.TRUE);

Loading…
Cancel
Save