mirror of https://github.com/weisJ/darklaf.git
weisj
5 years ago
72 changed files with 8435 additions and 516 deletions
@ -0,0 +1,104 @@ |
|||||||
|
/* |
||||||
|
* 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 org.jetbrains.annotations.Contract; |
||||||
|
import org.jetbrains.annotations.NotNull; |
||||||
|
|
||||||
|
public final class Insets2D implements Cloneable { |
||||||
|
|
||||||
|
public double top; |
||||||
|
public double left; |
||||||
|
public double bottom; |
||||||
|
public double right; |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates and initializes a new <code>Insets</code> object with the |
||||||
|
* specified top, left, bottom, and right insets. |
||||||
|
* |
||||||
|
* @param top the inset from the top. |
||||||
|
* @param left the inset from the left. |
||||||
|
* @param bottom the inset from the bottom. |
||||||
|
* @param right the inset from the right. |
||||||
|
*/ |
||||||
|
@Contract(pure = true) |
||||||
|
public Insets2D(final double top, final double left, final double bottom, final double right) { |
||||||
|
this.top = top; |
||||||
|
this.left = left; |
||||||
|
this.bottom = bottom; |
||||||
|
this.right = right; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set top, left, bottom, and right to the specified values |
||||||
|
* |
||||||
|
* @param top the inset from the top. |
||||||
|
* @param left the inset from the left. |
||||||
|
* @param bottom the inset from the bottom. |
||||||
|
* @param right the inset from the right. |
||||||
|
* @since 1.5 |
||||||
|
*/ |
||||||
|
public void set(final double top, final double left, final double bottom, final double right) { |
||||||
|
this.top = top; |
||||||
|
this.left = left; |
||||||
|
this.bottom = bottom; |
||||||
|
this.right = right; |
||||||
|
} |
||||||
|
|
||||||
|
@Contract(pure = true) |
||||||
|
@Override |
||||||
|
public int hashCode() { |
||||||
|
double sum1 = left + bottom; |
||||||
|
double sum2 = right + top; |
||||||
|
double val1 = sum1 * (sum1 + 1) / 2 + left; |
||||||
|
double val2 = sum2 * (sum2 + 1) / 2 + top; |
||||||
|
double sum3 = val1 + val2; |
||||||
|
return (int) (sum3 * (sum3 + 1) / 2 + val2); |
||||||
|
} |
||||||
|
|
||||||
|
@Contract(value = "null -> false", pure = true) |
||||||
|
@Override |
||||||
|
public boolean equals(final Object obj) { |
||||||
|
if (obj instanceof Insets2D) { |
||||||
|
Insets2D insets = (Insets2D) obj; |
||||||
|
return ((top == insets.top) && (left == insets.left) && |
||||||
|
(bottom == insets.bottom) && (right == insets.right)); |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
@NotNull |
||||||
|
@Contract(value = " -> new", pure = true) |
||||||
|
@Override |
||||||
|
public Insets2D clone() { |
||||||
|
return new Insets2D(top, left, bottom, right); |
||||||
|
} |
||||||
|
|
||||||
|
@NotNull |
||||||
|
public String toString() { |
||||||
|
return getClass().getName() + "[top=" + top + ",left=" + left + ",bottom=" + bottom + ",right=" + right + "]"; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
@ -0,0 +1,54 @@ |
|||||||
|
/* |
||||||
|
* 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 javax.swing.*; |
||||||
|
import javax.swing.plaf.UIResource; |
||||||
|
|
||||||
|
public class JLabelUIResource extends JLabel implements UIResource { |
||||||
|
|
||||||
|
public JLabelUIResource(final String text, final int horizontalAlignment) { |
||||||
|
this(text, null, horizontalAlignment); |
||||||
|
} |
||||||
|
|
||||||
|
public JLabelUIResource(final String text, final Icon icon, final int horizontalAlignment) { |
||||||
|
super(text, icon, horizontalAlignment); |
||||||
|
} |
||||||
|
|
||||||
|
public JLabelUIResource(final String text) { |
||||||
|
this(text, null, LEADING); |
||||||
|
} |
||||||
|
|
||||||
|
public JLabelUIResource(final Icon image, final int horizontalAlignment) { |
||||||
|
this(null, image, horizontalAlignment); |
||||||
|
} |
||||||
|
|
||||||
|
public JLabelUIResource(final Icon image) { |
||||||
|
this(null, image, CENTER); |
||||||
|
} |
||||||
|
|
||||||
|
public JLabelUIResource() { |
||||||
|
this("", null, LEADING); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,47 @@ |
|||||||
|
/* |
||||||
|
* 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 javax.swing.*; |
||||||
|
import javax.swing.plaf.UIResource; |
||||||
|
import java.awt.*; |
||||||
|
|
||||||
|
public class JPanelUIResource extends JPanel implements UIResource { |
||||||
|
|
||||||
|
public JPanelUIResource(final LayoutManager layout) { |
||||||
|
this(layout, true); |
||||||
|
} |
||||||
|
|
||||||
|
public JPanelUIResource(final LayoutManager layout, final boolean isDoubleBuffered) { |
||||||
|
super(layout, isDoubleBuffered); |
||||||
|
} |
||||||
|
|
||||||
|
public JPanelUIResource() { |
||||||
|
this(true); |
||||||
|
} |
||||||
|
|
||||||
|
public JPanelUIResource(final boolean isDoubleBuffered) { |
||||||
|
this(new FlowLayout(), isDoubleBuffered); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,56 @@ |
|||||||
|
/* |
||||||
|
* 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 javax.swing.border.Border; |
||||||
|
import java.awt.*; |
||||||
|
|
||||||
|
public class UIResourceWrapper extends JPanelUIResource { |
||||||
|
|
||||||
|
public UIResourceWrapper(final Component component) { |
||||||
|
super(new BorderLayout()); |
||||||
|
super.setOpaque(false); |
||||||
|
super.setBorder(null); |
||||||
|
add(component, BorderLayout.CENTER); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setBorder(final Border border) { |
||||||
|
super.setBorder(null); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setOpaque(final boolean isOpaque) { |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean isOpaque() { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Border getBorder() { |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,213 @@ |
|||||||
|
/* |
||||||
|
* 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.tabframe; |
||||||
|
|
||||||
|
import com.weis.darklaf.components.alignment.Alignment; |
||||||
|
import org.jetbrains.annotations.Contract; |
||||||
|
|
||||||
|
import javax.swing.*; |
||||||
|
import java.awt.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* Popup Component for {@link TabFrame}. |
||||||
|
* |
||||||
|
* @author Jannis Weis |
||||||
|
* @since 2019 |
||||||
|
*/ |
||||||
|
public class PanelPopup extends JPanel implements TabFramePopup { |
||||||
|
|
||||||
|
private Component content; |
||||||
|
private boolean open; |
||||||
|
private TabFrame parent; |
||||||
|
private String title; |
||||||
|
private Icon icon; |
||||||
|
private Alignment alignment; |
||||||
|
private int index; |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new Popup that holds one component. |
||||||
|
* |
||||||
|
* @param title the title of the component. |
||||||
|
* @param content the content of the popup. |
||||||
|
*/ |
||||||
|
public PanelPopup(final String title, final Component content) { |
||||||
|
this(title, null, content); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new Popup that holds one component. |
||||||
|
* |
||||||
|
* @param title the title of the component. |
||||||
|
* @param icon the icon of the popup. |
||||||
|
* @param content the content of the popup. |
||||||
|
*/ |
||||||
|
public PanelPopup(final String title, final Icon icon, final Component content) { |
||||||
|
setIcon(icon); |
||||||
|
setTitle(title); |
||||||
|
setContentPane(content); |
||||||
|
setEnabled(false); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getUIClassID() { |
||||||
|
return "TabFramePanelPopupUI"; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Dimension getSize(final Dimension rv) { |
||||||
|
if (!isEnabled()) { |
||||||
|
return new Dimension(0, 0); |
||||||
|
} |
||||||
|
return super.getSize(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int getWidth() { |
||||||
|
if (!isEnabled()) { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
return super.getWidth(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int getHeight() { |
||||||
|
if (!isEnabled()) { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
return super.getHeight(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Component getContentPane() { |
||||||
|
if (content == null) { |
||||||
|
setContentPane(null); |
||||||
|
} |
||||||
|
return content; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setContentPane(final Component component) { |
||||||
|
var old = this.content; |
||||||
|
this.content = component; |
||||||
|
if (content == null) { |
||||||
|
content = new JPanel(); |
||||||
|
} |
||||||
|
firePropertyChange("content", old, content); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Component getComponent() { |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void close() { |
||||||
|
if (parent != null && getAlignment() != null && getIndex() >= 0) { |
||||||
|
var oldOpen = isOpen(); |
||||||
|
parent.closeTab(getAlignment(), getIndex()); |
||||||
|
open = false; |
||||||
|
firePropertyChange("open", oldOpen, false); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Contract(pure = true) |
||||||
|
private boolean isOpen() { |
||||||
|
return open; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public TabFrame getTabFrame() { |
||||||
|
return parent; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setTabFrame(final TabFrame parent) { |
||||||
|
var old = this.parent; |
||||||
|
this.parent = parent; |
||||||
|
firePropertyChange("tabFrame", old, parent); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Alignment getAlignment() { |
||||||
|
return alignment; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setAlignment(final Alignment alignment) { |
||||||
|
if (alignment == null || this.alignment == Alignment.CENTER) { |
||||||
|
throw new IllegalArgumentException("Illegal alignment: " + (alignment != null |
||||||
|
? alignment.toString() : "null")); |
||||||
|
} |
||||||
|
var old = this.alignment; |
||||||
|
this.alignment = alignment; |
||||||
|
firePropertyChange("alignment", old, alignment); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void open() { |
||||||
|
if (parent != null && getAlignment() != null && getIndex() >= 0) { |
||||||
|
var oldOpen = isOpen(); |
||||||
|
parent.openTab(getAlignment(), getIndex()); |
||||||
|
open = true; |
||||||
|
firePropertyChange("open", oldOpen, true); |
||||||
|
requestFocus(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getTitle() { |
||||||
|
return title; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setTitle(final String title) { |
||||||
|
var old = this.title; |
||||||
|
this.title = title == null ? "" : title; |
||||||
|
firePropertyChange("title", old, this.title); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Icon getIcon() { |
||||||
|
return icon; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setIcon(final Icon icon) { |
||||||
|
var old = this.icon; |
||||||
|
this.icon = icon; |
||||||
|
firePropertyChange("icon", old, icon); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int getIndex() { |
||||||
|
return index; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setIndex(final int index) { |
||||||
|
int old = this.index; |
||||||
|
this.index = index; |
||||||
|
firePropertyChange("index", old, index); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,67 @@ |
|||||||
|
/* |
||||||
|
* 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.tabframe; |
||||||
|
|
||||||
|
import javax.swing.*; |
||||||
|
import javax.swing.border.Border; |
||||||
|
import java.awt.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* Holder component. |
||||||
|
* |
||||||
|
* @author Jannis Weis |
||||||
|
*/ |
||||||
|
public class PopupContainer extends JPanel { |
||||||
|
|
||||||
|
private Component popup; |
||||||
|
|
||||||
|
public PopupContainer() { |
||||||
|
super(new BorderLayout()); |
||||||
|
super.setBorder(null); |
||||||
|
} |
||||||
|
|
||||||
|
public Component getPopup() { |
||||||
|
return popup; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setBorder(final Border border) { |
||||||
|
super.setBorder(null); |
||||||
|
} |
||||||
|
|
||||||
|
public void setPopup(final Component component) { |
||||||
|
removeAll(); |
||||||
|
add(component, BorderLayout.CENTER); |
||||||
|
this.popup = component; |
||||||
|
revalidate(); |
||||||
|
repaint(); |
||||||
|
component.doLayout(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public Border getBorder() { |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,45 @@ |
|||||||
|
/* |
||||||
|
* 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.tabframe; |
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull; |
||||||
|
|
||||||
|
import javax.swing.*; |
||||||
|
import java.awt.*; |
||||||
|
|
||||||
|
public final class TabArea extends JPanel { |
||||||
|
|
||||||
|
public TabArea() { |
||||||
|
setLayout(null); |
||||||
|
setOpaque(true); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void paint(@NotNull final Graphics g) { |
||||||
|
g.setColor(getBackground()); |
||||||
|
g.fillRect(0, 0, getWidth(), getHeight()); |
||||||
|
paintChildren(g); |
||||||
|
paintBorder(g); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,862 @@ |
|||||||
|
/* |
||||||
|
* 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.tabframe; |
||||||
|
|
||||||
|
import com.weis.darklaf.components.alignment.Alignment; |
||||||
|
import org.jetbrains.annotations.Contract; |
||||||
|
import org.jetbrains.annotations.NotNull; |
||||||
|
|
||||||
|
import javax.swing.*; |
||||||
|
import javax.swing.plaf.ComponentUI; |
||||||
|
import java.awt.*; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Objects; |
||||||
|
|
||||||
|
/** |
||||||
|
* Frame that supports popup components. |
||||||
|
* |
||||||
|
* @author Jannis Weis |
||||||
|
*/ |
||||||
|
public class TabFrame extends JComponent { |
||||||
|
|
||||||
|
private final JComponent bottomTabs = createTabContainer(); |
||||||
|
private final JComponent topTabs = createTabContainer(); |
||||||
|
private final JComponent leftTabs = createTabContainer(); |
||||||
|
private final JComponent rightTabs = createTabContainer(); |
||||||
|
|
||||||
|
private final TabFrameContent content = createContentPane(); |
||||||
|
|
||||||
|
private final List<TabFrameTab>[] tabLists; |
||||||
|
private final List<TabFramePopup>[] popupLists; |
||||||
|
private final int[] selectedIndices; |
||||||
|
|
||||||
|
private int tabSize = -1; |
||||||
|
private int maxTabWidth = -1; |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates new {@link TabFrame}. |
||||||
|
* A TabFrame displays one center component and multiple popups around |
||||||
|
* that can be toggles with a TabbedPane like tabArea along the border. |
||||||
|
*/ |
||||||
|
public TabFrame() { |
||||||
|
super(); |
||||||
|
updateUI(); |
||||||
|
add(content.getComponent()); |
||||||
|
|
||||||
|
int count = Alignment.values().length; |
||||||
|
//noinspection unchecked
|
||||||
|
tabLists = (ArrayList<TabFrameTab>[]) new ArrayList[count]; |
||||||
|
//noinspection unchecked
|
||||||
|
popupLists = (ArrayList<TabFramePopup>[]) new ArrayList[count]; |
||||||
|
for (int i = 0; i < count; i++) { |
||||||
|
tabLists[i] = new ArrayList<>(); |
||||||
|
popupLists[i] = new ArrayList<>(); |
||||||
|
} |
||||||
|
selectedIndices = new int[count]; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void updateUI() { |
||||||
|
setUI(UIManager.getUI(this)); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the ui. |
||||||
|
* |
||||||
|
* @return the ui. |
||||||
|
*/ |
||||||
|
public TabFrameUI getUI() { |
||||||
|
return (TabFrameUI) super.getUI(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void setUI(final ComponentUI newUI) { |
||||||
|
if (!(newUI instanceof TabFrameUI)) { |
||||||
|
throw new IllegalArgumentException("UI class must be of type TabFrameUI"); |
||||||
|
} |
||||||
|
super.setUI(newUI); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getUIClassID() { |
||||||
|
return "TabFrameUI"; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the height/width of the tab container. |
||||||
|
* |
||||||
|
* @return the tab size. |
||||||
|
*/ |
||||||
|
public int getTabSize() { |
||||||
|
return tabSize >= 0 ? tabSize : getUI().getTabSize(this); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the height/width of the tab container. |
||||||
|
* <p> |
||||||
|
* A negative value means the ui should calculate the appropriate size. |
||||||
|
* |
||||||
|
* @param size the size of the tab container. |
||||||
|
*/ |
||||||
|
public void setTabSize(final int size) { |
||||||
|
this.tabSize = size; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the maximum width a tab can inhibit. |
||||||
|
* A negative value indicated the tabs should use as much space as |
||||||
|
* their proffered size indicates. |
||||||
|
* |
||||||
|
* @return the maximum tab width. |
||||||
|
*/ |
||||||
|
public int getMaxTabWidth() { |
||||||
|
return maxTabWidth; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the maximum width a tab can inhibit. |
||||||
|
* A negative value indicated the tabs should use as much space as |
||||||
|
* their proffered size indicates. |
||||||
|
* |
||||||
|
* @param maxTabWidth the maximum tab width. |
||||||
|
*/ |
||||||
|
public void setMaxTabWidth(final int maxTabWidth) { |
||||||
|
this.maxTabWidth = maxTabWidth; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the number of tabs at the top. |
||||||
|
* |
||||||
|
* @return number of tabs at the top. |
||||||
|
*/ |
||||||
|
public int getTopTabCount() { |
||||||
|
return getTabCountAt(Alignment.NORTH) + getTabCountAt(Alignment.NORTH_EAST); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the number of tabs at the given alignment position. |
||||||
|
* |
||||||
|
* @param a the alignment position. |
||||||
|
* @return number of tabs. |
||||||
|
*/ |
||||||
|
public int getTabCountAt(final Alignment a) { |
||||||
|
return tabsForAlignment(a).size(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get a list of tab components at the given alignment position. |
||||||
|
* |
||||||
|
* @param a the alignment position. |
||||||
|
* @return list of tab components at position. |
||||||
|
*/ |
||||||
|
public List<TabFrameTab> tabsForAlignment(@NotNull final Alignment a) { |
||||||
|
return tabLists[a.ordinal()]; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the number of tabs at the bottom. |
||||||
|
* |
||||||
|
* @return number of tabs at the bottom. |
||||||
|
*/ |
||||||
|
public int getBottomTabCount() { |
||||||
|
return getTabCountAt(Alignment.SOUTH) + getTabCountAt(Alignment.SOUTH_WEST); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the number of tabs at the left. |
||||||
|
* |
||||||
|
* @return number of tabs at the left. |
||||||
|
*/ |
||||||
|
public int getLeftTabCount() { |
||||||
|
return getTabCountAt(Alignment.WEST) + getTabCountAt(Alignment.NORTH_WEST); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the number of tabs at the right. |
||||||
|
* |
||||||
|
* @return number of tabs at the right. |
||||||
|
*/ |
||||||
|
public int getRightTabCount() { |
||||||
|
return getTabCountAt(Alignment.EAST) + getTabCountAt(Alignment.SOUTH_EAST); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the center component. |
||||||
|
* |
||||||
|
* @return the center component. |
||||||
|
*/ |
||||||
|
public Component getContent() { |
||||||
|
return content.getContent(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the center component. |
||||||
|
* |
||||||
|
* @param c the center component. |
||||||
|
*/ |
||||||
|
public void setContent(final Component c) { |
||||||
|
content.setContent(c); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the content pane. |
||||||
|
* |
||||||
|
* @return the content pane. |
||||||
|
*/ |
||||||
|
public TabFrameContent getContentPane() { |
||||||
|
return content; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Create a tab container. |
||||||
|
* |
||||||
|
* @return a tab container. |
||||||
|
*/ |
||||||
|
protected JComponent createTabContainer() { |
||||||
|
return new TabArea(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Create the content pane. |
||||||
|
* |
||||||
|
* @return the content pane. |
||||||
|
*/ |
||||||
|
protected TabFrameContent createContentPane() { |
||||||
|
return new TabFrameContentPane(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Insert a tab. |
||||||
|
* A default tab component and popup component will be created. |
||||||
|
* |
||||||
|
* @param c the component to add. |
||||||
|
* @param a the alignment position to add at.{@link TabFramePosition#getAlignment()} |
||||||
|
* @param index the index to insert at.{@link TabFramePosition#getIndex()} |
||||||
|
*/ |
||||||
|
public void insertTab(@NotNull final Component c, final Alignment a, final int index) { |
||||||
|
var title = c.getName(); |
||||||
|
insertTab(c, title == null ? "" : title, a, index); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Insert a tab. |
||||||
|
* A default tab component and popup component will be created. |
||||||
|
* |
||||||
|
* @param c the component to add. |
||||||
|
* @param title the title of the component. |
||||||
|
* @param a the alignment position to add at.{@link TabFramePosition#getAlignment()} |
||||||
|
* @param index the index to insert at.{@link TabFramePosition#getIndex()} |
||||||
|
*/ |
||||||
|
public void insertTab(@NotNull final Component c, final String title, final Alignment a, final int index) { |
||||||
|
insertTab(c, title, null, a, index); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Insert a tab. |
||||||
|
* A default tab component and popup component will be created. |
||||||
|
* |
||||||
|
* @param c the component to add. |
||||||
|
* @param title the title of the component. |
||||||
|
* @param icon the icon |
||||||
|
* @param a the alignment position to add at.{@link TabFramePosition#getAlignment()} |
||||||
|
* @param index the index to insert at.{@link TabFramePosition#getIndex()} |
||||||
|
*/ |
||||||
|
public void insertTab(@NotNull final Component c, final String title, final Icon icon, final Alignment a, |
||||||
|
final int index) { |
||||||
|
TabFramePopup popup = new PanelPopup(title, icon, c); |
||||||
|
insertTab(popup, title, icon, a, index); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Insert a tab. |
||||||
|
* A default tab component will be created. |
||||||
|
* |
||||||
|
* @param c the popup to add. |
||||||
|
* @param title the title of the component. |
||||||
|
* @param a the alignment position to add at.{@link TabFramePosition#getAlignment()} |
||||||
|
* @param index the index to insert at.{@link TabFramePosition#getIndex()} |
||||||
|
*/ |
||||||
|
public void insertTab(@NotNull final TabFramePopup c, final String title, final Icon icon, final Alignment a, |
||||||
|
final int index) { |
||||||
|
if (a == Alignment.CENTER) { |
||||||
|
return; |
||||||
|
} |
||||||
|
var tabComponent = createDefaultTab(title, icon, a, index); |
||||||
|
insertTabComp(tabComponent, a, index); |
||||||
|
compsForAlignment(a).add(index, c); |
||||||
|
c.setEnabled(false); |
||||||
|
c.setTabFrame(this); |
||||||
|
c.setAlignment(a); |
||||||
|
c.setIndex(index); |
||||||
|
} |
||||||
|
|
||||||
|
protected TabFrameTab createDefaultTab(final String text, final Icon icon, final Alignment a, final int index) { |
||||||
|
return new TabFrameTabLabel(text, icon, a, index, this); |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
* Inserts a tab component at the given position. |
||||||
|
*/ |
||||||
|
private void insertTabComp(@NotNull final TabFrameTab tabComp, final Alignment a, final int index) { |
||||||
|
tabComp.setOrientation(a); |
||||||
|
getTabContainer(a).add(tabComp.getComponent()); |
||||||
|
var tabs = tabsForAlignment(a); |
||||||
|
//Adjust indices for tabs.
|
||||||
|
var iterator = tabs.listIterator(index); |
||||||
|
while (iterator.hasNext()) { |
||||||
|
var tab = iterator.next(); |
||||||
|
tab.setIndex(tab.getIndex() + 1); |
||||||
|
} |
||||||
|
tabComp.setIndex(index); |
||||||
|
tabComp.setOrientation(a); |
||||||
|
tabs.add(index, tabComp); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get a list of components at the given alignment position. |
||||||
|
* |
||||||
|
* @param a the alignment position. |
||||||
|
* @return list of components at position. |
||||||
|
*/ |
||||||
|
public List<TabFramePopup> compsForAlignment(@NotNull final Alignment a) { |
||||||
|
return popupLists[a.ordinal()]; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the tab container for the given alignment position. |
||||||
|
* |
||||||
|
* @param a the alignment position.{@link TabFramePosition#getAlignment()} |
||||||
|
* @return the tab container. |
||||||
|
*/ |
||||||
|
@Contract(pure = true) |
||||||
|
public JComponent getTabContainer(@NotNull final Alignment a) { |
||||||
|
switch (a) { |
||||||
|
case NORTH: |
||||||
|
case NORTH_EAST: |
||||||
|
return getTopTabContainer(); |
||||||
|
case SOUTH: |
||||||
|
case SOUTH_WEST: |
||||||
|
return getBottomTabContainer(); |
||||||
|
case EAST: |
||||||
|
case SOUTH_EAST: |
||||||
|
return getRightTabContainer(); |
||||||
|
case WEST: |
||||||
|
case NORTH_WEST: |
||||||
|
return getLeftTabContainer(); |
||||||
|
case CENTER: |
||||||
|
throw new IllegalArgumentException("invalid alignment: " + a); |
||||||
|
default: |
||||||
|
throw new IllegalArgumentException(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the container that holds the top tab components. |
||||||
|
* |
||||||
|
* @return the top tab container. |
||||||
|
*/ |
||||||
|
public JComponent getTopTabContainer() { |
||||||
|
return topTabs; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the container that holds the bottom tab components. |
||||||
|
* |
||||||
|
* @return the bottom tab container. |
||||||
|
*/ |
||||||
|
public JComponent getBottomTabContainer() { |
||||||
|
return bottomTabs; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the container that holds the right tab components. |
||||||
|
* |
||||||
|
* @return the right tab container. |
||||||
|
*/ |
||||||
|
public JComponent getRightTabContainer() { |
||||||
|
return rightTabs; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the container that holds the left tab components. |
||||||
|
* |
||||||
|
* @return the left tab container. |
||||||
|
*/ |
||||||
|
public JComponent getLeftTabContainer() { |
||||||
|
return leftTabs; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the popup at the given position. |
||||||
|
* |
||||||
|
* @param c the popup to place at the position. |
||||||
|
* @param title the title of the popup. |
||||||
|
* @param icon the icon of the popup. |
||||||
|
* @param a the alignment position to place at.{@link TabFramePosition#getAlignment()} |
||||||
|
* @param index the index to place at.{@link TabFramePosition#getIndex()} |
||||||
|
*/ |
||||||
|
public void setTabAt(final TabFramePopup c, final String title, final Icon icon, |
||||||
|
final Alignment a, final int index) { |
||||||
|
if (a == Alignment.CENTER) { |
||||||
|
return; |
||||||
|
} |
||||||
|
var text = title == null ? c.getComponent().getName() : title; |
||||||
|
text = text == null ? c.getTitle() : text; |
||||||
|
var tabComponent = createDefaultTab(text, icon, a, index); |
||||||
|
c.setTitle(text); |
||||||
|
c.setIcon(icon); |
||||||
|
c.setTabFrame(this); |
||||||
|
c.setAlignment(a); |
||||||
|
c.setIndex(index); |
||||||
|
|
||||||
|
tabComponent.setSelected(getTabComponentAt(a, index).isSelected()); |
||||||
|
setTabComponent(tabComponent, a, index); |
||||||
|
compsForAlignment(a).set(index, c); |
||||||
|
if (tabComponent.isSelected()) { |
||||||
|
getComponentAt(a, index).doLayout(); |
||||||
|
getComponentAt(a, index).repaint(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the tab component at the given position. |
||||||
|
* |
||||||
|
* @param a the alignment position.{@link TabFramePosition#getAlignment()} |
||||||
|
* @param index the index.{@link TabFramePosition#getIndex()} |
||||||
|
* @return the tab component. |
||||||
|
*/ |
||||||
|
public TabFrameTab getTabComponentAt(final Alignment a, final int index) { |
||||||
|
var tabs = tabsForAlignment(a); |
||||||
|
return tabs.get(index); |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
* Set the tab component at the given position. |
||||||
|
*/ |
||||||
|
private void setTabComponent(@NotNull final TabFrameTab tab, final Alignment a, final int index) { |
||||||
|
var tabs = tabsForAlignment(a); |
||||||
|
var oldComp = tabs.get(index); |
||||||
|
getTabContainer(a).remove(oldComp.getComponent()); |
||||||
|
getTabContainer(a).add(tab.getComponent()); |
||||||
|
tabs.set(index, tab); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the component at the given position. |
||||||
|
* |
||||||
|
* @param a the alignment position.{@link TabFramePosition#getAlignment()} |
||||||
|
* @param index the index. {@link TabFramePosition#getIndex()} ()} |
||||||
|
* @return the popup component specified by {@link TabFramePopup#getContentPane()}. |
||||||
|
*/ |
||||||
|
public Component getComponentAt(final Alignment a, final int index) { |
||||||
|
var tabs = compsForAlignment(a); |
||||||
|
return tabs.get(index).getContentPane(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Close a popup. |
||||||
|
* |
||||||
|
* @param c the component to close. |
||||||
|
*/ |
||||||
|
public void closeTab(final Component c) { |
||||||
|
var res = findComponent(c); |
||||||
|
if (res != null) { |
||||||
|
closeTab(res.getAlignment(), res.getIndex()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the position of the given component or null if it isn't currently |
||||||
|
* added. |
||||||
|
* |
||||||
|
* @param c the component to find. |
||||||
|
* @return the position in the tabFrame.{@link TabFramePosition} |
||||||
|
*/ |
||||||
|
public TabFramePosition findComponent(final Component c) { |
||||||
|
for (var a : Alignment.values()) { |
||||||
|
var list = popupLists[a.ordinal()]; |
||||||
|
for (int i = 0; i < list.size(); i++) { |
||||||
|
if (Objects.equals(list.get(i).getContentPane(), c)) { |
||||||
|
return new TabFramePosition(a, i); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Close a popup. |
||||||
|
* |
||||||
|
* @param a the alignment position of the popup.{@link TabFramePosition#getAlignment()} |
||||||
|
* @param index the index of the tab.{@link TabFramePosition#getIndex()} |
||||||
|
*/ |
||||||
|
public void closeTab(final Alignment a, final int index) { |
||||||
|
toggleTab(a, index, false); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Toggles the visibility of a tab. |
||||||
|
* |
||||||
|
* @param a the alignment position.{@link TabFramePosition#getAlignment()} |
||||||
|
* @param index the index.{@link TabFramePosition#getIndex()} |
||||||
|
* @param enabled true if visible. |
||||||
|
*/ |
||||||
|
public void toggleTab(@NotNull final Alignment a, final int index, final boolean enabled) { |
||||||
|
var oldIndex = selectedIndices[a.getIndex()]; |
||||||
|
if (content.isEnabled(a) == enabled && oldIndex == index) return; |
||||||
|
var compAtIndex = getTabComponentAt(a, index); |
||||||
|
compAtIndex.setSelected(enabled); |
||||||
|
notifySelectionChange(compAtIndex); |
||||||
|
setPopupVisibility(compAtIndex, enabled); |
||||||
|
if (enabled) { |
||||||
|
getPopupComponentAt(a).doLayout(); |
||||||
|
getPopupComponentAt(a).requestFocus(); |
||||||
|
} |
||||||
|
firePropertyChange("visibleTab", new TabFramePosition(a, oldIndex), new TabFramePosition(a, index)); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Notify the tabFrame that a selection has changed. |
||||||
|
* |
||||||
|
* @param tabComponent the tab at which the selection has changed. |
||||||
|
*/ |
||||||
|
public void notifySelectionChange(final TabFrameTab tabComponent) { |
||||||
|
if (tabComponent == null) { |
||||||
|
return; |
||||||
|
} |
||||||
|
var a = tabComponent.getOrientation(); |
||||||
|
selectedIndices[a.ordinal()] = tabComponent.getIndex(); |
||||||
|
if (tabComponent.isSelected()) { |
||||||
|
for (var tc : tabsForAlignment(a)) { |
||||||
|
if (tc != null && tc != tabComponent) { |
||||||
|
tc.setSelected(false); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
* Set the visibility of the popup. |
||||||
|
*/ |
||||||
|
private void setPopupVisibility(@NotNull final TabFrameTab tabComponent, |
||||||
|
final boolean selected) { |
||||||
|
var a = tabComponent.getOrientation(); |
||||||
|
var c = compsForAlignment(a).get(tabComponent.getIndex()); |
||||||
|
c.setEnabled(selected); |
||||||
|
content.setComponentAt(a, c.getComponent()); |
||||||
|
content.setEnabled(a, selected); |
||||||
|
doLayout(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the popup component at the given position that is currently active. |
||||||
|
* |
||||||
|
* @param a the alignment position. {@link TabFramePosition#getAlignment()} |
||||||
|
* @return the popup component specified by {@link TabFramePopup#getComponent()}. |
||||||
|
*/ |
||||||
|
public Component getPopupComponentAt(final Alignment a) { |
||||||
|
var tabs = compsForAlignment(a); |
||||||
|
return tabs.get(Math.max(Math.min(tabs.size() - 1, selectedIndices[a.ordinal()]), 0)).getComponent(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Close a popup. |
||||||
|
* |
||||||
|
* @param c the popup to close. |
||||||
|
*/ |
||||||
|
public void closeTab(final TabFramePopup c) { |
||||||
|
if (c == null) return; |
||||||
|
closeTab(c.getAlignment(), c.getIndex()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Open a popup. |
||||||
|
* |
||||||
|
* @param c the component to open. |
||||||
|
*/ |
||||||
|
public void openTab(final Component c) { |
||||||
|
var res = findComponent(c); |
||||||
|
if (res != null) { |
||||||
|
openTab(res.getAlignment(), res.getIndex()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Open a popup. |
||||||
|
* |
||||||
|
* @param a the alignment position of the popup.{@link TabFramePosition#getAlignment()} |
||||||
|
* @param index the index of the tab.{@link TabFramePosition#getIndex()} |
||||||
|
*/ |
||||||
|
public void openTab(final Alignment a, final int index) { |
||||||
|
toggleTab(a, index, true); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Open a popup. |
||||||
|
* |
||||||
|
* @param c the popup to open. |
||||||
|
*/ |
||||||
|
public void openTab(final TabFramePopup c) { |
||||||
|
if (c == null) return; |
||||||
|
openTab(c.getAlignment(), c.getIndex()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Add a popup. |
||||||
|
* |
||||||
|
* @param c the content component. |
||||||
|
* @param title the title. |
||||||
|
* @param a the alignment position.{@link TabFramePosition#getAlignment()} |
||||||
|
*/ |
||||||
|
public void addTab(final Component c, final String title, final Alignment a) { |
||||||
|
addTab(c, title, null, a); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Add a popup. |
||||||
|
* |
||||||
|
* @param c the content component. |
||||||
|
* @param title the title. |
||||||
|
* @param icon the icon. |
||||||
|
* @param a the alignment position.{@link TabFramePosition#getAlignment()} |
||||||
|
*/ |
||||||
|
public void addTab(final Component c, final String title, final Icon icon, final Alignment a) { |
||||||
|
insertTab(c, title, icon, a, tabsForAlignment(a).size()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Add a popup. |
||||||
|
* |
||||||
|
* @param c the popup. |
||||||
|
* @param title the title. |
||||||
|
* @param icon the icon. |
||||||
|
* @param a the alignment position.{@link TabFramePosition#getAlignment()} |
||||||
|
*/ |
||||||
|
public void addTab(final TabFramePopup c, final String title, final Icon icon, final Alignment a) { |
||||||
|
insertTab(c, title, icon, a, tabsForAlignment(a).size()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Remove a popup. |
||||||
|
* |
||||||
|
* @param a the alignment position.{@link TabFramePosition#getAlignment()} |
||||||
|
* @param index the index of the tab.{@link TabFramePosition#getIndex()} |
||||||
|
*/ |
||||||
|
public void removeTab(final Alignment a, final int index) { |
||||||
|
var comp = compsForAlignment(a).get(index); |
||||||
|
comp.close(); |
||||||
|
comp.setTabFrame(null); |
||||||
|
comp.setIndex(-1); |
||||||
|
removeTabComp(a, index); |
||||||
|
doLayout(); |
||||||
|
getTabContainer(a).repaint(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Move a tab to a new position. |
||||||
|
* |
||||||
|
* @param tabComp the tab to move. |
||||||
|
* @param a the new alignment position.{@link TabFramePosition#getAlignment()} |
||||||
|
*/ |
||||||
|
public void moveTab(@NotNull final TabFrameTab tabComp, final Alignment a) { |
||||||
|
if (a == tabComp.getOrientation()) { |
||||||
|
return; |
||||||
|
} |
||||||
|
boolean oldSelected = tabComp.isSelected(); |
||||||
|
var oldAlign = tabComp.getOrientation(); |
||||||
|
int index = tabComp.getIndex(); |
||||||
|
compsForAlignment(oldAlign).get(index).close(); |
||||||
|
removeTabComp(oldAlign, index); |
||||||
|
|
||||||
|
var comp = compsForAlignment(oldAlign).remove(index); |
||||||
|
int newIndex = tabsForAlignment(a).size(); |
||||||
|
|
||||||
|
insertTabComp(tabComp, a, newIndex); |
||||||
|
compsForAlignment(a).add(newIndex, comp); |
||||||
|
tabComp.setSelected(oldSelected); |
||||||
|
notifySelectionChange(tabComp); |
||||||
|
|
||||||
|
comp.setIndex(newIndex); |
||||||
|
comp.setAlignment(a); |
||||||
|
|
||||||
|
doLayout(); |
||||||
|
getTabContainer(oldAlign).repaint(); |
||||||
|
getTabContainer(a).repaint(); |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
* Remove a tab component from the given position. |
||||||
|
*/ |
||||||
|
private void removeTabComp(final Alignment a, final int index) { |
||||||
|
var tabs = tabsForAlignment(a); |
||||||
|
//Adjust indices for tabs.
|
||||||
|
var iterator = tabs.listIterator(index); |
||||||
|
while (iterator.hasNext()) { |
||||||
|
var tab = iterator.next(); |
||||||
|
tab.setIndex(tab.getIndex() - 1); |
||||||
|
} |
||||||
|
var tab = tabs.remove(index); |
||||||
|
getTabContainer(a).remove(tab.getComponent()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the popup component at the given position. |
||||||
|
* |
||||||
|
* @param a the alignment position.{@link TabFramePosition#getAlignment()} |
||||||
|
* @param index the index.{@link TabFramePosition#getIndex()} |
||||||
|
* @return the popup component specified by {@link TabFramePopup#getComponent()}. |
||||||
|
*/ |
||||||
|
public Component getPopupComponentAt(final Alignment a, final int index) { |
||||||
|
var tabs = compsForAlignment(a); |
||||||
|
return tabs.get(index).getComponent(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the component at the given position. |
||||||
|
* |
||||||
|
* @param a the alignment position. {@link TabFramePosition#getAlignment()} |
||||||
|
* @return the component specified by {@link TabFramePopup#getContentPane()}. |
||||||
|
*/ |
||||||
|
public Component getComponentAt(final Alignment a) { |
||||||
|
var tabs = compsForAlignment(a); |
||||||
|
return tabs.get(Math.max(Math.min(tabs.size() - 1, selectedIndices[a.ordinal()]), 0)).getContentPane(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the custom tab component at the given position. |
||||||
|
* |
||||||
|
* @param a the alignment position.{@link TabFramePosition#getAlignment()} |
||||||
|
* @param index the index.{@link TabFramePosition#getIndex()} |
||||||
|
* @return the user tab component or null if none is installed. |
||||||
|
*/ |
||||||
|
public Component getUserTabComponentAt(final Alignment a, final int index) { |
||||||
|
var tab = getTabComponentAt(a, index); |
||||||
|
if (tab instanceof TabFrameTabContainer) { |
||||||
|
return ((TabFrameTabContainer) tab).getContent(); |
||||||
|
} else { |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the custom tab component at the given position. |
||||||
|
* |
||||||
|
* @param component the custom tab component. |
||||||
|
* @param a the alignment position.{@link TabFramePosition#getAlignment()} |
||||||
|
* @param index the index.{@link TabFramePosition#getIndex()} |
||||||
|
*/ |
||||||
|
public void setUserTabComponentAt(final JComponent component, final Alignment a, final int index) { |
||||||
|
var tabs = tabsForAlignment(a); |
||||||
|
var tabContainer = tabs.get(index); |
||||||
|
if (component == null) { |
||||||
|
if (tabContainer instanceof TabFrameTabContainer) { |
||||||
|
setTabComponent(((TabFrameTabContainer) tabContainer).oldTab, a, index); |
||||||
|
} |
||||||
|
} else { |
||||||
|
if (tabContainer instanceof TabFrameTabContainer) { |
||||||
|
((TabFrameTabContainer) tabContainer).setContent(component); |
||||||
|
} else { |
||||||
|
var cont = new TabFrameTabContainer(this, component, tabContainer, a, index); |
||||||
|
setTabComponent(cont, a, index); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the accelerator index at the given position. |
||||||
|
* |
||||||
|
* @param accelerator the accelerator. (a negative value being no accelerator). |
||||||
|
* @param a the alignment position.{@link TabFramePosition#getAlignment()} |
||||||
|
* @param index the index.{@link TabFramePosition#getIndex()} |
||||||
|
*/ |
||||||
|
public void setAcceleratorAt(final int accelerator, final Alignment a, final int index) { |
||||||
|
getTabComponentAt(a, index).setAccelerator(accelerator); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the position of the alignment peer. That being the |
||||||
|
* other position that occupies the same tab container given by |
||||||
|
* {@link #getTabContainer(Alignment)}. |
||||||
|
* <p> |
||||||
|
* NORTH <--> NORTH_EAST |
||||||
|
* <p> |
||||||
|
* EAST <--> SOUTH_EAST |
||||||
|
* <p> |
||||||
|
* SOUTH <--> SOUTH_WEST |
||||||
|
* <p> |
||||||
|
* WEST <--> NORTH_WEST |
||||||
|
* |
||||||
|
* @param a the alignment position.{@link TabFramePosition#getAlignment()} |
||||||
|
* @return the peer position.{@link TabFramePosition#getAlignment()} |
||||||
|
*/ |
||||||
|
public Alignment getPeer(@NotNull final Alignment a) { |
||||||
|
switch (a) { |
||||||
|
case NORTH: |
||||||
|
case SOUTH: |
||||||
|
case WEST: |
||||||
|
case EAST: |
||||||
|
return a.clockwise(); |
||||||
|
case NORTH_EAST: |
||||||
|
case NORTH_WEST: |
||||||
|
case SOUTH_EAST: |
||||||
|
case SOUTH_WEST: |
||||||
|
return a.anticlockwise(); |
||||||
|
} |
||||||
|
return a; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* This class represents a position inside the tabFrame. |
||||||
|
*/ |
||||||
|
public static class TabFramePosition { |
||||||
|
private final Alignment a; |
||||||
|
private final int index; |
||||||
|
|
||||||
|
@Contract(pure = true) |
||||||
|
public TabFramePosition(final Alignment a, final int index) { |
||||||
|
this.a = a; |
||||||
|
this.index = index; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* The alignment position. |
||||||
|
* This specifies at what location the tab is placed. |
||||||
|
* |
||||||
|
* @return the alignment position. |
||||||
|
*/ |
||||||
|
public Alignment getAlignment() { |
||||||
|
return a; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* The index inside the alignment position. |
||||||
|
* |
||||||
|
* @return the index. |
||||||
|
*/ |
||||||
|
public int getIndex() { |
||||||
|
return index; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,83 @@ |
|||||||
|
/* |
||||||
|
* 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.tabframe; |
||||||
|
|
||||||
|
import com.weis.darklaf.components.alignment.Alignment; |
||||||
|
|
||||||
|
import java.awt.*; |
||||||
|
|
||||||
|
public interface TabFrameContent { |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the current component displayed in the middle. |
||||||
|
* |
||||||
|
* @return the content component. |
||||||
|
*/ |
||||||
|
Component getContent(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the content to displayed in the middle. |
||||||
|
* |
||||||
|
* @param content the content component. |
||||||
|
*/ |
||||||
|
void setContent(final Component content); |
||||||
|
|
||||||
|
/** |
||||||
|
* Enables a popup if possible. |
||||||
|
* |
||||||
|
* @param a the alignment. |
||||||
|
* @param enabled true if enabled. |
||||||
|
*/ |
||||||
|
void setEnabled(Alignment a, boolean enabled); |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns whether the given popup is enabled. |
||||||
|
* |
||||||
|
* @param a the alignment of the popup. |
||||||
|
* @return true if enabled. |
||||||
|
*/ |
||||||
|
boolean isEnabled(Alignment a); |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the component at the specified position. |
||||||
|
* |
||||||
|
* @param a the alignment. |
||||||
|
* @param component the component. |
||||||
|
*/ |
||||||
|
void setComponentAt(final Alignment a, final Component component); |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the component that displays the content. |
||||||
|
* |
||||||
|
* @return the display component. |
||||||
|
*/ |
||||||
|
Component getComponent(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns an array with the enabled status of each alignment. |
||||||
|
* |
||||||
|
* @return the enabled status. |
||||||
|
*/ |
||||||
|
boolean[] getStatus(); |
||||||
|
} |
@ -0,0 +1,449 @@ |
|||||||
|
/* |
||||||
|
* 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.tabframe; |
||||||
|
|
||||||
|
import com.weis.darklaf.components.alignment.Alignment; |
||||||
|
import com.weis.darklaf.decorators.AncestorAdapter; |
||||||
|
import org.jetbrains.annotations.Contract; |
||||||
|
import org.jetbrains.annotations.NotNull; |
||||||
|
|
||||||
|
import javax.swing.*; |
||||||
|
import javax.swing.event.AncestorEvent; |
||||||
|
import java.awt.*; |
||||||
|
import java.util.function.BiConsumer; |
||||||
|
|
||||||
|
/** |
||||||
|
* Content pane for {@link TabFrame}. |
||||||
|
* |
||||||
|
* @author Jannis Weis |
||||||
|
*/ |
||||||
|
public class TabFrameContentPane extends JPanel implements TabFrameContent { |
||||||
|
private static final double HORIZONTAL_PROP_LEFT = 0.2; |
||||||
|
private static final double VERTICAL_PROP_TOP = 0.2; |
||||||
|
private static final double VERTICAL_PROP_BOTTOM = 0.75; |
||||||
|
private static final double HORIZONTAL_PROP_RIGHT = 0.75; |
||||||
|
|
||||||
|
private final ToggleSplitPane topSplit; |
||||||
|
private final ToggleSplitPane bottomSplit; |
||||||
|
private final ToggleSplitPane leftSplit; |
||||||
|
private final ToggleSplitPane rightSplit; |
||||||
|
private final ToggleSplitPane leftSplitter; |
||||||
|
private final ToggleSplitPane rightSplitter; |
||||||
|
private final ToggleSplitPane topSplitter; |
||||||
|
private final ToggleSplitPane bottomSplitter; |
||||||
|
|
||||||
|
private final boolean[] enabled = new boolean[8]; |
||||||
|
|
||||||
|
private Component cont = new JPanel(); |
||||||
|
|
||||||
|
public TabFrameContentPane() { |
||||||
|
super(new BorderLayout()); |
||||||
|
cont.setBackground(Color.YELLOW); |
||||||
|
|
||||||
|
PopupContainer leftBottomPanel = new PopupContainer(); |
||||||
|
PopupContainer rightTopPanel = new PopupContainer(); |
||||||
|
PopupContainer rightBottomPanel = new PopupContainer(); |
||||||
|
PopupContainer topLeftPanel = new PopupContainer(); |
||||||
|
PopupContainer topRightPanel = new PopupContainer(); |
||||||
|
PopupContainer bottomLeftPanel = new PopupContainer(); |
||||||
|
PopupContainer bottomRightPanel = new PopupContainer(); |
||||||
|
PopupContainer leftTopPanel = new PopupContainer(); |
||||||
|
|
||||||
|
rightSplitter = new ToggleSplitPane("rightSplitter"); |
||||||
|
rightSplitter.setOrientation(JSplitPane.VERTICAL_SPLIT); |
||||||
|
rightSplitter.setTopComponent(rightTopPanel); |
||||||
|
rightSplitter.setBottomComponent(rightBottomPanel); |
||||||
|
|
||||||
|
leftSplitter = new ToggleSplitPane("leftSplitter"); |
||||||
|
leftSplitter.setOrientation(JSplitPane.VERTICAL_SPLIT); |
||||||
|
leftSplitter.setTopComponent(leftTopPanel); |
||||||
|
leftSplitter.setBottomComponent(leftBottomPanel); |
||||||
|
|
||||||
|
topSplitter = new ToggleSplitPane("topSplitter"); |
||||||
|
topSplitter.setOrientation(JSplitPane.HORIZONTAL_SPLIT); |
||||||
|
topSplitter.setLeftComponent(topLeftPanel); |
||||||
|
topSplitter.setRightComponent(topRightPanel); |
||||||
|
|
||||||
|
bottomSplitter = new ToggleSplitPane("bottomSplitter"); |
||||||
|
bottomSplitter.setOrientation(JSplitPane.HORIZONTAL_SPLIT); |
||||||
|
bottomSplitter.setLeftComponent(bottomLeftPanel); |
||||||
|
bottomSplitter.setRightComponent(bottomRightPanel); |
||||||
|
|
||||||
|
|
||||||
|
topSplit = new ToggleSplitPane("topSplit"); |
||||||
|
bottomSplit = new ToggleSplitPane("bottomSplit"); |
||||||
|
topSplit.setOrientation(JSplitPane.VERTICAL_SPLIT); |
||||||
|
bottomSplit.setOrientation(JSplitPane.VERTICAL_SPLIT); |
||||||
|
|
||||||
|
topSplit.setTopComponent(topSplitter); |
||||||
|
topSplit.setBottomComponent(bottomSplit); |
||||||
|
bottomSplit.setBottomComponent(bottomSplitter); |
||||||
|
|
||||||
|
leftSplit = new ToggleSplitPane("leftSplit"); |
||||||
|
rightSplit = new ToggleSplitPane("rightSplit"); |
||||||
|
leftSplit.setOrientation(JSplitPane.HORIZONTAL_SPLIT); |
||||||
|
rightSplit.setOrientation(JSplitPane.HORIZONTAL_SPLIT); |
||||||
|
|
||||||
|
bottomSplit.setTopComponent(leftSplit); |
||||||
|
leftSplit.setLeftComponent(leftSplitter); |
||||||
|
leftSplit.setRightComponent(rightSplit); |
||||||
|
rightSplit.setRightComponent(rightSplitter); |
||||||
|
rightSplit.setLeftComponent(cont); |
||||||
|
|
||||||
|
topSplit.setResizeWeight(0.0d); |
||||||
|
leftSplit.setResizeWeight(0.0d); |
||||||
|
bottomSplit.setResizeWeight(1.0d); |
||||||
|
rightSplit.setResizeWeight(1.0d); |
||||||
|
setupSplitterPanes(JSplitPane::setResizeWeight, 0.5d); |
||||||
|
|
||||||
|
setupSplitPanes(JSplitPane::setEnabled, false); |
||||||
|
setupSplitPanes(ToggleSplitPane::setResizable, false); |
||||||
|
setupSplitterPanes(JSplitPane::setEnabled, false); |
||||||
|
setupSplitterPanes(ToggleSplitPane::setResizable, false); |
||||||
|
|
||||||
|
add(topSplit, BorderLayout.CENTER); |
||||||
|
|
||||||
|
addAncestorListener(new AncestorAdapter() { |
||||||
|
@Override |
||||||
|
public void ancestorAdded(final AncestorEvent event) { |
||||||
|
removeAncestorListener(this); |
||||||
|
SwingUtilities.invokeLater(() -> init()); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
private <T> void setupSplitterPanes(@NotNull final BiConsumer<? super ToggleSplitPane, T> consumer, |
||||||
|
final T flag) { |
||||||
|
consumer.accept(bottomSplitter, flag); |
||||||
|
consumer.accept(leftSplitter, flag); |
||||||
|
consumer.accept(rightSplitter, flag); |
||||||
|
consumer.accept(topSplitter, flag); |
||||||
|
} |
||||||
|
|
||||||
|
private <T> void setupSplitPanes(@NotNull final BiConsumer<? super ToggleSplitPane, T> consumer, |
||||||
|
final T flag) { |
||||||
|
consumer.accept(topSplit, flag); |
||||||
|
consumer.accept(leftSplit, flag); |
||||||
|
consumer.accept(bottomSplit, flag); |
||||||
|
consumer.accept(rightSplit, flag); |
||||||
|
} |
||||||
|
|
||||||
|
private void init() { |
||||||
|
disableAll(true); |
||||||
|
topSplit.savePosition(VERTICAL_PROP_TOP); |
||||||
|
bottomSplit.savePosition(VERTICAL_PROP_BOTTOM); |
||||||
|
leftSplit.savePosition(HORIZONTAL_PROP_LEFT); |
||||||
|
rightSplit.savePosition(HORIZONTAL_PROP_RIGHT); |
||||||
|
setupSplitterPanes(ToggleSplitPane::savePosition, 0.5d); |
||||||
|
doLayout(); |
||||||
|
} |
||||||
|
|
||||||
|
public void disableAll(final boolean force) { |
||||||
|
for (var a : Alignment.values()) { |
||||||
|
setEnabled(a, false, force); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Show or hide the corresponding panel. |
||||||
|
* |
||||||
|
* @param a position of panel. |
||||||
|
* @param enabled true if should be shown. |
||||||
|
* @param force whether to force the layout process. |
||||||
|
*/ |
||||||
|
public void setEnabled(@NotNull final Alignment a, final boolean enabled, final boolean force) { |
||||||
|
if (enabled == isEnabled(a) && !force) return; |
||||||
|
switch (a) { |
||||||
|
case NORTH: |
||||||
|
changeStatus( |
||||||
|
enabled, Alignment.NORTH_EAST, |
||||||
|
topSplit, topSplitter, |
||||||
|
new LayoutProportions(VERTICAL_PROP_TOP, 1.0, 0.0, 0.0), |
||||||
|
new LayoutWeights(0.0, 0.0, 0.0, 1.0)); |
||||||
|
break; |
||||||
|
case NORTH_EAST: |
||||||
|
changeStatus( |
||||||
|
enabled, Alignment.NORTH, |
||||||
|
topSplit, topSplitter, |
||||||
|
new LayoutProportions(VERTICAL_PROP_TOP, 0.0, 0.0, 1.0), |
||||||
|
new LayoutWeights(0.0, 0.0, 1.0, 0.0)); |
||||||
|
break; |
||||||
|
case EAST: |
||||||
|
changeStatus( |
||||||
|
enabled, Alignment.SOUTH_EAST, |
||||||
|
rightSplit, rightSplitter, |
||||||
|
new LayoutProportions(HORIZONTAL_PROP_RIGHT, 1.0, 1.0, 0.0), |
||||||
|
new LayoutWeights(1.0, 1.0, 0.0, 1.0)); |
||||||
|
break; |
||||||
|
case SOUTH_EAST: |
||||||
|
changeStatus( |
||||||
|
enabled, Alignment.EAST, |
||||||
|
rightSplit, rightSplitter, |
||||||
|
new LayoutProportions(HORIZONTAL_PROP_RIGHT, 0.0, 1.0, 1.0), |
||||||
|
new LayoutWeights(1.0, 1.0, 1.0, 0.0)); |
||||||
|
break; |
||||||
|
case NORTH_WEST: |
||||||
|
changeStatus( |
||||||
|
enabled, Alignment.WEST, |
||||||
|
leftSplit, leftSplitter, |
||||||
|
new LayoutProportions(VERTICAL_PROP_TOP, 1.0, 0.0, 0.0), |
||||||
|
new LayoutWeights(0.0, 0.0, 0.0, 1.0)); |
||||||
|
break; |
||||||
|
case WEST: |
||||||
|
changeStatus( |
||||||
|
enabled, Alignment.NORTH_WEST, |
||||||
|
leftSplit, leftSplitter, |
||||||
|
new LayoutProportions(VERTICAL_PROP_TOP, 0.0, 0.0, 1.0), |
||||||
|
new LayoutWeights(0.0, 0.0, 1.0, 0.0)); |
||||||
|
break; |
||||||
|
case SOUTH_WEST: |
||||||
|
changeStatus( |
||||||
|
enabled, Alignment.SOUTH, |
||||||
|
bottomSplit, bottomSplitter, |
||||||
|
new LayoutProportions(VERTICAL_PROP_BOTTOM, 1.0, 1.0, 0.0), |
||||||
|
new LayoutWeights(1.0, 1.0, 0.0, 1.0)); |
||||||
|
break; |
||||||
|
case SOUTH: |
||||||
|
changeStatus( |
||||||
|
enabled, Alignment.SOUTH_WEST, |
||||||
|
bottomSplit, bottomSplitter, |
||||||
|
new LayoutProportions(VERTICAL_PROP_BOTTOM, 0.0, 1.0, 1.0), |
||||||
|
new LayoutWeights(1.0, 1.0, 1.0, 0.0)); |
||||||
|
break; |
||||||
|
case CENTER: |
||||||
|
break; |
||||||
|
} |
||||||
|
setEnabledFlag(a, enabled); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Change status of panel. |
||||||
|
* |
||||||
|
* @param enabled new status. |
||||||
|
* @param peer peer alignment. |
||||||
|
* @param split the split panel. |
||||||
|
* @param splitter the splitter panel. |
||||||
|
* @param proportions the layout proportions |
||||||
|
* @param weights the layout weights |
||||||
|
*/ |
||||||
|
private void changeStatus(final boolean enabled, @NotNull final Alignment peer, |
||||||
|
@NotNull final ToggleSplitPane split, |
||||||
|
@NotNull final ToggleSplitPane splitter, |
||||||
|
@NotNull final LayoutProportions proportions, |
||||||
|
@NotNull final LayoutWeights weights) { |
||||||
|
if (enabled) { |
||||||
|
enable(split, weights.splitEnable); |
||||||
|
if (!isEnabled(peer)) { |
||||||
|
disable(splitter, weights.splitterPeerDisable, proportions.splitterPeerDisable); |
||||||
|
} else { |
||||||
|
enable(splitter, 0.5d); |
||||||
|
} |
||||||
|
} else { |
||||||
|
if (!isEnabled(peer)) { |
||||||
|
disable(split, weights.splitDisable, proportions.splitDisable); |
||||||
|
} |
||||||
|
disable(splitter, weights.splitterDisable, proportions.splitterDisable); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
* Update the flags. |
||||||
|
*/ |
||||||
|
private void setEnabledFlag(@NotNull final Alignment a, final boolean e) { |
||||||
|
if (a != Alignment.CENTER) { |
||||||
|
enabled[a.getIndex()] = e; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void enable(@NotNull final ToggleSplitPane splitPane, final double weight) { |
||||||
|
boolean restore = !splitPane.isResizable(); |
||||||
|
splitPane.setEnabled(true); |
||||||
|
splitPane.setResizable(true); |
||||||
|
splitPane.setResizeWeight(weight); |
||||||
|
if (restore) { |
||||||
|
splitPane.restorePosition(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void disable(@NotNull final ToggleSplitPane splitPane, final double weight, final double location) { |
||||||
|
if (splitPane.isResizable()) { |
||||||
|
splitPane.savePosition(); |
||||||
|
} |
||||||
|
splitPane.setResizeWeight(weight); |
||||||
|
splitPane.forceSetDividerLocation(location); |
||||||
|
splitPane.setEnabled(false); |
||||||
|
splitPane.setResizable(false); |
||||||
|
} |
||||||
|
|
||||||
|
public Component getContent() { |
||||||
|
return cont; |
||||||
|
} |
||||||
|
|
||||||
|
public void setContent(final Component pane) { |
||||||
|
cont = pane; |
||||||
|
rightSplit.setLeftComponent(pane); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Show or hide the corresponding panel. |
||||||
|
* |
||||||
|
* @param a position of panel. |
||||||
|
* @param enabled true if should be shown. |
||||||
|
*/ |
||||||
|
public void setEnabled(@NotNull final Alignment a, final boolean enabled) { |
||||||
|
setEnabled(a, enabled, false); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns whether the corresponding panel is currently enabled/visible. |
||||||
|
* |
||||||
|
* @param a the position of the panel. |
||||||
|
* @return true if enabled. |
||||||
|
*/ |
||||||
|
public boolean isEnabled(@NotNull final Alignment a) { |
||||||
|
if (a == Alignment.CENTER) { |
||||||
|
return false; |
||||||
|
} else { |
||||||
|
return enabled[a.getIndex()]; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void setComponentAt(@NotNull final Alignment a, final Component c) { |
||||||
|
switch (a) { |
||||||
|
case NORTH: |
||||||
|
((PopupContainer) topSplitter.getLeftComponent()).setPopup(c); |
||||||
|
break; |
||||||
|
case NORTH_EAST: |
||||||
|
((PopupContainer) topSplitter.getRightComponent()).setPopup(c); |
||||||
|
break; |
||||||
|
case EAST: |
||||||
|
((PopupContainer) rightSplitter.getTopComponent()).setPopup(c); |
||||||
|
break; |
||||||
|
case SOUTH_EAST: |
||||||
|
((PopupContainer) rightSplitter.getBottomComponent()).setPopup(c); |
||||||
|
break; |
||||||
|
case SOUTH: |
||||||
|
((PopupContainer) bottomSplitter.getRightComponent()).setPopup(c); |
||||||
|
break; |
||||||
|
case SOUTH_WEST: |
||||||
|
((PopupContainer) bottomSplitter.getLeftComponent()).setPopup(c); |
||||||
|
break; |
||||||
|
case WEST: |
||||||
|
((PopupContainer) leftSplitter.getBottomComponent()).setPopup(c); |
||||||
|
break; |
||||||
|
case NORTH_WEST: |
||||||
|
((PopupContainer) leftSplitter.getTopComponent()).setPopup(c); |
||||||
|
break; |
||||||
|
case CENTER: |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Component getComponent() { |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the status of the individual panels. |
||||||
|
* |
||||||
|
* @return array of status of panels. |
||||||
|
*/ |
||||||
|
@NotNull |
||||||
|
public boolean[] getStatus() { |
||||||
|
return enabled; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the popup component at the position. |
||||||
|
* |
||||||
|
* @param a the position. |
||||||
|
* @return the popup component at position. |
||||||
|
*/ |
||||||
|
@NotNull |
||||||
|
public PanelPopup getPopupComponent(@NotNull final Alignment a) { |
||||||
|
Component popupComponent; |
||||||
|
switch (a) { |
||||||
|
case NORTH: |
||||||
|
popupComponent = ((PopupContainer) topSplitter.getLeftComponent()).getPopup(); |
||||||
|
break; |
||||||
|
case NORTH_EAST: |
||||||
|
popupComponent = ((PopupContainer) topSplitter.getRightComponent()).getPopup(); |
||||||
|
break; |
||||||
|
case EAST: |
||||||
|
popupComponent = ((PopupContainer) rightSplitter.getTopComponent()).getPopup(); |
||||||
|
break; |
||||||
|
case SOUTH_EAST: |
||||||
|
popupComponent = ((PopupContainer) rightSplitter.getBottomComponent()).getPopup(); |
||||||
|
break; |
||||||
|
case SOUTH: |
||||||
|
popupComponent = ((PopupContainer) bottomSplitter.getRightComponent()).getPopup(); |
||||||
|
break; |
||||||
|
case SOUTH_WEST: |
||||||
|
popupComponent = ((PopupContainer) bottomSplitter.getLeftComponent()).getPopup(); |
||||||
|
break; |
||||||
|
case WEST: |
||||||
|
popupComponent = ((PopupContainer) leftSplitter.getBottomComponent()).getPopup(); |
||||||
|
break; |
||||||
|
case NORTH_WEST: |
||||||
|
popupComponent = ((PopupContainer) leftSplitter.getTopComponent()).getPopup(); |
||||||
|
break; |
||||||
|
default: |
||||||
|
throw new IllegalArgumentException("CENTER is not supported"); |
||||||
|
} |
||||||
|
return (PanelPopup) popupComponent; |
||||||
|
} |
||||||
|
|
||||||
|
protected static class LayoutProportions { |
||||||
|
protected final double splitRestore; |
||||||
|
protected final double splitterPeerDisable; |
||||||
|
protected final double splitDisable; |
||||||
|
protected final double splitterDisable; |
||||||
|
|
||||||
|
@Contract(pure = true) |
||||||
|
public LayoutProportions(final double splitRestore, final double splitterPeerDisable, |
||||||
|
final double splitDisable, final double splitterDisable) { |
||||||
|
this.splitRestore = splitRestore; |
||||||
|
this.splitterPeerDisable = splitterPeerDisable; |
||||||
|
this.splitDisable = splitDisable; |
||||||
|
this.splitterDisable = splitterDisable; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected static class LayoutWeights { |
||||||
|
protected final double splitEnable; |
||||||
|
protected final double splitterDisable; |
||||||
|
protected final double splitDisable; |
||||||
|
protected final double splitterPeerDisable; |
||||||
|
|
||||||
|
@Contract(pure = true) |
||||||
|
public LayoutWeights(final double splitEnable, final double splitterDisable, |
||||||
|
final double splitDisable, final double splitterPeerDisable) { |
||||||
|
this.splitEnable = splitEnable; |
||||||
|
this.splitterDisable = splitterDisable; |
||||||
|
this.splitDisable = splitDisable; |
||||||
|
this.splitterPeerDisable = splitterPeerDisable; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,158 @@ |
|||||||
|
/* |
||||||
|
* 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.tabframe; |
||||||
|
|
||||||
|
import com.weis.darklaf.components.alignment.Alignment; |
||||||
|
|
||||||
|
import javax.swing.*; |
||||||
|
import java.awt.*; |
||||||
|
|
||||||
|
public interface TabFramePopup { |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the current content pane. A popup may hold more |
||||||
|
* than one content pane. In this case this method should return |
||||||
|
* the component that is currently displayed. |
||||||
|
* |
||||||
|
* @return the current content pane. |
||||||
|
*/ |
||||||
|
Component getContentPane(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the content pane. This needn't replace the old |
||||||
|
* content pane if this popup supports multiple content panes. |
||||||
|
*/ |
||||||
|
void setContentPane(Component contentPane); |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the component that realizes this popup. |
||||||
|
* |
||||||
|
* @return the component. |
||||||
|
*/ |
||||||
|
Component getComponent(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Close the popup. |
||||||
|
*/ |
||||||
|
default void close() { |
||||||
|
if (getTabFrame() != null && getAlignment() != null && getIndex() >= 0) { |
||||||
|
getTabFrame().closeTab(getAlignment(), getIndex()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the {{@link TabFrame}} this popup belongs to. |
||||||
|
* |
||||||
|
* @return the {{@link TabFrame}}. |
||||||
|
*/ |
||||||
|
TabFrame getTabFrame(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the {{@link TabFrame}} this popup belongs to. |
||||||
|
* |
||||||
|
* @param tabFrame the {{@link TabFrame}}. |
||||||
|
*/ |
||||||
|
void setTabFrame(TabFrame tabFrame); |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the alignment position in the {{@link TabFrame}}. |
||||||
|
* |
||||||
|
* @return the alignment position. |
||||||
|
*/ |
||||||
|
Alignment getAlignment(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the index of the popup. |
||||||
|
* |
||||||
|
* @return the index. |
||||||
|
*/ |
||||||
|
int getIndex(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the index of the popup. |
||||||
|
* This method should only be called from {{@link TabFrame}}. |
||||||
|
* |
||||||
|
* @param index the index. |
||||||
|
*/ |
||||||
|
void setIndex(int index); |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the alignment position in the {{@link TabFrame}}. |
||||||
|
* This method should only be called from {{@link TabFrame}}. |
||||||
|
* |
||||||
|
* @param alignment the alignment position. |
||||||
|
*/ |
||||||
|
void setAlignment(Alignment alignment); |
||||||
|
|
||||||
|
/** |
||||||
|
* Open the popup. |
||||||
|
*/ |
||||||
|
default void open() { |
||||||
|
if (getTabFrame() != null && getAlignment() != null && getIndex() >= 0) { |
||||||
|
getTabFrame().closeTab(getAlignment(), getIndex()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns whether this popup is enabled. |
||||||
|
* |
||||||
|
* @return true if enabled. |
||||||
|
*/ |
||||||
|
boolean isEnabled(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the enabled status of the popup. |
||||||
|
* |
||||||
|
* @param enabled true if enabled. |
||||||
|
*/ |
||||||
|
void setEnabled(boolean enabled); |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the title of the popup. |
||||||
|
* |
||||||
|
* @return the title. |
||||||
|
*/ |
||||||
|
String getTitle(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the title of the popup. |
||||||
|
* |
||||||
|
* @param title the title. |
||||||
|
*/ |
||||||
|
void setTitle(String title); |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the icon of the popup. |
||||||
|
* |
||||||
|
* @return the icon. |
||||||
|
*/ |
||||||
|
Icon getIcon(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the icon of the popup. |
||||||
|
* |
||||||
|
* @param icon the icon. |
||||||
|
*/ |
||||||
|
void setIcon(Icon icon); |
||||||
|
} |
@ -0,0 +1,108 @@ |
|||||||
|
/* |
||||||
|
* 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.tabframe; |
||||||
|
|
||||||
|
import com.weis.darklaf.components.alignment.Alignment; |
||||||
|
|
||||||
|
import java.awt.*; |
||||||
|
|
||||||
|
public interface TabFrameTab { |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the index of the tab. |
||||||
|
* |
||||||
|
* @return the current index. |
||||||
|
*/ |
||||||
|
int getIndex(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the index of the tab. |
||||||
|
* |
||||||
|
* @param index the index. |
||||||
|
*/ |
||||||
|
void setIndex(int index); |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the component housing the tab. |
||||||
|
* |
||||||
|
* @return the display component. |
||||||
|
*/ |
||||||
|
Component getComponent(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the orientation. |
||||||
|
* |
||||||
|
* @return the orientation. |
||||||
|
*/ |
||||||
|
Alignment getOrientation(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the orientation. |
||||||
|
* |
||||||
|
* @param a the orientation. |
||||||
|
*/ |
||||||
|
void setOrientation(Alignment a); |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns whether the tab is selected. |
||||||
|
* |
||||||
|
* @return true if selected. |
||||||
|
*/ |
||||||
|
boolean isSelected(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the selected status of the tab. |
||||||
|
* |
||||||
|
* @param selected true if selected |
||||||
|
*/ |
||||||
|
void setSelected(boolean selected); |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the accelerator. |
||||||
|
* |
||||||
|
* @return the accelerator. |
||||||
|
*/ |
||||||
|
int getAccelerator(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the accelerator. |
||||||
|
* |
||||||
|
* @param accelerator accelerator (>0). |
||||||
|
*/ |
||||||
|
void setAccelerator(int accelerator); |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the tab frame this tab currently belongs to. |
||||||
|
* |
||||||
|
* @return the TabFrame. |
||||||
|
*/ |
||||||
|
TabFrame getTabFrame(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the tab frame this tab currently belongs to. |
||||||
|
* |
||||||
|
* @param tabFrame the TabFrame. |
||||||
|
*/ |
||||||
|
void setTabFrame(TabFrame tabFrame); |
||||||
|
} |
@ -0,0 +1,153 @@ |
|||||||
|
/* |
||||||
|
* 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.tabframe; |
||||||
|
|
||||||
|
import com.weis.darklaf.components.alignment.Alignment; |
||||||
|
|
||||||
|
import javax.swing.*; |
||||||
|
import java.awt.*; |
||||||
|
|
||||||
|
public class TabFrameTabContainer extends JPanel implements TabFrameTab { |
||||||
|
|
||||||
|
protected final TabFrameTab oldTab; |
||||||
|
private TabFrame 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, |
||||||
|
final Alignment alignment, final int index) { |
||||||
|
super(new BorderLayout()); |
||||||
|
this.parent = parent; |
||||||
|
this.oldTab = oldTab; |
||||||
|
this.accelerator = -1; |
||||||
|
setOpaque(false); |
||||||
|
setOrientation(alignment); |
||||||
|
setIndex(index); |
||||||
|
setContent(content); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getUIClassID() { |
||||||
|
return "TabFrameTabContainerUI"; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the content component. |
||||||
|
* |
||||||
|
* @return the content component. |
||||||
|
*/ |
||||||
|
public Component getContent() { |
||||||
|
return content; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the content to display. |
||||||
|
* |
||||||
|
* @param content the content component. |
||||||
|
*/ |
||||||
|
public void setContent(final JComponent content) { |
||||||
|
var old = this.content; |
||||||
|
remove(content); |
||||||
|
this.content = content; |
||||||
|
add(content, BorderLayout.CENTER); |
||||||
|
firePropertyChange("content", old, content); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Color getBackground() { |
||||||
|
if (content != null && !content.isOpaque()) { |
||||||
|
return super.getBackground(); |
||||||
|
} |
||||||
|
return content != null ? content.getBackground() : super.getBackground(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int getIndex() { |
||||||
|
return index; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setIndex(final int index) { |
||||||
|
this.index = index; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Component getComponent() { |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Alignment getOrientation() { |
||||||
|
return orientation; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setOrientation(final Alignment a) { |
||||||
|
if (this.orientation == a) return; |
||||||
|
var oldOrientation = this.orientation; |
||||||
|
this.orientation = a; |
||||||
|
firePropertyChange("orientation", oldOrientation, orientation); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean isSelected() { |
||||||
|
return selected; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setSelected(final boolean selected) { |
||||||
|
if (selected == this.selected) return; |
||||||
|
boolean oldSelected = this.selected; |
||||||
|
this.selected = selected; |
||||||
|
firePropertyChange("selected", oldSelected, selected); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int getAccelerator() { |
||||||
|
return accelerator; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setAccelerator(final int accelerator) { |
||||||
|
if (this.accelerator == accelerator) return; |
||||||
|
int oldAccelerator = this.accelerator; |
||||||
|
this.accelerator = accelerator; |
||||||
|
firePropertyChange("accelerator", oldAccelerator, accelerator); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public TabFrame getTabFrame() { |
||||||
|
return parent; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setTabFrame(final TabFrame parent) { |
||||||
|
var old = this.parent; |
||||||
|
this.parent = parent; |
||||||
|
firePropertyChange("tabFrame", old, parent); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,167 @@ |
|||||||
|
/* |
||||||
|
* 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.tabframe; |
||||||
|
|
||||||
|
import com.weis.darklaf.components.alignment.Alignment; |
||||||
|
import com.weis.darklaf.icons.EmptyIcon; |
||||||
|
import com.weis.darklaf.ui.tabframe.DarkTabFrameTabLabelUI; |
||||||
|
import org.jetbrains.annotations.NotNull; |
||||||
|
import org.jetbrains.annotations.Nullable; |
||||||
|
|
||||||
|
import javax.swing.*; |
||||||
|
import java.awt.*; |
||||||
|
import java.util.Objects; |
||||||
|
|
||||||
|
/** |
||||||
|
* Tab Component for {@link TabFrame}. |
||||||
|
* |
||||||
|
* @author Jannis Weis |
||||||
|
*/ |
||||||
|
public class TabFrameTabLabel extends JLabel implements TabFrameTab { |
||||||
|
|
||||||
|
private TabFrame parent; |
||||||
|
private Alignment orientation; |
||||||
|
private String title; |
||||||
|
private boolean selected; |
||||||
|
private int accelerator; |
||||||
|
private int index; |
||||||
|
|
||||||
|
/** |
||||||
|
* Create new TabComponent for the frame of {@link TabFrame}. |
||||||
|
* |
||||||
|
* @param title the title. |
||||||
|
* @param icon the icon. |
||||||
|
* @param orientation the alignment. |
||||||
|
* @param index the index. |
||||||
|
* @param parent the parent layout manager. |
||||||
|
*/ |
||||||
|
public TabFrameTabLabel(final String title, final Icon icon, final Alignment orientation, |
||||||
|
final int index, @NotNull final TabFrame parent) { |
||||||
|
this.index = index; |
||||||
|
this.accelerator = -1; |
||||||
|
this.parent = parent; |
||||||
|
setOrientation(orientation); |
||||||
|
setIcon(icon == null ? EmptyIcon.create(0) : icon); |
||||||
|
setTitle(title); |
||||||
|
setText(title); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getUIClassID() { |
||||||
|
return "TabFrameTabLabelUI"; |
||||||
|
} |
||||||
|
|
||||||
|
public void setUI(final DarkTabFrameTabLabelUI ui) { |
||||||
|
super.setUI(ui); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int getIndex() { |
||||||
|
return index; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setIndex(final int index) { |
||||||
|
this.index = index; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Component getComponent() { |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Alignment getOrientation() { |
||||||
|
return orientation; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setOrientation(final Alignment a) { |
||||||
|
if (this.orientation == a) return; |
||||||
|
var oldOrientation = this.orientation; |
||||||
|
this.orientation = a; |
||||||
|
firePropertyChange("orientation", oldOrientation, orientation); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean isSelected() { |
||||||
|
return selected; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setSelected(final boolean selected) { |
||||||
|
if (selected == this.selected) return; |
||||||
|
boolean oldSelected = this.selected; |
||||||
|
this.selected = selected; |
||||||
|
firePropertyChange("selected", oldSelected, selected); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int getAccelerator() { |
||||||
|
return accelerator; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setAccelerator(final int accelerator) { |
||||||
|
if (this.accelerator == accelerator) return; |
||||||
|
int oldAccelerator = this.accelerator; |
||||||
|
this.accelerator = accelerator; |
||||||
|
firePropertyChange("accelerator", oldAccelerator, accelerator); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public TabFrame getTabFrame() { |
||||||
|
return parent; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setTabFrame(final TabFrame parent) { |
||||||
|
var old = this.parent; |
||||||
|
this.parent = parent; |
||||||
|
firePropertyChange("tabFrame", old, parent); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the current title. |
||||||
|
* This needn't be the same as the displayed text. |
||||||
|
* For this use {@link #getText()} instead. |
||||||
|
* |
||||||
|
* @return the current title. |
||||||
|
*/ |
||||||
|
public String getTitle() { |
||||||
|
return title; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the title of the component. |
||||||
|
* |
||||||
|
* @param title the title |
||||||
|
*/ |
||||||
|
public void setTitle(@Nullable final String title) { |
||||||
|
if (Objects.equals(title, this.title)) return; |
||||||
|
String oldTitle = this.title; |
||||||
|
this.title = title; |
||||||
|
firePropertyChange("title", oldTitle, selected); |
||||||
|
} |
||||||
|
} |
@ -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.tabframe; |
||||||
|
|
||||||
|
import javax.swing.plaf.ComponentUI; |
||||||
|
|
||||||
|
public abstract class TabFrameUI extends ComponentUI { |
||||||
|
|
||||||
|
public abstract int getTabSize(TabFrame tabFrame); |
||||||
|
} |
@ -0,0 +1,154 @@ |
|||||||
|
/* |
||||||
|
* 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.tabframe; |
||||||
|
|
||||||
|
import javax.swing.*; |
||||||
|
import java.awt.*; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.Collection; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* Tabbed Popup Component for {@link TabFrame}. |
||||||
|
* |
||||||
|
* @author Jannis Weis |
||||||
|
*/ |
||||||
|
public class TabbedPopup extends PanelPopup { |
||||||
|
|
||||||
|
private JTabbedPane tabbedPane; |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a Popup that can hold multiple content panes using a TabbedPane. |
||||||
|
* |
||||||
|
* @param title the title of the popup. |
||||||
|
*/ |
||||||
|
public TabbedPopup(final String title) { |
||||||
|
this(title, null, null); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a Popup that can hold multiple content panes using a TabbedPane. |
||||||
|
* |
||||||
|
* @param title the title of the popup. |
||||||
|
* @param icon the icon of the popup. |
||||||
|
* @param content the initial content pane. |
||||||
|
*/ |
||||||
|
public TabbedPopup(final String title, final Icon icon, final Component content) { |
||||||
|
super(title, icon, content); |
||||||
|
setIcon(icon); |
||||||
|
setTitle(title); |
||||||
|
setContentPane(content); |
||||||
|
setEnabled(false); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a Popup that can hold multiple content panes using a TabbedPane. |
||||||
|
* |
||||||
|
* @param title the title of the popup. |
||||||
|
* @param icon the icon of the popup. |
||||||
|
*/ |
||||||
|
public TabbedPopup(final String title, final Icon icon) { |
||||||
|
this(title, icon, null); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a Popup that can hold multiple content panes using a TabbedPane. |
||||||
|
* |
||||||
|
* @param title the title of the popup. |
||||||
|
* @param content the initial content pane. |
||||||
|
*/ |
||||||
|
public TabbedPopup(final String title, final Component content) { |
||||||
|
this(title, null, content); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns all currently installed content panes. |
||||||
|
* i.e. this area all components currently added as tabs to |
||||||
|
* the TabbedPane. |
||||||
|
* {@see {@link #getTabbedPane()}}. |
||||||
|
* |
||||||
|
* @return a collection of components. |
||||||
|
*/ |
||||||
|
public Collection<Component> getContentPanes() { |
||||||
|
int size = getTabbedPane().getTabCount(); |
||||||
|
List<Component> compList = new ArrayList<>(size); |
||||||
|
for (int i = 0; i < size; i++) { |
||||||
|
compList.add(tabbedPane.getComponentAt(i)); |
||||||
|
} |
||||||
|
return compList; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the tabbed pane. |
||||||
|
* |
||||||
|
* @return the tabbed pane. |
||||||
|
*/ |
||||||
|
public JTabbedPane getTabbedPane() { |
||||||
|
if (tabbedPane == null) { |
||||||
|
tabbedPane = createTabbedPane(); |
||||||
|
} |
||||||
|
return tabbedPane; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates the tabbedPane. |
||||||
|
* Overwrite this method to customize the tabbedPane used. |
||||||
|
* |
||||||
|
* @return a tabbed pane. |
||||||
|
*/ |
||||||
|
protected JTabbedPane createTabbedPane() { |
||||||
|
return new JTabbedPane(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getUIClassID() { |
||||||
|
return "TabFrameTabbedPopupUI"; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the currently selected component from the TabbedPane. |
||||||
|
* {@see {@link #getTabbedPane()}}. |
||||||
|
* |
||||||
|
* @return the selected component. |
||||||
|
*/ |
||||||
|
public Component getContentPane() { |
||||||
|
return tabbedPane.getSelectedComponent(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Adds the component to the tabbed pane if it isn't already added. |
||||||
|
* This method exists to conform the interface methods. |
||||||
|
* It is preferred to directly add to the TabbedPane obtained by |
||||||
|
* {@link #getTabbedPane()}. |
||||||
|
* |
||||||
|
* @param component the component to add. |
||||||
|
*/ |
||||||
|
public void setContentPane(final Component component) { |
||||||
|
if (component == null) return; |
||||||
|
if (getContentPanes().contains(component)) { |
||||||
|
return; |
||||||
|
} |
||||||
|
getTabbedPane().addTab(component.getName(), component); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,159 @@ |
|||||||
|
/* |
||||||
|
* 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.tabframe; |
||||||
|
|
||||||
|
import javax.swing.*; |
||||||
|
import javax.swing.plaf.basic.BasicSplitPaneUI; |
||||||
|
|
||||||
|
public class ToggleSplitPane extends JSplitPane { |
||||||
|
|
||||||
|
private int disabledPos = 0; |
||||||
|
private int disabledMax = -1; |
||||||
|
private boolean resizable = true; |
||||||
|
private boolean lastEnabled = true; |
||||||
|
private double restorePercentage; |
||||||
|
|
||||||
|
|
||||||
|
public ToggleSplitPane() { |
||||||
|
this(null); |
||||||
|
} |
||||||
|
|
||||||
|
public ToggleSplitPane(final String name) { |
||||||
|
setName(name); |
||||||
|
putClientProperty("JSplitPane.style", "invisible"); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public boolean isResizable() { |
||||||
|
return resizable; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set if the split pane should be able to resize. |
||||||
|
* |
||||||
|
* @param resizable true if it should resize. |
||||||
|
*/ |
||||||
|
public void setResizable(final boolean resizable) { |
||||||
|
this.resizable = resizable; |
||||||
|
if (!resizable) { |
||||||
|
lastEnabled = isEnabled(); |
||||||
|
setEnabled(false); |
||||||
|
disabledPos = super.getDividerLocation(); |
||||||
|
disabledMax = getMaximumDividerLocation(); |
||||||
|
} else { |
||||||
|
setEnabled(lastEnabled); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setEnabled(final boolean enabled) { |
||||||
|
((BasicSplitPaneUI) getUI()).getDivider().setEnabled(enabled); |
||||||
|
} |
||||||
|
|
||||||
|
public void savePosition() { |
||||||
|
savePosition(getRelativeDividerLocation()); |
||||||
|
} |
||||||
|
|
||||||
|
public void savePosition(final double position) { |
||||||
|
restorePercentage = Math.max(0, Math.min(1.0, position)); |
||||||
|
} |
||||||
|
|
||||||
|
public double getRelativeDividerLocation() { |
||||||
|
if (getOrientation() == HORIZONTAL_SPLIT) { |
||||||
|
return getDividerLocation() / (double) getWidth(); |
||||||
|
} else { |
||||||
|
return getDividerLocation() / (double) getHeight(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void restorePosition() { |
||||||
|
setDividerLocation(restorePercentage); |
||||||
|
doLayout(); |
||||||
|
} |
||||||
|
|
||||||
|
public void forceSetDividerLocation(final int location) { |
||||||
|
super.setDividerLocation(location); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int getDividerLocation() { |
||||||
|
if (resizable) { |
||||||
|
return super.getDividerLocation(); |
||||||
|
} else { |
||||||
|
return disabledMax == disabledPos ? getMaximumDividerLocation() : disabledPos; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void forceSetDividerLocation(final double proportionalLocation) { |
||||||
|
if (proportionalLocation < 0.0 || |
||||||
|
proportionalLocation > 1.0) { |
||||||
|
throw new IllegalArgumentException("proportional location must " |
||||||
|
+ "be between 0.0 and 1.0."); |
||||||
|
} |
||||||
|
if (getOrientation() == VERTICAL_SPLIT) { |
||||||
|
super.setDividerLocation((int) ((double) (getHeight()) * proportionalLocation)); |
||||||
|
} else { |
||||||
|
super.setDividerLocation((int) ((double) (getWidth()) * proportionalLocation)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int getLastDividerLocation() { |
||||||
|
if (resizable) { |
||||||
|
return super.getLastDividerLocation(); |
||||||
|
} else { |
||||||
|
return disabledMax == disabledPos ? getMaximumDividerLocation() : disabledPos; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setDividerLocation(final int location) { |
||||||
|
if (resizable) { |
||||||
|
super.setDividerLocation(location); |
||||||
|
} else if (disabledPos == disabledMax) { |
||||||
|
super.setDividerLocation(getMaximumDividerLocation()); |
||||||
|
doLayout(); |
||||||
|
} else if (disabledPos == 0) { |
||||||
|
super.setDividerLocation(0); |
||||||
|
doLayout(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public int getMaximumDividerLocation() { |
||||||
|
int max = getOrientation() == HORIZONTAL_SPLIT ? getWidth() : getHeight(); |
||||||
|
return Math.max(max, getMinimumDividerLocation()); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int getMinimumDividerLocation() { |
||||||
|
var comp = getRightComponent(); |
||||||
|
return comp == null ? 0 : getOrientation() == HORIZONTAL_SPLIT |
||||||
|
? comp.getMinimumSize().width |
||||||
|
: comp.getMinimumSize().height; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
@ -0,0 +1,91 @@ |
|||||||
|
/* |
||||||
|
* 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.decorators; |
||||||
|
|
||||||
|
import org.jetbrains.annotations.Contract; |
||||||
|
|
||||||
|
import javax.swing.*; |
||||||
|
import java.awt.event.MouseEvent; |
||||||
|
import java.awt.event.MouseListener; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Jannis Weis |
||||||
|
*/ |
||||||
|
public class HoverListener implements MouseListener { |
||||||
|
|
||||||
|
private final JComponent component; |
||||||
|
private boolean hover = false; |
||||||
|
private boolean scheduled = false; |
||||||
|
|
||||||
|
@Contract(pure = true) |
||||||
|
public HoverListener(final JComponent component) { |
||||||
|
this.component = component; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isHover() { |
||||||
|
return hover; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void mouseClicked(final MouseEvent e) { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void mousePressed(final MouseEvent e) { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void mouseReleased(final MouseEvent e) { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void mouseEntered(final MouseEvent e) { |
||||||
|
if (!hover) { |
||||||
|
hover = true; |
||||||
|
scheduleRepaint(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void scheduleRepaint() { |
||||||
|
if (!scheduled) { |
||||||
|
scheduled = true; |
||||||
|
SwingUtilities.invokeLater(() -> { |
||||||
|
component.invalidate(); |
||||||
|
component.repaint(); |
||||||
|
scheduled = false; |
||||||
|
}); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void mouseExited(final MouseEvent e) { |
||||||
|
if (hover) { |
||||||
|
hover = false; |
||||||
|
scheduleRepaint(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,116 @@ |
|||||||
|
/* |
||||||
|
* 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.icons; |
||||||
|
|
||||||
|
import com.weis.darklaf.components.alignment.Alignment; |
||||||
|
import org.jetbrains.annotations.Contract; |
||||||
|
|
||||||
|
import javax.swing.*; |
||||||
|
import java.awt.*; |
||||||
|
import java.awt.geom.AffineTransform; |
||||||
|
|
||||||
|
public class RotatableIcon implements Icon { |
||||||
|
|
||||||
|
private Icon icon; |
||||||
|
private Alignment alignment; |
||||||
|
|
||||||
|
@Contract(pure = true) |
||||||
|
public RotatableIcon() { |
||||||
|
this(null); |
||||||
|
} |
||||||
|
|
||||||
|
@Contract(pure = true) |
||||||
|
public RotatableIcon(final Icon icon) { |
||||||
|
setIcon(icon); |
||||||
|
this.alignment = null; |
||||||
|
} |
||||||
|
|
||||||
|
public void setIcon(final Icon icon) { |
||||||
|
this.icon = icon == null ? EmptyIcon.create(0) : icon; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void paintIcon(final Component c, final Graphics g, final int x, final int y) { |
||||||
|
if (icon instanceof DarkSVGIcon) { |
||||||
|
((DarkSVGIcon) icon).paintIcon(c, g, x, y, getAngle()); |
||||||
|
} else if (icon != null) { |
||||||
|
Graphics2D g2 = (Graphics2D) g.create(); |
||||||
|
AffineTransform transform = new AffineTransform(); |
||||||
|
transform.rotate(getAngle(), x + getIconWidth() / 2.0, y + getIconHeight() / 2.0); |
||||||
|
g2.transform(transform); |
||||||
|
icon.paintIcon(c, g2, x, y); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Contract(pure = true) |
||||||
|
private double getAngle() { |
||||||
|
double angle = 0.0; |
||||||
|
switch (alignment) { |
||||||
|
case NORTH: |
||||||
|
case CENTER: |
||||||
|
angle = 0.0; |
||||||
|
break; |
||||||
|
case SOUTH: |
||||||
|
angle = 180.0; |
||||||
|
break; |
||||||
|
case EAST: |
||||||
|
angle = 90.0; |
||||||
|
break; |
||||||
|
case WEST: |
||||||
|
angle = 270.0; |
||||||
|
break; |
||||||
|
case NORTH_EAST: |
||||||
|
angle = 45.0; |
||||||
|
break; |
||||||
|
case NORTH_WEST: |
||||||
|
angle = 315.0; |
||||||
|
break; |
||||||
|
case SOUTH_EAST: |
||||||
|
angle = 135.0; |
||||||
|
break; |
||||||
|
case SOUTH_WEST: |
||||||
|
angle = 225.0; |
||||||
|
break; |
||||||
|
} |
||||||
|
return Math.toRadians(angle); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int getIconWidth() { |
||||||
|
return icon.getIconWidth(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int getIconHeight() { |
||||||
|
return icon.getIconHeight(); |
||||||
|
} |
||||||
|
|
||||||
|
public Alignment getOrientation() { |
||||||
|
return alignment; |
||||||
|
} |
||||||
|
|
||||||
|
public void setOrientation(final Alignment alignment) { |
||||||
|
this.alignment = alignment; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,33 @@ |
|||||||
|
/* |
||||||
|
* 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.splitpane; |
||||||
|
|
||||||
|
import javax.swing.plaf.BorderUIResource; |
||||||
|
|
||||||
|
public class DarkSplitPaneBorder extends BorderUIResource.EmptyBorderUIResource { |
||||||
|
|
||||||
|
public DarkSplitPaneBorder() { |
||||||
|
super(0, 0, 0, 0); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,382 @@ |
|||||||
|
/* |
||||||
|
* 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.JLabelUIResource; |
||||||
|
import com.weis.darklaf.components.alignment.Alignment; |
||||||
|
import com.weis.darklaf.components.border.MutableLineBorder; |
||||||
|
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.ui.panel.DarkPanelUI; |
||||||
|
import com.weis.darklaf.util.DarkUIUtil; |
||||||
|
import org.jetbrains.annotations.Contract; |
||||||
|
import org.jetbrains.annotations.NotNull; |
||||||
|
|
||||||
|
import javax.swing.*; |
||||||
|
import javax.swing.border.EmptyBorder; |
||||||
|
import javax.swing.plaf.ComponentUI; |
||||||
|
import javax.swing.plaf.UIResource; |
||||||
|
import java.awt.*; |
||||||
|
import java.awt.event.AWTEventListener; |
||||||
|
import java.awt.event.ActionEvent; |
||||||
|
import java.awt.event.FocusEvent; |
||||||
|
import java.awt.event.MouseEvent; |
||||||
|
import java.beans.PropertyChangeEvent; |
||||||
|
import java.beans.PropertyChangeListener; |
||||||
|
|
||||||
|
public class DarkPanelPopupUI extends DarkPanelUI implements PropertyChangeListener, AWTEventListener { |
||||||
|
|
||||||
|
|
||||||
|
protected HeaderButton closeButton; |
||||||
|
private final Action closeAction = new AbstractAction() { |
||||||
|
@Override |
||||||
|
public void actionPerformed(final ActionEvent e) { |
||||||
|
closeButton.doClick(); |
||||||
|
} |
||||||
|
}; |
||||||
|
protected JPanel content; |
||||||
|
protected JLabel label; |
||||||
|
protected Color headerFocusBackground; |
||||||
|
protected Color headerButtonFocusHoverBackground; |
||||||
|
protected Color headerButtonFocusClickBackground; |
||||||
|
protected Color headerBackground; |
||||||
|
protected Color headerButtonHoverBackground; |
||||||
|
protected Color headerButtonClickBackground; |
||||||
|
protected String accelerator; |
||||||
|
private PanelPopup popupComponent; |
||||||
|
private JPanel header; |
||||||
|
private MutableLineBorder headerBorder; |
||||||
|
private MutableLineBorder contentBorder; |
||||||
|
private boolean oldFocus; |
||||||
|
|
||||||
|
@NotNull |
||||||
|
@Contract("_ -> new") |
||||||
|
public static ComponentUI createUI(final JComponent c) { |
||||||
|
return new DarkPanelPopupUI(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void installUI(final JComponent c) { |
||||||
|
popupComponent = (PanelPopup) c; |
||||||
|
super.installUI(c); |
||||||
|
installDefaults(); |
||||||
|
installComponents(); |
||||||
|
installListeners(); |
||||||
|
} |
||||||
|
|
||||||
|
protected void installDefaults() { |
||||||
|
headerBackground = UIManager.getColor("TabFramePopup.headerBackground"); |
||||||
|
headerButtonHoverBackground = UIManager.getColor("TabFramePopup.headerButtonHoverBackground"); |
||||||
|
headerButtonClickBackground = UIManager.getColor("TabFramePopup.headerButtonClickBackground"); |
||||||
|
headerFocusBackground = UIManager.getColor("TabFramePopup.headerFocusBackground"); |
||||||
|
headerButtonFocusHoverBackground = UIManager.getColor("TabFramePopup.headerButtonFocusHoverBackground"); |
||||||
|
headerButtonFocusClickBackground = UIManager.getColor("TabFramePopup.headerButtonFocusClickBackground"); |
||||||
|
accelerator = UIManager.getString("TabFramePopup.closeAccelerator"); |
||||||
|
popupComponent.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) |
||||||
|
.put(KeyStroke.getKeyStroke(accelerator), accelerator); |
||||||
|
popupComponent.getActionMap().put(accelerator, closeAction); |
||||||
|
popupComponent.setLayout(new BorderLayout()); |
||||||
|
} |
||||||
|
|
||||||
|
protected void installComponents() { |
||||||
|
closeButton = createCloseButton(); |
||||||
|
label = createLabel(); |
||||||
|
header = createHeader(); |
||||||
|
|
||||||
|
JPanel headerContainer = new JPanel(new BorderLayout()); |
||||||
|
setHeaderBackground(false); |
||||||
|
headerContainer.add(header, BorderLayout.CENTER); |
||||||
|
headerContainer.setMinimumSize(UIManager.getDimension("TabFramePopup.minimumHeaderSize")); |
||||||
|
|
||||||
|
content = new JPanel(new BorderLayout()); |
||||||
|
content.add(popupComponent.getContentPane(), BorderLayout.CENTER); |
||||||
|
|
||||||
|
popupComponent.setLayout(new BorderLayout()); |
||||||
|
popupComponent.add(headerContainer, BorderLayout.NORTH); |
||||||
|
popupComponent.add(content, BorderLayout.CENTER); |
||||||
|
|
||||||
|
headerBorder = createBorder(); |
||||||
|
contentBorder = createBorder(); |
||||||
|
headerContainer.setBorder(headerBorder); |
||||||
|
content.setBorder(contentBorder); |
||||||
|
} |
||||||
|
|
||||||
|
protected void installListeners() { |
||||||
|
popupComponent.addPropertyChangeListener(this); |
||||||
|
Toolkit.getDefaultToolkit().addAWTEventListener(this, AWTEvent.FOCUS_EVENT_MASK); |
||||||
|
var frame = popupComponent.getTabFrame(); |
||||||
|
if (frame != null) { |
||||||
|
frame.addPropertyChangeListener(this); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected HeaderButton createCloseButton() { |
||||||
|
var closeButton = new HeaderButton(UIManager.getIcon("TabFramePopup.close.icon"), this); |
||||||
|
closeButton.setBorder(new EmptyBorder(4, 4, 4, 4)); |
||||||
|
closeButton.addActionListener(e -> popupComponent.close()); |
||||||
|
var tooltip = UIManager.getString("TabFramePopup.closeTooltipText"); |
||||||
|
closeButton.setToolTipText(tooltip); |
||||||
|
return closeButton; |
||||||
|
} |
||||||
|
|
||||||
|
protected JLabel createLabel() { |
||||||
|
var label = new JLabelUIResource(popupComponent.getTitle(), popupComponent.getIcon(), JLabel.LEFT); |
||||||
|
label.setOpaque(false); |
||||||
|
return label; |
||||||
|
} |
||||||
|
|
||||||
|
protected JPanel createHeader() { |
||||||
|
var header = new JPanel(); |
||||||
|
header.setLayout(new BoxLayout(header, BoxLayout.X_AXIS)); |
||||||
|
header.add(Box.createHorizontalStrut(5)); |
||||||
|
header.add(label); |
||||||
|
header.add(Box.createGlue()); |
||||||
|
header.add(closeButton); |
||||||
|
header.add(Box.createHorizontalStrut(1)); |
||||||
|
header.setBorder(UIManager.getBorder("TabFramePopup.headerBorder")); |
||||||
|
return header; |
||||||
|
} |
||||||
|
|
||||||
|
protected void setHeaderBackground(final boolean focus) { |
||||||
|
closeButton.setFocus(focus); |
||||||
|
if (header != null) { |
||||||
|
header.setBackground(focus ? headerFocusBackground : headerBackground); |
||||||
|
} |
||||||
|
if (oldFocus != focus) { |
||||||
|
if (header != null) { |
||||||
|
header.repaint(); |
||||||
|
} |
||||||
|
closeButton.repaint(); |
||||||
|
oldFocus = focus; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected MutableLineBorder createBorder() { |
||||||
|
var color = UIManager.getColor("TabFramePopup.borderColor"); |
||||||
|
return new MutableLineBorder.UIResource(0, 0, 0, 0, color); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void uninstallUI(final JComponent c) { |
||||||
|
super.uninstallUI(c); |
||||||
|
uninstallComponents(); |
||||||
|
uninstallListeners(); |
||||||
|
popupComponent.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) |
||||||
|
.remove(KeyStroke.getKeyStroke(accelerator)); |
||||||
|
popupComponent.getActionMap().remove(accelerator); |
||||||
|
popupComponent = null; |
||||||
|
} |
||||||
|
|
||||||
|
protected void uninstallComponents() { |
||||||
|
popupComponent.removeAll(); |
||||||
|
} |
||||||
|
|
||||||
|
protected void uninstallListeners() { |
||||||
|
popupComponent.removePropertyChangeListener(this); |
||||||
|
Toolkit.getDefaultToolkit().removeAWTEventListener(this); |
||||||
|
var frame = popupComponent.getTabFrame(); |
||||||
|
if (frame != null) { |
||||||
|
frame.removePropertyChangeListener(this); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public final Dimension getPreferredSize(@NotNull final JComponent c) { |
||||||
|
if (!c.isEnabled()) { |
||||||
|
return new Dimension(0, 0); |
||||||
|
} else { |
||||||
|
return super.getPreferredSize(c); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Dimension getMinimumSize(@NotNull final JComponent c) { |
||||||
|
if (!c.isEnabled()) { |
||||||
|
return new Dimension(0, 0); |
||||||
|
} else { |
||||||
|
return super.getMinimumSize(c); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Dimension getMaximumSize(@NotNull final JComponent c) { |
||||||
|
if (!c.isEnabled()) { |
||||||
|
return new Dimension(0, 0); |
||||||
|
} else { |
||||||
|
return super.getMaximumSize(c); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void propertyChange(@NotNull final PropertyChangeEvent evt) { |
||||||
|
var key = evt.getPropertyName(); |
||||||
|
if ("open".equals(key)) { |
||||||
|
if (Boolean.TRUE.equals(evt.getNewValue())) { |
||||||
|
setHeaderBackground(true); |
||||||
|
} |
||||||
|
} else if ("content".equals(key)) { |
||||||
|
if (content == null) return; |
||||||
|
content.add((Component) evt.getNewValue(), BorderLayout.CENTER); |
||||||
|
content.invalidate(); |
||||||
|
} else if ("title".equals(key)) { |
||||||
|
if (label == null) return; |
||||||
|
label.setText(evt.getNewValue().toString()); |
||||||
|
label.repaint(); |
||||||
|
} else if ("icon".equals(key)) { |
||||||
|
if (label == null) return; |
||||||
|
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()) { |
||||||
|
updateBorder(true); |
||||||
|
} |
||||||
|
} |
||||||
|
} else if ("tabFrame".equals(key)) { |
||||||
|
var oldVal = evt.getOldValue(); |
||||||
|
var newVal = evt.getNewValue(); |
||||||
|
if (oldVal instanceof TabFrame) { |
||||||
|
((TabFrame) oldVal).removePropertyChangeListener(this); |
||||||
|
} |
||||||
|
if (newVal instanceof TabFrame) { |
||||||
|
((TabFrame) newVal).addPropertyChangeListener(this); |
||||||
|
} |
||||||
|
} else if ("peerInsets".equals(key)) { |
||||||
|
updateBorder(false); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected void updateBorder(final boolean notifyPeer) { |
||||||
|
if (popupComponent.getTabFrame() != null) { |
||||||
|
var tabFrame = popupComponent.getTabFrame(); |
||||||
|
var status = tabFrame.getContentPane().getStatus(); |
||||||
|
var alignment = popupComponent.getAlignment(); |
||||||
|
var insets = getBorderSize(alignment, status); |
||||||
|
|
||||||
|
applyBorderInsets(insets); |
||||||
|
popupComponent.doLayout(); |
||||||
|
popupComponent.repaint(); |
||||||
|
if (notifyPeer) { |
||||||
|
var peer = tabFrame.getPopupComponentAt(tabFrame.getPeer(popupComponent.getAlignment())); |
||||||
|
peer.firePropertyChange("peerInsets", 0, 1); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@NotNull |
||||||
|
protected Insets getBorderSize(@NotNull final Alignment a, final boolean[] info) { |
||||||
|
var insets = new Insets(0, 0, 0, 0); |
||||||
|
switch (a) { |
||||||
|
case NORTH: |
||||||
|
case NORTH_EAST: |
||||||
|
if (info[Alignment.NORTH.getIndex()] || info[Alignment.NORTH_EAST.getIndex()]) { |
||||||
|
insets.bottom = 1; |
||||||
|
} |
||||||
|
if (info[Alignment.NORTH.getIndex()] && a == Alignment.NORTH_EAST) { |
||||||
|
insets.left = 1; |
||||||
|
} |
||||||
|
return insets; |
||||||
|
case SOUTH: |
||||||
|
case SOUTH_WEST: |
||||||
|
if (info[Alignment.SOUTH_WEST.getIndex()] || info[Alignment.SOUTH.getIndex()]) { |
||||||
|
insets.top = 1; |
||||||
|
} |
||||||
|
if (info[Alignment.SOUTH_WEST.getIndex()] && a == Alignment.SOUTH) { |
||||||
|
insets.left = 1; |
||||||
|
} |
||||||
|
return insets; |
||||||
|
case EAST: |
||||||
|
case SOUTH_EAST: |
||||||
|
if (info[Alignment.EAST.getIndex()] || info[Alignment.SOUTH_EAST.getIndex()]) { |
||||||
|
insets.left = 1; |
||||||
|
} |
||||||
|
if (info[Alignment.EAST.getIndex()] && a == Alignment.SOUTH_EAST) { |
||||||
|
insets.top = 1; |
||||||
|
} |
||||||
|
return insets; |
||||||
|
case WEST: |
||||||
|
case NORTH_WEST: |
||||||
|
if (info[Alignment.NORTH_WEST.getIndex()] || info[Alignment.WEST.getIndex()]) { |
||||||
|
insets.right = 1; |
||||||
|
} |
||||||
|
if (info[Alignment.NORTH_WEST.getIndex()] && a == Alignment.WEST) { |
||||||
|
insets.top = 1; |
||||||
|
} |
||||||
|
return insets; |
||||||
|
default: |
||||||
|
return insets; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected void applyBorderInsets(@NotNull final Insets insets) { |
||||||
|
headerBorder.setInsets(insets.top, insets.left, 1, insets.right); |
||||||
|
contentBorder.setInsets(0, insets.left, insets.bottom, insets.right); |
||||||
|
} |
||||||
|
|
||||||
|
protected boolean hasFocus() { |
||||||
|
return oldFocus; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void eventDispatched(@NotNull final AWTEvent event) { |
||||||
|
if (event.getID() == FocusEvent.FOCUS_GAINED) { |
||||||
|
boolean focus = DarkUIUtil.hasFocus(popupComponent); |
||||||
|
setHeaderBackground(focus); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected static final class HeaderButton extends JButton implements UIResource { |
||||||
|
|
||||||
|
private final ToolTipContext context = new ToolTipContext(this); |
||||||
|
private final DarkPanelPopupUI ui; |
||||||
|
|
||||||
|
public HeaderButton(@NotNull final Icon icon, final DarkPanelPopupUI ui) { |
||||||
|
super(icon); |
||||||
|
this.ui = ui; |
||||||
|
putClientProperty("JButton.buttonType", "square"); |
||||||
|
putClientProperty("JButton.forceRoundCorner", Boolean.TRUE); |
||||||
|
putClientProperty("JButton.variant", "shadow"); |
||||||
|
setRolloverEnabled(true); |
||||||
|
setFocus(false); |
||||||
|
setOpaque(false); |
||||||
|
} |
||||||
|
|
||||||
|
public void setFocus(final boolean focus) { |
||||||
|
putClientProperty("JButton.shadow.hover", focus ? ui.headerButtonFocusHoverBackground |
||||||
|
: ui.headerButtonHoverBackground); |
||||||
|
putClientProperty("JButton.shadow.click", focus ? ui.headerButtonFocusClickBackground |
||||||
|
: ui.headerButtonClickBackground); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Point getToolTipLocation(final MouseEvent event) { |
||||||
|
return context.getToolTipLocation(event); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public JToolTip createToolTip() { |
||||||
|
return context.getToolTip(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,33 @@ |
|||||||
|
/* |
||||||
|
* 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 javax.swing.plaf.BorderUIResource; |
||||||
|
|
||||||
|
public class DarkTabFramePopupHeaderBorder extends BorderUIResource.EmptyBorderUIResource { |
||||||
|
|
||||||
|
public DarkTabFramePopupHeaderBorder() { |
||||||
|
super(1, 0, 1, 0); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,33 @@ |
|||||||
|
/* |
||||||
|
* 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 javax.swing.plaf.BorderUIResource; |
||||||
|
|
||||||
|
public class DarkTabFrameTabBorder extends BorderUIResource.EmptyBorderUIResource { |
||||||
|
|
||||||
|
public DarkTabFrameTabBorder() { |
||||||
|
super(2, 5, 2, 5); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,190 @@ |
|||||||
|
/* |
||||||
|
* 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.TabFrame; |
||||||
|
import com.weis.darklaf.components.tabframe.TabFrameTabContainer; |
||||||
|
import com.weis.darklaf.decorators.HoverListener; |
||||||
|
import com.weis.darklaf.ui.panel.DarkPanelUI; |
||||||
|
import com.weis.darklaf.util.DarkUIUtil; |
||||||
|
import org.jetbrains.annotations.Contract; |
||||||
|
import org.jetbrains.annotations.NotNull; |
||||||
|
|
||||||
|
import javax.swing.*; |
||||||
|
import javax.swing.plaf.ComponentUI; |
||||||
|
import java.awt.*; |
||||||
|
import java.awt.event.ActionEvent; |
||||||
|
import java.awt.event.MouseAdapter; |
||||||
|
import java.awt.event.MouseEvent; |
||||||
|
import java.awt.event.MouseListener; |
||||||
|
import java.beans.PropertyChangeEvent; |
||||||
|
import java.beans.PropertyChangeListener; |
||||||
|
|
||||||
|
public class DarkTabFrameTabContainerUI extends DarkPanelUI implements PropertyChangeListener { |
||||||
|
|
||||||
|
protected TabFrameTabContainer tabContainer; |
||||||
|
private final MouseListener mouseListener = new MouseAdapter() { |
||||||
|
@Override |
||||||
|
public void mousePressed(final MouseEvent e) { |
||||||
|
if (SwingUtilities.isLeftMouseButton(e)) { |
||||||
|
tabContainer.getTabFrame().toggleTab(tabContainer.getOrientation(), tabContainer.getIndex(), |
||||||
|
!tabContainer.isSelected()); |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
private HoverListener hoverListener; |
||||||
|
private Color selectedColor; |
||||||
|
private Color hoverColor; |
||||||
|
|
||||||
|
@NotNull |
||||||
|
@Contract("_ -> new") |
||||||
|
public static ComponentUI createUI(final JComponent c) { |
||||||
|
return new DarkTabFrameTabContainerUI(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void installUI(final JComponent c) { |
||||||
|
tabContainer = (TabFrameTabContainer) c; |
||||||
|
super.installUI(c); |
||||||
|
installDefaults(tabContainer); |
||||||
|
installListeners(); |
||||||
|
installAccelerator(tabContainer.getTabFrame()); |
||||||
|
} |
||||||
|
|
||||||
|
protected void installListeners() { |
||||||
|
hoverListener = new HoverListener(tabContainer); |
||||||
|
tabContainer.addMouseListener(hoverListener); |
||||||
|
tabContainer.addPropertyChangeListener(this); |
||||||
|
tabContainer.addMouseListener(mouseListener); |
||||||
|
var cont = tabContainer.getContent(); |
||||||
|
if (cont != null) { |
||||||
|
cont.addMouseListener(hoverListener); |
||||||
|
cont.addMouseListener(mouseListener); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
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); |
||||||
|
tabContainer.removeMouseListener(hoverListener); |
||||||
|
tabContainer.removeMouseListener(mouseListener); |
||||||
|
tabContainer.removePropertyChangeListener(this); |
||||||
|
var cont = tabContainer.getContent(); |
||||||
|
if (cont != null) { |
||||||
|
cont.removeMouseListener(hoverListener); |
||||||
|
cont.removeMouseListener(mouseListener); |
||||||
|
} |
||||||
|
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) { |
||||||
|
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(); |
||||||
|
if ("content".equals(key)) { |
||||||
|
var oldVal = evt.getOldValue(); |
||||||
|
var newVal = evt.getNewValue(); |
||||||
|
if (oldVal instanceof Component) { |
||||||
|
((Component) oldVal).removeMouseListener(mouseListener); |
||||||
|
((Component) oldVal).removeMouseListener(hoverListener); |
||||||
|
} |
||||||
|
if (newVal instanceof Component) { |
||||||
|
((Component) newVal).addMouseListener(mouseListener); |
||||||
|
((Component) newVal).addMouseListener(hoverListener); |
||||||
|
} |
||||||
|
} else if ("selected".equals(key)) { |
||||||
|
if (tabContainer == null) return; |
||||||
|
tabContainer.repaint(); |
||||||
|
} else if ("accelerator".equals(key)) { |
||||||
|
if (tabContainer == null) return; |
||||||
|
uninstallAccelerator(tabContainer.getTabFrame()); |
||||||
|
installAccelerator(tabContainer.getTabFrame()); |
||||||
|
} else if ("tabFrame".equals(key)) { |
||||||
|
if (evt.getOldValue() instanceof TabFrame) { |
||||||
|
uninstallAccelerator((TabFrame) evt.getOldValue()); |
||||||
|
} |
||||||
|
if (evt.getNewValue() instanceof TabFrame) { |
||||||
|
installAccelerator((TabFrame) evt.getNewValue()); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,274 @@ |
|||||||
|
/* |
||||||
|
* 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.TabFrame; |
||||||
|
import com.weis.darklaf.components.tabframe.TabFrameTabLabel; |
||||||
|
import com.weis.darklaf.decorators.HoverListener; |
||||||
|
import com.weis.darklaf.icons.RotatableIcon; |
||||||
|
import com.weis.darklaf.ui.label.DarkLabelUI; |
||||||
|
import com.weis.darklaf.util.DarkUIUtil; |
||||||
|
import org.jetbrains.annotations.Contract; |
||||||
|
import org.jetbrains.annotations.NotNull; |
||||||
|
import sun.swing.SwingUtilities2; |
||||||
|
|
||||||
|
import javax.swing.*; |
||||||
|
import javax.swing.plaf.ComponentUI; |
||||||
|
import javax.swing.plaf.basic.BasicHTML; |
||||||
|
import javax.swing.text.View; |
||||||
|
import java.awt.*; |
||||||
|
import java.awt.event.ActionEvent; |
||||||
|
import java.awt.event.MouseAdapter; |
||||||
|
import java.awt.event.MouseEvent; |
||||||
|
import java.awt.event.MouseListener; |
||||||
|
import java.beans.PropertyChangeEvent; |
||||||
|
import java.beans.PropertyChangeListener; |
||||||
|
|
||||||
|
public class DarkTabFrameTabLabelUI extends DarkLabelUI implements PropertyChangeListener { |
||||||
|
|
||||||
|
private TabFrameTabLabel tabComponent; |
||||||
|
private final MouseListener mouseListener = new MouseAdapter() { |
||||||
|
@Override |
||||||
|
public void mousePressed(final MouseEvent e) { |
||||||
|
if (SwingUtilities.isLeftMouseButton(e)) { |
||||||
|
tabComponent.getTabFrame().toggleTab(tabComponent.getOrientation(), tabComponent.getIndex(), |
||||||
|
!tabComponent.isSelected()); |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
private HoverListener hoverListener; |
||||||
|
private Color defaultFontColor; |
||||||
|
private Color selectedFontColor; |
||||||
|
private Color selectedColor; |
||||||
|
private Color hoverColor; |
||||||
|
private RotatableIcon rotatableIcon = new RotatableIcon(); |
||||||
|
private Rectangle paintIconR = new Rectangle(); |
||||||
|
private Rectangle paintTextR = new Rectangle(); |
||||||
|
|
||||||
|
@NotNull |
||||||
|
@Contract("_ -> new") |
||||||
|
public static ComponentUI createUI(final JComponent c) { |
||||||
|
return new DarkTabFrameTabLabelUI(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void paint(@NotNull final Graphics g, final JComponent c) { |
||||||
|
g.setColor(getBackground(tabComponent)); |
||||||
|
g.fillRect(0, 0, tabComponent.getWidth(), tabComponent.getHeight()); |
||||||
|
|
||||||
|
JLabel label = (JLabel) c; |
||||||
|
String text = label.getText(); |
||||||
|
Icon icon = getIcon(); |
||||||
|
|
||||||
|
if ((icon == null) && (text == null)) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
FontMetrics fm = SwingUtilities2.getFontMetrics(label, g); |
||||||
|
String clippedText = layout(label, fm, c.getWidth(), c.getHeight()); |
||||||
|
|
||||||
|
if (icon != null) { |
||||||
|
icon.paintIcon(c, g, paintIconR.x, paintIconR.y); |
||||||
|
} |
||||||
|
|
||||||
|
if (text != null) { |
||||||
|
View v = (View) c.getClientProperty(BasicHTML.propertyKey); |
||||||
|
if (v != null) { |
||||||
|
v.paint(g, paintTextR); |
||||||
|
} else { |
||||||
|
int textX = paintTextR.x; |
||||||
|
int textY = paintTextR.y + fm.getAscent(); |
||||||
|
|
||||||
|
if (label.isEnabled()) { |
||||||
|
paintEnabledText(label, g, clippedText, textX, textY); |
||||||
|
} else { |
||||||
|
paintDisabledText(label, g, clippedText, textX, textY); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void installUI(final JComponent c) { |
||||||
|
tabComponent = (TabFrameTabLabel) c; |
||||||
|
super.installUI(c); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void uninstallUI(final JComponent c) { |
||||||
|
super.uninstallUI(c); |
||||||
|
uninstallAccelerator(tabComponent.getTabFrame()); |
||||||
|
tabComponent.removeMouseListener(hoverListener); |
||||||
|
tabComponent.removeMouseListener(mouseListener); |
||||||
|
hoverListener = null; |
||||||
|
tabComponent = null; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void installDefaults(final JLabel c) { |
||||||
|
super.installDefaults(c); |
||||||
|
tabComponent.setFont(UIManager.getFont("TabFrameTab.font")); |
||||||
|
tabComponent.setOpaque(true); |
||||||
|
defaultFontColor = UIManager.getColor("TabFrameTab.foreground"); |
||||||
|
selectedColor = UIManager.getColor("TabFrameTab.selectedBackground"); |
||||||
|
hoverColor = UIManager.getColor("TabFrameTab.hoverBackground"); |
||||||
|
selectedFontColor = UIManager.getColor("TabFrameTab.selectedForeground"); |
||||||
|
LookAndFeel.installBorder(c, "TabFrameTab.border"); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void installListeners(final JLabel c) { |
||||||
|
super.installListeners(c); |
||||||
|
hoverListener = new HoverListener(tabComponent); |
||||||
|
tabComponent.addMouseListener(hoverListener); |
||||||
|
tabComponent.addMouseListener(mouseListener); |
||||||
|
installAccelerator(tabComponent.getTabFrame()); |
||||||
|
} |
||||||
|
|
||||||
|
protected void installAccelerator(final TabFrame tabFrame) { |
||||||
|
if (tabFrame == null) return; |
||||||
|
int acc = tabComponent.getAccelerator(); |
||||||
|
if (acc < 0) return; |
||||||
|
System.out.println("install"); |
||||||
|
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 = tabComponent.getOrientation(); |
||||||
|
var index = tabComponent.getIndex(); |
||||||
|
if (!tabComponent.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 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; |
||||||
|
int accelerator = tabComponent.getAccelerator(); |
||||||
|
if (accelerator >= 0 && accelerator <= 9) { |
||||||
|
tabComponent.setText(accelerator + ":" + title); |
||||||
|
tabComponent.setDisplayedMnemonicIndex(0); |
||||||
|
} else { |
||||||
|
tabComponent.setText(title); |
||||||
|
tabComponent.setDisplayedMnemonicIndex(1); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected void uninstallAccelerator(final TabFrame tabFrame) { |
||||||
|
if (tabFrame == null) return; |
||||||
|
int acc = tabComponent.getAccelerator(); |
||||||
|
String accAction = "accelerator_" + acc; |
||||||
|
tabFrame.getActionMap().remove(accAction); |
||||||
|
} |
||||||
|
|
||||||
|
public Color getBackground(@NotNull final TabFrameTabLabel tab) { |
||||||
|
return tab.isSelected() |
||||||
|
? selectedColor |
||||||
|
: hoverListener.isHover() ? hoverColor : tab.getBackground(); |
||||||
|
} |
||||||
|
|
||||||
|
protected Icon getIcon() { |
||||||
|
Icon icon = (tabComponent.isEnabled()) ? tabComponent.getIcon() : tabComponent.getDisabledIcon(); |
||||||
|
rotatableIcon.setIcon(icon); |
||||||
|
if (rotatableIcon.getOrientation() == null) { |
||||||
|
rotatableIcon.setOrientation(mapOrientation(tabComponent.getOrientation())); |
||||||
|
} |
||||||
|
return rotatableIcon; |
||||||
|
} |
||||||
|
|
||||||
|
private String layout(@NotNull final JLabel label, final FontMetrics fm, |
||||||
|
final int width, final int height) { |
||||||
|
Insets insets = label.getInsets(null); |
||||||
|
String text = label.getText(); |
||||||
|
Rectangle paintViewR = new Rectangle(); |
||||||
|
paintViewR.x = insets.left; |
||||||
|
paintViewR.y = insets.top; |
||||||
|
paintViewR.width = width - (insets.left + insets.right); |
||||||
|
paintViewR.height = height - (insets.top + insets.bottom); |
||||||
|
paintIconR.x = paintIconR.y = paintIconR.width = paintIconR.height = 0; |
||||||
|
paintTextR.x = paintTextR.y = paintTextR.width = paintTextR.height = 0; |
||||||
|
return layoutCL(label, fm, text, getIcon(), paintViewR, paintIconR, paintTextR); |
||||||
|
} |
||||||
|
|
||||||
|
protected Alignment mapOrientation(@NotNull final Alignment newValue) { |
||||||
|
switch (newValue) { |
||||||
|
case CENTER: |
||||||
|
case NORTH: |
||||||
|
case NORTH_EAST: |
||||||
|
case SOUTH: |
||||||
|
case SOUTH_WEST: |
||||||
|
return Alignment.NORTH; |
||||||
|
case EAST: |
||||||
|
case SOUTH_EAST: |
||||||
|
return Alignment.WEST; |
||||||
|
case WEST: |
||||||
|
case NORTH_WEST: |
||||||
|
return Alignment.EAST; |
||||||
|
} |
||||||
|
return Alignment.NORTH; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,188 @@ |
|||||||
|
/* |
||||||
|
* 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.border.MutableLineBorder; |
||||||
|
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.TabFrameUI; |
||||||
|
import com.weis.darklaf.util.DarkUIUtil; |
||||||
|
import org.jdesktop.jxlayer.JXLayer; |
||||||
|
import org.jetbrains.annotations.Contract; |
||||||
|
import org.jetbrains.annotations.NotNull; |
||||||
|
import org.pbjar.jxlayer.plaf.ext.transform.DefaultTransformModel; |
||||||
|
import org.pbjar.jxlayer.plaf.ext.transform.TransformUtils; |
||||||
|
|
||||||
|
import javax.swing.*; |
||||||
|
import javax.swing.plaf.ComponentUI; |
||||||
|
import java.awt.*; |
||||||
|
import java.awt.event.AWTEventListener; |
||||||
|
import java.awt.event.MouseEvent; |
||||||
|
|
||||||
|
/** |
||||||
|
* UI class for {@link TabFrame}. |
||||||
|
* |
||||||
|
* @author Jannis Weis |
||||||
|
* @since 2018 |
||||||
|
*/ |
||||||
|
public class DarkTabFrameUI extends TabFrameUI implements AWTEventListener { |
||||||
|
|
||||||
|
private TabFrame tabFrame; |
||||||
|
|
||||||
|
private JXLayer<JComponent> rotatePaneLeft; |
||||||
|
private JXLayer<JComponent> rotatePaneRight; |
||||||
|
|
||||||
|
private LayoutManager layout; |
||||||
|
private MutableLineBorder topBorder; |
||||||
|
private MutableLineBorder bottomBorder; |
||||||
|
private MutableLineBorder leftBorder; |
||||||
|
private MutableLineBorder rightBorder; |
||||||
|
private Color lineColor; |
||||||
|
|
||||||
|
@NotNull |
||||||
|
@Contract("_ -> new") |
||||||
|
public static ComponentUI createUI(final JComponent c) { |
||||||
|
return new DarkTabFrameUI(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void installUI(@NotNull final JComponent c) { |
||||||
|
tabFrame = (TabFrame) c; |
||||||
|
installDefaults(); |
||||||
|
installComponents(); |
||||||
|
installListeners(); |
||||||
|
} |
||||||
|
|
||||||
|
protected void installDefaults() { |
||||||
|
layout = createLayout(); |
||||||
|
tabFrame.setLayout(layout); |
||||||
|
lineColor = UIManager.getColor("TabFrame.line"); |
||||||
|
topBorder = new MutableLineBorder.UIResource(0, 0, 1, 0, lineColor); |
||||||
|
bottomBorder = new MutableLineBorder.UIResource(1, 0, 0, 0, lineColor); |
||||||
|
rightBorder = new MutableLineBorder.UIResource(0, 0, 1, 0, lineColor); |
||||||
|
leftBorder = new MutableLineBorder.UIResource(0, 0, 1, 0, lineColor); |
||||||
|
|
||||||
|
tabFrame.getTopTabContainer().setBorder(topBorder); |
||||||
|
tabFrame.getBottomTabContainer().setBorder(bottomBorder); |
||||||
|
tabFrame.getRightTabContainer().setBorder(rightBorder); |
||||||
|
tabFrame.getLeftTabContainer().setBorder(leftBorder); |
||||||
|
} |
||||||
|
|
||||||
|
private void installComponents() { |
||||||
|
DefaultTransformModel rightTransformModel = new DefaultTransformModel(); |
||||||
|
rightTransformModel.setQuadrantRotation(1); |
||||||
|
rightTransformModel.setScaleToPreferredSize(true); |
||||||
|
rotatePaneRight = TransformUtils.createTransformJXLayer(tabFrame.getRightTabContainer(), rightTransformModel); |
||||||
|
|
||||||
|
DefaultTransformModel leftTransformModel = new DefaultTransformModel(); |
||||||
|
leftTransformModel.setQuadrantRotation(3); |
||||||
|
leftTransformModel.setScaleToPreferredSize(true); |
||||||
|
rotatePaneLeft = TransformUtils.createTransformJXLayer(tabFrame.getLeftTabContainer(), leftTransformModel); |
||||||
|
|
||||||
|
tabFrame.add(tabFrame.getTopTabContainer()); |
||||||
|
tabFrame.add(tabFrame.getBottomTabContainer()); |
||||||
|
tabFrame.add(rotatePaneLeft); |
||||||
|
tabFrame.add(rotatePaneRight); |
||||||
|
} |
||||||
|
|
||||||
|
protected void installListeners() { |
||||||
|
Toolkit.getDefaultToolkit().addAWTEventListener(this, AWTEvent.MOUSE_EVENT_MASK); |
||||||
|
} |
||||||
|
|
||||||
|
protected LayoutManager createLayout() { |
||||||
|
return new TabFrameLayout(tabFrame, this); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void uninstallUI(final JComponent c) { |
||||||
|
tabFrame.remove(tabFrame.getTopTabContainer()); |
||||||
|
tabFrame.remove(tabFrame.getBottomTabContainer()); |
||||||
|
tabFrame.remove(rotatePaneLeft); |
||||||
|
tabFrame.remove(rotatePaneRight); |
||||||
|
layout = null; |
||||||
|
lineColor = null; |
||||||
|
topBorder = null; |
||||||
|
bottomBorder = null; |
||||||
|
leftBorder = null; |
||||||
|
rightBorder = null; |
||||||
|
rotatePaneLeft = null; |
||||||
|
rotatePaneRight = null; |
||||||
|
uninstallListeners(); |
||||||
|
} |
||||||
|
|
||||||
|
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 TabFrame tabFrame) { |
||||||
|
return UIManager.getInt("TabFrame.tabHeight"); |
||||||
|
} |
||||||
|
|
||||||
|
public JComponent getLeftContainer() { |
||||||
|
return rotatePaneLeft; |
||||||
|
} |
||||||
|
|
||||||
|
public JComponent getRightContainer() { |
||||||
|
return rotatePaneRight; |
||||||
|
} |
||||||
|
|
||||||
|
public JComponent getTopContainer() { |
||||||
|
return tabFrame.getTopTabContainer(); |
||||||
|
} |
||||||
|
|
||||||
|
public JComponent getBottomContainer() { |
||||||
|
return tabFrame.getBottomTabContainer(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void eventDispatched(@NotNull final AWTEvent event) { |
||||||
|
if (event.getID() == MouseEvent.MOUSE_PRESSED) { |
||||||
|
var e = (MouseEvent) event; |
||||||
|
var comp = e.getComponent().getComponentAt(e.getPoint()); |
||||||
|
var parent = DarkUIUtil.getParentOfType(TabFramePopup.class, comp); |
||||||
|
if (parent != null) { |
||||||
|
parent.getComponent().requestFocus(); |
||||||
|
return; |
||||||
|
} |
||||||
|
var parent2 = DarkUIUtil.getParentOfType(PopupContainer.class, comp); |
||||||
|
if (parent2 != null) { |
||||||
|
parent2.getPopup().requestFocus(); |
||||||
|
return; |
||||||
|
} |
||||||
|
var parent3 = DarkUIUtil.getParentOfType(TabFrame.class, comp); |
||||||
|
if (parent3 != null && comp != null && !comp.hasFocus()) { |
||||||
|
parent3.requestFocus(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,190 @@ |
|||||||
|
/* |
||||||
|
* 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.JPanelUIResource; |
||||||
|
import com.weis.darklaf.components.border.MutableLineBorder; |
||||||
|
import com.weis.darklaf.components.tabframe.TabbedPopup; |
||||||
|
import com.weis.darklaf.ui.tabbedpane.DarkTabbedPaneUI; |
||||||
|
import com.weis.darklaf.ui.tabbedpane.NewTabButton; |
||||||
|
import org.jetbrains.annotations.Contract; |
||||||
|
import org.jetbrains.annotations.NotNull; |
||||||
|
|
||||||
|
import javax.swing.*; |
||||||
|
import javax.swing.border.EmptyBorder; |
||||||
|
import javax.swing.plaf.ComponentUI; |
||||||
|
import java.awt.*; |
||||||
|
|
||||||
|
public class DarkTabbedPopupUI extends DarkPanelPopupUI { |
||||||
|
|
||||||
|
|
||||||
|
protected Color headerHoverBackground; |
||||||
|
protected Color headerSelectedBackground; |
||||||
|
protected Color headerSelectedHoverBackground; |
||||||
|
protected Color headerFocusHoverBackground; |
||||||
|
protected Color headerFocusSelectedBackground; |
||||||
|
protected Color headerFocusSelectedHoverBackground; |
||||||
|
private TabbedPopup popupComponent; |
||||||
|
private JTabbedPane tabbedPane; |
||||||
|
private MutableLineBorder border; |
||||||
|
private JPanel holder; |
||||||
|
|
||||||
|
@NotNull |
||||||
|
@Contract("_ -> new") |
||||||
|
public static ComponentUI createUI(final JComponent c) { |
||||||
|
return new DarkTabbedPopupUI(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void installUI(final JComponent c) { |
||||||
|
popupComponent = (TabbedPopup) c; |
||||||
|
super.installUI(c); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void installDefaults() { |
||||||
|
super.installDefaults(); |
||||||
|
headerHoverBackground = UIManager.getColor("TabFramePopup.headerHoverBackground"); |
||||||
|
headerSelectedBackground = UIManager.getColor("TabFramePopup.headerSelectedBackground"); |
||||||
|
headerSelectedHoverBackground = UIManager.getColor("TabFramePopup.headerSelectedHoverBackground"); |
||||||
|
headerFocusHoverBackground = UIManager.getColor("TabFramePopup.headerFocusHoverBackground"); |
||||||
|
headerFocusSelectedBackground = UIManager.getColor("TabFramePopup.headerFocusSelectedBackground"); |
||||||
|
headerFocusSelectedHoverBackground = UIManager.getColor("TabFramePopup.headerFocusSelectedHoverBackground"); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void installComponents() { |
||||||
|
closeButton = createCloseButton(); |
||||||
|
label = createLabel(); |
||||||
|
tabbedPane = createTabbedPane(); |
||||||
|
setupTabbedPane(); |
||||||
|
border = createBorder(); |
||||||
|
holder = new JPanel(new BorderLayout()); |
||||||
|
holder.add(tabbedPane, BorderLayout.CENTER); |
||||||
|
holder.setBorder(border); |
||||||
|
popupComponent.add(holder); |
||||||
|
} |
||||||
|
|
||||||
|
protected JTabbedPane createTabbedPane() { |
||||||
|
var tabbedPane = popupComponent.getTabbedPane(); |
||||||
|
tabbedPane.setUI(new DarkTabFrameTabbedPaneUI()); |
||||||
|
tabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT); |
||||||
|
return tabbedPane; |
||||||
|
} |
||||||
|
|
||||||
|
protected void setupTabbedPane() { |
||||||
|
label.setBorder(new EmptyBorder(0, 5, 0, 5)); |
||||||
|
var buttonHolder = new JPanelUIResource(); |
||||||
|
buttonHolder.setLayout(new BoxLayout(buttonHolder, BoxLayout.X_AXIS)); |
||||||
|
buttonHolder.add(Box.createHorizontalStrut(1)); |
||||||
|
buttonHolder.add(closeButton); |
||||||
|
buttonHolder.add(Box.createHorizontalStrut(1)); |
||||||
|
buttonHolder.setOpaque(false); |
||||||
|
tabbedPane.setOpaque(false); |
||||||
|
tabbedPane.putClientProperty("JTabbedPane.leadingComponent", label); |
||||||
|
tabbedPane.putClientProperty("JTabbedPane.trailingComponent", buttonHolder); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void uninstallComponents() { |
||||||
|
super.uninstallComponents(); |
||||||
|
popupComponent.remove(tabbedPane); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void applyBorderInsets(@NotNull final Insets insets) { |
||||||
|
border.setInsets(insets.top, insets.left, insets.bottom, insets.right); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void setHeaderBackground(final boolean focus) { |
||||||
|
var oldFocus = hasFocus(); |
||||||
|
super.setHeaderBackground(focus); |
||||||
|
if (oldFocus != focus) { |
||||||
|
holder.setBackground(focus ? headerFocusBackground : headerBackground); |
||||||
|
holder.repaint(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected class DarkTabFrameTabbedPaneUI extends DarkTabbedPaneUI { |
||||||
|
|
||||||
|
protected Color getTabBackgroundColor(final int tabIndex, final boolean isSelected, final boolean hover) { |
||||||
|
if (hasFocus()) { |
||||||
|
if (isSelected) { |
||||||
|
return hover ? headerFocusSelectedHoverBackground |
||||||
|
: headerFocusSelectedBackground; |
||||||
|
} else { |
||||||
|
return hover ? headerFocusHoverBackground |
||||||
|
: headerFocusBackground; |
||||||
|
} |
||||||
|
} else { |
||||||
|
if (isSelected) { |
||||||
|
return hover ? headerSelectedHoverBackground |
||||||
|
: headerSelectedBackground; |
||||||
|
} else { |
||||||
|
return hover ? headerHoverBackground |
||||||
|
: headerBackground; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected Color getTabBorderColor() { |
||||||
|
return UIManager.getColor("TabFramePopup.borderColor"); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected Color getAccentColor(final boolean focus) { |
||||||
|
return super.getAccentColor(focus || hasFocus()); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public JComponent createNewTabButton() { |
||||||
|
return new TabFrameNewTabButton(this); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public JButton createMoreTabsButton() { |
||||||
|
return super.createMoreTabsButton(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected class TabFrameNewTabButton extends NewTabButton { |
||||||
|
|
||||||
|
protected TabFrameNewTabButton(@NotNull final DarkTabbedPaneUI ui) { |
||||||
|
super(ui); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected JButton createButton() { |
||||||
|
var button = new HeaderButton(ui.getNewTabIcon(), DarkTabbedPopupUI.this); |
||||||
|
button.putClientProperty("JButton.variant", "shadow"); |
||||||
|
button.putClientProperty("JButton.buttonType", "square"); |
||||||
|
button.putClientProperty("JButton.thin", Boolean.TRUE); |
||||||
|
button.setRolloverEnabled(true); |
||||||
|
button.setOpaque(false); |
||||||
|
return button; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,229 @@ |
|||||||
|
/* |
||||||
|
* 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.TabFrame; |
||||||
|
import org.jetbrains.annotations.Contract; |
||||||
|
import org.jetbrains.annotations.NotNull; |
||||||
|
|
||||||
|
import java.awt.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Jannis Weis |
||||||
|
* @since 2019 |
||||||
|
*/ |
||||||
|
public class TabFrameLayout implements LayoutManager { |
||||||
|
|
||||||
|
private final TabFrame tabFrame; |
||||||
|
private DarkTabFrameUI ui; |
||||||
|
private int maxTabHeight; |
||||||
|
private int maxTabWidth; |
||||||
|
|
||||||
|
@Contract(pure = true) |
||||||
|
public TabFrameLayout(@NotNull final TabFrame tabFrame, final DarkTabFrameUI ui) { |
||||||
|
this.tabFrame = tabFrame; |
||||||
|
this.ui = ui; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void addLayoutComponent(final String name, final Component comp) { |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void removeLayoutComponent(final Component comp) { |
||||||
|
} |
||||||
|
|
||||||
|
@NotNull |
||||||
|
@Override |
||||||
|
public Dimension preferredLayoutSize(final Container parent) { |
||||||
|
var b = tabFrame.getContentPane().getComponent().getPreferredSize(); |
||||||
|
return new Dimension(tabFrame.getLeftTabContainer().getWidth() |
||||||
|
+ tabFrame.getRightTabContainer().getWidth() + b.width, |
||||||
|
tabFrame.getTopTabContainer().getHeight() |
||||||
|
+ tabFrame.getBottomTabContainer().getHeight() + b.height); |
||||||
|
} |
||||||
|
|
||||||
|
@NotNull |
||||||
|
@Override |
||||||
|
public Dimension minimumLayoutSize(final Container parent) { |
||||||
|
var b = tabFrame.getContentPane().getComponent().getMinimumSize(); |
||||||
|
return new Dimension(tabFrame.getLeftTabContainer().getWidth() |
||||||
|
+ tabFrame.getRightTabContainer().getWidth() + b.width, |
||||||
|
tabFrame.getTopTabContainer().getHeight() |
||||||
|
+ tabFrame.getBottomTabContainer().getHeight() + b.height); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void layoutContainer(@NotNull final Container parent) { |
||||||
|
var dim = parent.getSize(); |
||||||
|
int topSize = tabFrame.getTopTabCount(); |
||||||
|
int bottomSize = tabFrame.getBottomTabCount(); |
||||||
|
int leftSize = tabFrame.getLeftTabCount(); |
||||||
|
int rightSize = tabFrame.getRightTabCount(); |
||||||
|
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(); |
||||||
|
var bottomPane = ui.getBottomContainer(); |
||||||
|
tabFrame.getContentPane().getComponent().setBounds(leftPane.getWidth(), topPane.getHeight(), |
||||||
|
dim.width - leftPane.getWidth() - rightPane.getWidth(), |
||||||
|
dim.height - topPane.getHeight() - bottomPane.getHeight()); |
||||||
|
} |
||||||
|
|
||||||
|
protected void layoutTopTab(final Dimension dim, final int topSize, final int leftSize, final int rightSize) { |
||||||
|
if (topSize > 0) { |
||||||
|
tabFrame.getTopTabContainer().setBounds(0, 0, dim.width, tabFrame.getTabSize()); |
||||||
|
layoutHorizontal(dim, Alignment.NORTH, Alignment.NORTH_EAST, 0, leftSize, rightSize); |
||||||
|
} else { |
||||||
|
tabFrame.getTopTabContainer().setBounds(0, 0, 0, 0); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected void layoutBottomTab(final Dimension dim, final int bottomSize, final int leftSize, final int rightSize) { |
||||||
|
if (bottomSize > 0) { |
||||||
|
int size = tabFrame.getTabSize(); |
||||||
|
tabFrame.getBottomTabContainer().setBounds(0, dim.height - size, dim.width, size); |
||||||
|
layoutHorizontal(dim, Alignment.SOUTH_WEST, Alignment.SOUTH, 1, leftSize, rightSize); |
||||||
|
} else { |
||||||
|
tabFrame.getBottomTabContainer().setBounds(0, 0, 0, 0); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
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); |
||||||
|
int leftEnd = layoutTabArea(start, left, true, tabHeight - 1); |
||||||
|
start.x = rightSize > 0 ? dim.width - tabHeight : dim.width; |
||||||
|
int rightStart = layoutTabArea(start, right, false, tabHeight - 1); |
||||||
|
if (rightStart < leftEnd) { |
||||||
|
shift(leftEnd - rightStart, right); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected void layoutLeftTab(final Dimension dim, final int leftSize) { |
||||||
|
var leftPane = ui.getLeftContainer(); |
||||||
|
var topPane = tabFrame.getTopTabContainer(); |
||||||
|
var bottomPane = tabFrame.getBottomTabContainer(); |
||||||
|
int tabWidth = calculateMaxTabSize(Alignment.WEST); |
||||||
|
if (leftSize > 0) { |
||||||
|
int height = dim.height - topPane.getHeight() - bottomPane.getHeight(); |
||||||
|
leftPane.setBounds(0, topPane.getHeight(), tabWidth, 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); |
||||||
|
} |
||||||
|
} else { |
||||||
|
tabFrame.getLeftTabContainer().setBounds(0, 0, 0, 0); |
||||||
|
leftPane.setBounds(0, 0, 0, 0); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
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) { |
||||||
|
int height = dim.height - topPane.getHeight() - bottomPane.getHeight(); |
||||||
|
rightPane.setBounds(dim.width - tabWidth, topPane.getHeight(), tabWidth, 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); |
||||||
|
} |
||||||
|
} else { |
||||||
|
tabFrame.getRightTabContainer().setBounds(0, 0, 0, 0); |
||||||
|
topPane.setBounds(0, 0, 0, 0); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected void shift(final int shift, final Alignment a) { |
||||||
|
for (var c : tabFrame.tabsForAlignment(a)) { |
||||||
|
var pos = c.getComponent().getLocation(); |
||||||
|
pos.x += shift; |
||||||
|
c.getComponent().setLocation(pos); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
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; |
||||||
|
var bounds = new Rectangle(0, 0, 0, 0); |
||||||
|
for (var c : tabFrame.tabsForAlignment(a)) { |
||||||
|
bounds.width = getTabWidth(c.getComponent()); |
||||||
|
bounds.height = size; |
||||||
|
if (forward) { |
||||||
|
bounds.x = x; |
||||||
|
bounds.y = y; |
||||||
|
x += bounds.width; |
||||||
|
} else { |
||||||
|
x -= bounds.width; |
||||||
|
bounds.x = x; |
||||||
|
bounds.y = y; |
||||||
|
} |
||||||
|
c.getComponent().setBounds(bounds); |
||||||
|
} |
||||||
|
return x; |
||||||
|
} |
||||||
|
|
||||||
|
protected int getTabWidth(@NotNull final Component c) { |
||||||
|
int maxWidth = tabFrame.getMaxTabWidth(); |
||||||
|
int width = c.getPreferredSize().width; |
||||||
|
if (maxWidth < 0) { |
||||||
|
return width; |
||||||
|
} else { |
||||||
|
return Math.min(maxWidth, width); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected int calculateMaxTabSize(final Alignment a) { |
||||||
|
int max = tabFrame.getTabSize(); |
||||||
|
for (var c : tabFrame.tabsForAlignment(a)) { |
||||||
|
max = Math.max(max, c.getComponent().getMaximumSize().height + 1); |
||||||
|
} |
||||||
|
for (var c : tabFrame.tabsForAlignment(tabFrame.getPeer(a))) { |
||||||
|
max = Math.max(max, c.getComponent().getMaximumSize().height + 1); |
||||||
|
} |
||||||
|
return max; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,489 @@ |
|||||||
|
/* |
||||||
|
Copyright (c) 2009, Piet Blok |
||||||
|
All rights reserved. |
||||||
|
<p> |
||||||
|
Redistribution and use in source and binary forms, with or without |
||||||
|
modification, are permitted provided that the following conditions |
||||||
|
are met: |
||||||
|
<p> |
||||||
|
* Redistributions of source code must retain the above copyright |
||||||
|
notice, this list of conditions and the following disclaimer. |
||||||
|
* Redistributions in binary form must reproduce the above |
||||||
|
copyright notice, this list of conditions and the following |
||||||
|
disclaimer in the documentation and/or other materials provided |
||||||
|
with the distribution. |
||||||
|
* Neither the name of the copyright holder nor the names of the |
||||||
|
contributors may be used to endorse or promote products derived |
||||||
|
from this software without specific prior written permission. |
||||||
|
<p> |
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
*/ |
||||||
|
package org.pbjar.jxlayer.plaf.ext; |
||||||
|
|
||||||
|
import org.jdesktop.jxlayer.JXLayer; |
||||||
|
import org.jdesktop.jxlayer.plaf.AbstractLayerUI; |
||||||
|
import org.jdesktop.jxlayer.plaf.LayerUI; |
||||||
|
import org.jetbrains.annotations.Contract; |
||||||
|
import org.jetbrains.annotations.NotNull; |
||||||
|
import org.jetbrains.annotations.Nullable; |
||||||
|
|
||||||
|
import javax.swing.*; |
||||||
|
import javax.swing.plaf.ComponentUI; |
||||||
|
import java.awt.*; |
||||||
|
import java.awt.event.MouseEvent; |
||||||
|
import java.awt.event.MouseWheelEvent; |
||||||
|
import java.awt.event.MouseWheelListener; |
||||||
|
import java.awt.geom.AffineTransform; |
||||||
|
import java.awt.geom.NoninvertibleTransformException; |
||||||
|
|
||||||
|
/** |
||||||
|
* This class provides for {@link MouseEvent} re-dispatching. It may be used to set a tool tip on |
||||||
|
* {@link JXLayer}'s glass pane and still have the child components receive {@link MouseEvent}s. |
||||||
|
* <p> |
||||||
|
* <b>Note:</b> A {@link MouseEventUI} instance cannot be shared and can be set |
||||||
|
* to a single {@link JXLayer} instance only. |
||||||
|
* <p/> |
||||||
|
*/ |
||||||
|
public class MouseEventUI<V extends JComponent> extends AbstractLayerUI<V> { |
||||||
|
|
||||||
|
static { |
||||||
|
/* |
||||||
|
* The instantiating of a JInternalFrame before a MouseEventUI is set to |
||||||
|
* a JXLayer containing a JDesktopPane that has no JInternalFrames set, |
||||||
|
* prevents the failing of re dispatched MouseEvents. |
||||||
|
* |
||||||
|
* This is a work around a problem that I don't really understand. |
||||||
|
* Please see |
||||||
|
* http://forums.java.net/jive/thread.jspa?threadID=66763&tstart=0 for a
|
||||||
|
* discussion on this problem. |
||||||
|
*/ |
||||||
|
new JInternalFrame(); |
||||||
|
} |
||||||
|
|
||||||
|
@Nullable |
||||||
|
private Component lastEnteredTarget, lastPressedTarget; |
||||||
|
private boolean dispatchingMode = false; |
||||||
|
@Nullable |
||||||
|
private JXLayer<? extends V> installedLayer; |
||||||
|
|
||||||
|
/** |
||||||
|
* Overridden to override the {@link LayerUI} implementation that only consults the view. |
||||||
|
* <p> |
||||||
|
* This implementation is a copy of the {@link ComponentUI#contains(JComponent, int, int)} |
||||||
|
* method. |
||||||
|
*/ |
||||||
|
@SuppressWarnings("deprecation") |
||||||
|
@Override |
||||||
|
public boolean contains(@NotNull final JComponent c, final int x, final int y) { |
||||||
|
return c.inside(x, y); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Overridden to check if this {@link LayerUI} has not been installed already, and to set the |
||||||
|
* argument {@code component} as the installed {@link JXLayer}. |
||||||
|
* |
||||||
|
* @throws IllegalStateException when this {@link LayerUI} has been installed already |
||||||
|
* @see #getInstalledLayer() |
||||||
|
*/ |
||||||
|
@SuppressWarnings("unchecked") |
||||||
|
@Override |
||||||
|
public void installUI(@NotNull final JComponent component) throws IllegalStateException { |
||||||
|
super.installUI(component); |
||||||
|
if (installedLayer != null) { |
||||||
|
throw new IllegalStateException(this.getClass().getName() |
||||||
|
+ " cannot be shared between multiple layers"); |
||||||
|
} |
||||||
|
installedLayer = (JXLayer<? extends V>) component; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Overridden to remove the installed {@link JXLayer}. |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public void uninstallUI(@NotNull final JComponent c) { |
||||||
|
installedLayer = null; |
||||||
|
super.uninstallUI(c); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Overridden to only get the following event types: {@link AWTEvent#MOUSE_EVENT_MASK}, {@link |
||||||
|
* AWTEvent#MOUSE_MOTION_EVENT_MASK} and {@link AWTEvent#MOUSE_WHEEL_EVENT_MASK}. |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public long getLayerEventMask() { |
||||||
|
return AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK |
||||||
|
| AWTEvent.MOUSE_WHEEL_EVENT_MASK; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Overridden to allow for re-dispatching of mouse events to their intended (visual) recipients, |
||||||
|
* rather than to the components according to their bounds. |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public void eventDispatched(final AWTEvent event, @NotNull final JXLayer<? extends V> layer) { |
||||||
|
if (event instanceof MouseEvent) { |
||||||
|
MouseEvent mouseEvent = (MouseEvent) event; |
||||||
|
if (!dispatchingMode) { |
||||||
|
// Process an original mouse event
|
||||||
|
dispatchingMode = true; |
||||||
|
try { |
||||||
|
redispatch(mouseEvent, layer); |
||||||
|
} finally { |
||||||
|
dispatchingMode = false; |
||||||
|
} |
||||||
|
} else { |
||||||
|
// Process a generated mouse event
|
||||||
|
/* |
||||||
|
* Added a check, because on mouse entered or exited, the cursor |
||||||
|
* may be set to specific dragging cursors. |
||||||
|
*/ |
||||||
|
if (MouseEvent.MOUSE_ENTERED == mouseEvent.getID() |
||||||
|
|| MouseEvent.MOUSE_EXITED == mouseEvent.getID()) { |
||||||
|
layer.getGlassPane().setCursor(null); |
||||||
|
} else { |
||||||
|
Component component = mouseEvent.getComponent(); |
||||||
|
layer.getGlassPane().setCursor(component.getCursor()); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Re-dispatches the event to the first component in the hierarchy that has a {@link |
||||||
|
* MouseWheelListener} registered. |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
protected void processMouseWheelEvent(@NotNull final MouseWheelEvent event, |
||||||
|
@NotNull final JXLayer<? extends V> jxlayer) { |
||||||
|
/* |
||||||
|
* Only process an event if it is not already consumed. This may be the |
||||||
|
* case if this LayerUI is contained in a wrapped hierarchy. |
||||||
|
*/ |
||||||
|
if (!event.isConsumed()) { |
||||||
|
/* |
||||||
|
* Since we will create a new event, the argument event must be |
||||||
|
* consumed. |
||||||
|
*/ |
||||||
|
event.consume(); |
||||||
|
/* |
||||||
|
* Find a target up in the hierarchy that has |
||||||
|
* MouseWheelEventListeners registered. |
||||||
|
*/ |
||||||
|
Component target = event.getComponent(); |
||||||
|
Component newTarget = findWheelListenerComponent(target); |
||||||
|
if (newTarget == null) { |
||||||
|
newTarget = jxlayer.getParent(); |
||||||
|
} |
||||||
|
/* |
||||||
|
* Convert the location relative to the new target |
||||||
|
*/ |
||||||
|
Point point = SwingUtilities.convertPoint(event.getComponent(), |
||||||
|
event.getPoint(), newTarget); |
||||||
|
/* |
||||||
|
* Create a new event and dispatch it. |
||||||
|
*/ |
||||||
|
newTarget.dispatchEvent(createMouseWheelEvent(event, point, |
||||||
|
newTarget)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@NotNull |
||||||
|
private Point calculateTargetPoint(@NotNull final JXLayer<? extends V> layer, |
||||||
|
@NotNull final MouseEvent mouseEvent) { |
||||||
|
Point point = mouseEvent.getPoint(); |
||||||
|
SwingUtilities.convertPointToScreen(point, mouseEvent.getComponent()); |
||||||
|
SwingUtilities.convertPointFromScreen(point, layer); |
||||||
|
/* |
||||||
|
* Removed the contains check because it results in jumping when |
||||||
|
* dragging internal frames in a desktop pane and dragging outside the |
||||||
|
* boundaries of the desktop. |
||||||
|
* |
||||||
|
* Introduced this check to solve some scrolling problem, but don't |
||||||
|
* quite remember the specifics. Maybe that problem is gone by other |
||||||
|
* changes. |
||||||
|
*/ |
||||||
|
// Rectangle layerBounds = layer.getBounds();
|
||||||
|
// Container parent = layer.getParent();
|
||||||
|
// Rectangle parentRectangle = new Rectangle(-layerBounds.x,
|
||||||
|
// -layerBounds.y, parent.getWidth(), parent.getHeight());
|
||||||
|
// if (parentRectangle.contains(point)) {
|
||||||
|
return transformPoint(layer, point); |
||||||
|
// } else {
|
||||||
|
// return new Point(-1, -1);
|
||||||
|
// }
|
||||||
|
} |
||||||
|
|
||||||
|
@NotNull |
||||||
|
private MouseWheelEvent createMouseWheelEvent( |
||||||
|
@NotNull final MouseWheelEvent mouseWheelEvent, @NotNull final Point point, @NotNull final Component target) { |
||||||
|
return new MouseWheelEvent(target, //
|
||||||
|
mouseWheelEvent.getID(), //
|
||||||
|
mouseWheelEvent.getWhen(), //
|
||||||
|
mouseWheelEvent.getModifiersEx(), //
|
||||||
|
point.x, //
|
||||||
|
point.y, //
|
||||||
|
mouseWheelEvent.getClickCount(), //
|
||||||
|
mouseWheelEvent.isPopupTrigger(), //
|
||||||
|
mouseWheelEvent.getScrollType(), //
|
||||||
|
mouseWheelEvent.getScrollAmount(), //
|
||||||
|
mouseWheelEvent.getWheelRotation() //
|
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
private void dispatchMouseEvent(@Nullable final MouseEvent mouseEvent) { |
||||||
|
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);
|
||||||
|
// }
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Contract("null -> null") |
||||||
|
@Nullable |
||||||
|
private Component findWheelListenerComponent(@Nullable final Component target) { |
||||||
|
if (target == null) { |
||||||
|
return null; |
||||||
|
} else if (target.getMouseWheelListeners().length == 0) { |
||||||
|
return findWheelListenerComponent(target.getParent()); |
||||||
|
} else { |
||||||
|
return target; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void generateEnterExitEvents(@NotNull final JXLayer<? extends V> layer, |
||||||
|
@NotNull final MouseEvent originalEvent, final Component newTarget, @NotNull final Point realPoint) { |
||||||
|
if (lastEnteredTarget != newTarget) { |
||||||
|
dispatchMouseEvent(transformMouseEvent(layer, originalEvent, |
||||||
|
lastEnteredTarget, realPoint, MouseEvent.MOUSE_EXITED)); |
||||||
|
lastEnteredTarget = newTarget; |
||||||
|
dispatchMouseEvent(transformMouseEvent(layer, originalEvent, |
||||||
|
lastEnteredTarget, realPoint, MouseEvent.MOUSE_ENTERED)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@SuppressWarnings("DuplicatedCode") |
||||||
|
@Nullable |
||||||
|
private Component getListeningComponent(@NotNull final MouseEvent event, @NotNull final Component component) { |
||||||
|
Component comp; |
||||||
|
switch (event.getID()) { |
||||||
|
case MouseEvent.MOUSE_CLICKED: |
||||||
|
case MouseEvent.MOUSE_ENTERED: |
||||||
|
case MouseEvent.MOUSE_EXITED: |
||||||
|
case MouseEvent.MOUSE_PRESSED: |
||||||
|
case MouseEvent.MOUSE_RELEASED: |
||||||
|
comp = getMouseListeningComponent(component); |
||||||
|
break; |
||||||
|
case MouseEvent.MOUSE_DRAGGED: |
||||||
|
case MouseEvent.MOUSE_MOVED: |
||||||
|
comp = getMouseMotionListeningComponent(component); |
||||||
|
break; |
||||||
|
case MouseEvent.MOUSE_WHEEL: |
||||||
|
comp = getMouseWheelListeningComponent(component); |
||||||
|
break; |
||||||
|
default: |
||||||
|
comp = null; |
||||||
|
} |
||||||
|
return comp; |
||||||
|
} |
||||||
|
|
||||||
|
@Nullable |
||||||
|
private Component getMouseListeningComponent(@NotNull final Component component) { |
||||||
|
if (component.getMouseListeners().length > 0) { |
||||||
|
return component; |
||||||
|
} else { |
||||||
|
Container parent = component.getParent(); |
||||||
|
if (parent != null) { |
||||||
|
return getMouseListeningComponent(parent); |
||||||
|
} else { |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@SuppressWarnings("Duplicates") |
||||||
|
@Nullable |
||||||
|
private Component getMouseMotionListeningComponent(@NotNull final Component component) { |
||||||
|
/* |
||||||
|
* Mouse motion events may result in MOUSE_ENTERED and MOUSE_EXITED. |
||||||
|
* |
||||||
|
* Therefore, components with MouseListeners registered should be |
||||||
|
* returned as well. |
||||||
|
*/ |
||||||
|
if (component.getMouseMotionListeners().length > 0 |
||||||
|
|| component.getMouseListeners().length > 0) { |
||||||
|
return component; |
||||||
|
} else { |
||||||
|
Container parent = component.getParent(); |
||||||
|
if (parent != null) { |
||||||
|
return getMouseMotionListeningComponent(parent); |
||||||
|
} else { |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Nullable |
||||||
|
private Component getMouseWheelListeningComponent(@NotNull final Component component) { |
||||||
|
if (component.getMouseWheelListeners().length > 0) { |
||||||
|
return component; |
||||||
|
} else { |
||||||
|
Container parent = component.getParent(); |
||||||
|
if (parent != null) { |
||||||
|
return getMouseWheelListeningComponent(parent); |
||||||
|
} else { |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Nullable |
||||||
|
private Component getTarget(@NotNull final JXLayer<? extends V> layer, @NotNull final Point targetPoint) { |
||||||
|
Component view = layer.getView(); |
||||||
|
if (view == null) { |
||||||
|
return null; |
||||||
|
} else { |
||||||
|
Point viewPoint = SwingUtilities.convertPoint(layer, targetPoint, |
||||||
|
view); |
||||||
|
return SwingUtilities.getDeepestComponentAt(view, viewPoint.x, |
||||||
|
viewPoint.y); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@SuppressWarnings("Duplicates") |
||||||
|
private void redispatch(@NotNull final MouseEvent originalEvent, |
||||||
|
@NotNull final JXLayer<? extends V> layer) { |
||||||
|
if (layer.getView() != null) { |
||||||
|
if (originalEvent.getComponent() != layer.getGlassPane()) { |
||||||
|
originalEvent.consume(); |
||||||
|
} |
||||||
|
MouseEvent newEvent = null; |
||||||
|
|
||||||
|
Point realPoint = calculateTargetPoint(layer, originalEvent); |
||||||
|
Component realTarget = getTarget(layer, realPoint); |
||||||
|
if (realTarget != null) { |
||||||
|
realTarget = getListeningComponent(originalEvent, realTarget); |
||||||
|
} |
||||||
|
|
||||||
|
switch (originalEvent.getID()) { |
||||||
|
case MouseEvent.MOUSE_PRESSED: |
||||||
|
newEvent = transformMouseEvent(layer, originalEvent, realTarget, realPoint); |
||||||
|
if (newEvent != null) { |
||||||
|
lastPressedTarget = newEvent.getComponent(); |
||||||
|
} |
||||||
|
break; |
||||||
|
case MouseEvent.MOUSE_RELEASED: |
||||||
|
newEvent = |
||||||
|
transformMouseEvent(layer, originalEvent, lastPressedTarget, realPoint); |
||||||
|
lastPressedTarget = null; |
||||||
|
break; |
||||||
|
case MouseEvent.MOUSE_ENTERED: |
||||||
|
case MouseEvent.MOUSE_EXITED: |
||||||
|
generateEnterExitEvents(layer, originalEvent, realTarget, realPoint); |
||||||
|
break; |
||||||
|
case MouseEvent.MOUSE_MOVED: |
||||||
|
newEvent = transformMouseEvent(layer, originalEvent, realTarget, realPoint); |
||||||
|
generateEnterExitEvents(layer, originalEvent, realTarget, realPoint); |
||||||
|
break; |
||||||
|
case MouseEvent.MOUSE_DRAGGED: |
||||||
|
newEvent = |
||||||
|
transformMouseEvent(layer, originalEvent, lastPressedTarget, realPoint); |
||||||
|
generateEnterExitEvents(layer, originalEvent, realTarget, realPoint); |
||||||
|
break; |
||||||
|
case MouseEvent.MOUSE_CLICKED: |
||||||
|
newEvent = transformMouseEvent(layer, originalEvent, realTarget, realPoint); |
||||||
|
break; |
||||||
|
case (MouseEvent.MOUSE_WHEEL): |
||||||
|
redispatchMouseWheelEvent((MouseWheelEvent) originalEvent, realTarget, layer); |
||||||
|
break; |
||||||
|
} |
||||||
|
dispatchMouseEvent(newEvent); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void redispatchMouseWheelEvent(@NotNull final MouseWheelEvent mouseWheelEvent, |
||||||
|
final Component target, @NotNull final JXLayer<? extends V> layer) { |
||||||
|
MouseWheelEvent newEvent = this.transformMouseWheelEvent( |
||||||
|
mouseWheelEvent, target, layer); |
||||||
|
processMouseWheelEvent(newEvent, layer); |
||||||
|
} |
||||||
|
|
||||||
|
@Nullable |
||||||
|
private MouseEvent transformMouseEvent(@NotNull final JXLayer<? extends V> layer, |
||||||
|
@NotNull final MouseEvent mouseEvent, final Component target, @NotNull final Point realPoint) { |
||||||
|
return transformMouseEvent(layer, mouseEvent, target, realPoint, |
||||||
|
mouseEvent.getID()); |
||||||
|
} |
||||||
|
|
||||||
|
@Nullable |
||||||
|
private MouseEvent transformMouseEvent(@NotNull final JXLayer<? extends V> layer, |
||||||
|
@NotNull final MouseEvent mouseEvent, @Nullable final Component target, @NotNull final Point targetPoint, final int id) { |
||||||
|
if (target == null) { |
||||||
|
return null; |
||||||
|
} else { |
||||||
|
Point newPoint = new Point(targetPoint); |
||||||
|
SwingUtilities.convertPointToScreen(newPoint, layer); |
||||||
|
SwingUtilities.convertPointFromScreen(newPoint, target); |
||||||
|
return new MouseEvent(target, //
|
||||||
|
id, //
|
||||||
|
mouseEvent.getWhen(), //
|
||||||
|
mouseEvent.getModifiersEx(), //
|
||||||
|
newPoint.x, //
|
||||||
|
newPoint.y, //
|
||||||
|
mouseEvent.getClickCount(), //
|
||||||
|
mouseEvent.isPopupTrigger(), //
|
||||||
|
mouseEvent.getButton()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@NotNull |
||||||
|
private MouseWheelEvent transformMouseWheelEvent( |
||||||
|
@NotNull final MouseWheelEvent mouseWheelEvent, final Component t, |
||||||
|
final JXLayer<? extends V> layer) { |
||||||
|
var target = t; |
||||||
|
if (target == null) { |
||||||
|
target = layer; |
||||||
|
} |
||||||
|
Point point = SwingUtilities.convertPoint(mouseWheelEvent.getComponent(), |
||||||
|
mouseWheelEvent.getPoint(), target); |
||||||
|
return createMouseWheelEvent(mouseWheelEvent, |
||||||
|
point, target); |
||||||
|
} |
||||||
|
|
||||||
|
@NotNull |
||||||
|
private Point transformPoint(final JXLayer<? extends V> layer, @NotNull final Point point) { |
||||||
|
AffineTransform transform = this.getTransform(layer); |
||||||
|
if (transform != null) { |
||||||
|
try { |
||||||
|
transform.inverseTransform(point, point); |
||||||
|
} catch (NoninvertibleTransformException e) { |
||||||
|
e.printStackTrace(); |
||||||
|
} |
||||||
|
} |
||||||
|
return point; |
||||||
|
} |
||||||
|
|
||||||
|
@Nullable |
||||||
|
protected JXLayer<? extends V> getInstalledLayer() { |
||||||
|
return installedLayer; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,644 @@ |
|||||||
|
package org.pbjar.jxlayer.plaf.ext; |
||||||
|
/* |
||||||
|
* Copyright (c) 2009, Piet Blok |
||||||
|
* All rights reserved. |
||||||
|
* <p> |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions |
||||||
|
* are met: |
||||||
|
* <p> |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* * Redistributions in binary form must reproduce the above |
||||||
|
* copyright notice, this list of conditions and the following |
||||||
|
* disclaimer in the documentation and/or other materials provided |
||||||
|
* with the distribution. |
||||||
|
* * Neither the name of the copyright holder nor the names of the |
||||||
|
* contributors may be used to endorse or promote products derived |
||||||
|
* from this software without specific prior written permission. |
||||||
|
* <p> |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
*/ |
||||||
|
|
||||||
|
import com.sun.java.swing.SwingUtilities3; |
||||||
|
import com.weis.darklaf.LogFormatter; |
||||||
|
import org.jdesktop.jxlayer.JXLayer; |
||||||
|
import org.jdesktop.jxlayer.plaf.AbstractBufferedLayerUI; |
||||||
|
import org.jdesktop.jxlayer.plaf.LayerUI; |
||||||
|
import org.jdesktop.swingx.ForwardingRepaintManager; |
||||||
|
import org.jetbrains.annotations.Contract; |
||||||
|
import org.jetbrains.annotations.NotNull; |
||||||
|
import org.jetbrains.annotations.Nullable; |
||||||
|
import org.pbjar.jxlayer.plaf.ext.transform.DefaultTransformModel; |
||||||
|
import org.pbjar.jxlayer.plaf.ext.transform.TransformLayout; |
||||||
|
import org.pbjar.jxlayer.plaf.ext.transform.TransformModel; |
||||||
|
import org.pbjar.jxlayer.plaf.ext.transform.TransformRPMAnnotation; |
||||||
|
import org.pbjar.jxlayer.plaf.ext.transform.TransformRPMFallBack; |
||||||
|
import org.pbjar.jxlayer.plaf.ext.transform.TransformRPMSwingX; |
||||||
|
import org.pbjar.jxlayer.repaint.RepaintManagerProvider; |
||||||
|
import org.pbjar.jxlayer.repaint.RepaintManagerUtils; |
||||||
|
import org.pbjar.jxlayer.repaint.WrappedRepaintManager; |
||||||
|
|
||||||
|
import javax.swing.*; |
||||||
|
import javax.swing.border.Border; |
||||||
|
import javax.swing.event.ChangeListener; |
||||||
|
import javax.swing.text.GlyphView.GlyphPainter; |
||||||
|
import javax.swing.text.JTextComponent; |
||||||
|
import java.awt.*; |
||||||
|
import java.awt.geom.AffineTransform; |
||||||
|
import java.awt.geom.Area; |
||||||
|
import java.awt.image.BufferedImage; |
||||||
|
import java.beans.PropertyChangeListener; |
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.HashSet; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.Objects; |
||||||
|
import java.util.Set; |
||||||
|
import java.util.logging.ConsoleHandler; |
||||||
|
import java.util.logging.Logger; |
||||||
|
|
||||||
|
/** |
||||||
|
* This class provides for all necessary functionality when using transformations in a {@link |
||||||
|
* LayerUI}. |
||||||
|
* |
||||||
|
* <p>Some implementation details: |
||||||
|
* |
||||||
|
* <p> |
||||||
|
* |
||||||
|
* <ul> |
||||||
|
* <li>It extends {@link MouseEventUI} because, when applying transformations, the whereabouts of |
||||||
|
* child components on screen (device space) do not necessarily match the location according |
||||||
|
* to their bounds as set by layout managers (component space). So, mouse events must always |
||||||
|
* be redirected to the intended recipients. |
||||||
|
* <li>When enabled, this implementation sets a different {@link LayoutManager} to be used by |
||||||
|
* {@link JXLayer}. Instead of setting the size of the view to {@link JXLayer}'s inner area, |
||||||
|
* it sets the size of the view to the view's <em>preferred</em> size and centers it in the |
||||||
|
* inner area. Also, when calculating the preferred size of {@link JXLayer}, it transforms the |
||||||
|
* normally calculated size with the {@link AffineTransform} returned from {@link |
||||||
|
* #getPreferredTransform(Dimension, JXLayer)}. |
||||||
|
* <li>This implementation allocates a fresh {@link BufferedImage} the size of the clip area, each |
||||||
|
* time that the {@link #paint(Graphics, JComponent)} method is invoked. This is different |
||||||
|
* from the implementation of {@link AbstractBufferedLayerUI}, that maintains a cached image, |
||||||
|
* the size of the view. An important reason to not follow the {@link AbstractBufferedLayerUI} |
||||||
|
* strategy is that, when applying scaling transformations with a large scaling factor, a |
||||||
|
* {@link OutOfMemoryError} may be thrown because it will try to allocate a buffer of an |
||||||
|
* extreme size, even if not all of its contents will actually be visible on the screen. |
||||||
|
* <li>Rather than configuring the screen graphics object, the image's graphics object is |
||||||
|
* configured through {@link #configureGraphics(Graphics2D, JXLayer)}. |
||||||
|
* <li>Regardless of whether or not the view is opaque, a background color is painted. It is |
||||||
|
* obtained from the first component upwards in the hierarchy starting with the view, that is |
||||||
|
* opaque. If an opaque component is not found, the background color of the layer is used. |
||||||
|
* Painting the background is necessary to prevent visual artifacts when the transformation is |
||||||
|
* changed dynamically. |
||||||
|
* <li>Rendering hints may be set with {@link #setRenderingHints(Map)}, {@link |
||||||
|
* #addRenderingHint(RenderingHints.Key, Object)} and {@link #addRenderingHints(Map)}. |
||||||
|
* </ul> |
||||||
|
* |
||||||
|
* <p>Known limitations: |
||||||
|
* |
||||||
|
* <p> |
||||||
|
* |
||||||
|
* <ol> |
||||||
|
* <li>In Java versions <b>before Java 6u10</b>, this implementation employs a custom {@link |
||||||
|
* RepaintManager} in order to have descendant's repaint requests propagated up to the {@link |
||||||
|
* JXLayer} ancestor. This {@link RepaintManager} will work well with and without other {@link |
||||||
|
* RepaintManager} that are either subclasses of the {@link WrappedRepaintManager} or SwingX's |
||||||
|
* {@link ForwardingRepaintManager}. Other {@link RepaintManager}s may cause conflicts. |
||||||
|
* <p>In Java versions <b>6u10 or higher</b>, an attempt will be made to use the new |
||||||
|
* RepaintManager delegate facility that has been designed for JavaFX. |
||||||
|
* <li>Transformations will be applied on the whole of the content of the {@link JXLayer}. The |
||||||
|
* result is that {@link Border}s and other content within {@link JXLayer}'s insets will |
||||||
|
* generally either be invisible, or will be rendered in a very undesirable way. If you want a |
||||||
|
* {@link Border} to be transformed together with {@link JXLayer}'s view, that border should |
||||||
|
* be set on the view instead. On the other hand, if you want the {@link Border} not to be |
||||||
|
* transformed, that border must be set on {@link JXLayer}'s parent. |
||||||
|
* </ol> |
||||||
|
* |
||||||
|
* <b>Note:</b> A {@link TransformUI} instance cannot be shared and can be set to a single {@link |
||||||
|
* JXLayer} instance only. |
||||||
|
* |
||||||
|
* @author Piet Blok |
||||||
|
*/ |
||||||
|
public class TransformUI extends MouseEventUI<JComponent> { |
||||||
|
|
||||||
|
|
||||||
|
private static final LayoutManager transformLayout = new TransformLayout(); |
||||||
|
private static final String KEY_VIEW = "view"; |
||||||
|
private static final boolean delegatePossible; |
||||||
|
private static final RepaintManager wrappedManager = new TransformRepaintManager(); |
||||||
|
private static final Logger LOGGER = Logger.getLogger(TransformUI.class.getName()); |
||||||
|
|
||||||
|
static { |
||||||
|
LOGGER.setUseParentHandlers(false); |
||||||
|
ConsoleHandler handler = new ConsoleHandler(); |
||||||
|
handler.setFormatter(new LogFormatter()); |
||||||
|
LOGGER.addHandler(handler); |
||||||
|
|
||||||
|
boolean value; |
||||||
|
try { |
||||||
|
SwingUtilities3.class.getMethod("setDelegateRepaintManager", JComponent.class, RepaintManager.class); |
||||||
|
value = true; |
||||||
|
} catch (Throwable t) { |
||||||
|
value = false; |
||||||
|
} |
||||||
|
delegatePossible = value; |
||||||
|
LOGGER.info("Java " + System.getProperty("java.version") + " " + System.getProperty("java.vm.version") |
||||||
|
+ (delegatePossible ? ": RepaintManager delegate facility for JavaFX will be used." |
||||||
|
: ": RepaintManager.setCurrentManager() will be used.")); |
||||||
|
} |
||||||
|
|
||||||
|
private final ChangeListener changeListener = e -> revalidateLayer(); |
||||||
|
private final RepaintManagerProvider rpmProvider = |
||||||
|
new RepaintManagerProvider() { |
||||||
|
|
||||||
|
@NotNull |
||||||
|
@Override |
||||||
|
public Class<? extends ForwardingRepaintManager> getForwardingRepaintManagerClass() { |
||||||
|
return TransformRPMSwingX.class; |
||||||
|
} |
||||||
|
|
||||||
|
@NotNull |
||||||
|
@Override |
||||||
|
public Class<? extends WrappedRepaintManager> getWrappedRepaintManagerClass() { |
||||||
|
return TransformRPMFallBack.class; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean isAdequate(@NotNull final Class<? extends RepaintManager> manager) { |
||||||
|
return manager.isAnnotationPresent(TransformRPMAnnotation.class); |
||||||
|
} |
||||||
|
}; |
||||||
|
private final Map<RenderingHints.Key, Object> renderingHints = new HashMap<>(); |
||||||
|
private final Set<JComponent> originalDoubleBuffered = new HashSet<>(); |
||||||
|
private JComponent view; |
||||||
|
private final PropertyChangeListener viewChangeListener = |
||||||
|
evt -> setView((JComponent) evt.getNewValue()); |
||||||
|
@Nullable |
||||||
|
private TransformModel transformModel; |
||||||
|
private LayoutManager originalLayout; |
||||||
|
|
||||||
|
/** |
||||||
|
* Construct a {@link TransformUI} with a {@link DefaultTransformModel}. |
||||||
|
*/ |
||||||
|
public TransformUI() { |
||||||
|
this(new DefaultTransformModel()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Construct a {@link TransformUI} with a specified model. |
||||||
|
* |
||||||
|
* @param model the model |
||||||
|
*/ |
||||||
|
public TransformUI(final TransformModel model) { |
||||||
|
super(); |
||||||
|
this.setModel(model); |
||||||
|
} |
||||||
|
|
||||||
|
private void revalidateLayer() { |
||||||
|
JXLayer<? extends JComponent> installedLayer = this.getInstalledLayer(); |
||||||
|
if (installedLayer != null) { |
||||||
|
installedLayer.revalidate(); |
||||||
|
installedLayer.repaint(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* {@link JTextComponent} and its descendants have some caret position problems when used inside a |
||||||
|
* transformed {@link JXLayer}. When you plan to use {@link JTextComponent}(s) inside the |
||||||
|
* hierarchy of a transformed {@link JXLayer}, call this method in an early stage, before |
||||||
|
* instantiating any {@link JTextComponent}. |
||||||
|
* |
||||||
|
* <p>It executes the following method: |
||||||
|
* |
||||||
|
* <pre> |
||||||
|
* System.setProperty("i18n", Boolean.TRUE.toString()); |
||||||
|
* </pre> |
||||||
|
* |
||||||
|
* <p>As a result, a {@link GlyphPainter} will be selected that uses floating point instead of |
||||||
|
* fixed point calculations. |
||||||
|
*/ |
||||||
|
public static void prepareForJTextComponent() { |
||||||
|
System.setProperty("i18n", Boolean.TRUE.toString()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Add one rendering hint to the currently active rendering hints. |
||||||
|
* |
||||||
|
* @param key the key |
||||||
|
* @param value the value |
||||||
|
*/ |
||||||
|
public void addRenderingHint(final RenderingHints.Key key, final Object value) { |
||||||
|
this.renderingHints.put(key, value); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Add new rendering hints to the currently active rendering hints. |
||||||
|
* |
||||||
|
* @param hints the new rendering hints |
||||||
|
*/ |
||||||
|
public void addRenderingHints(@NotNull final Map<RenderingHints.Key, Object> hints) { |
||||||
|
this.renderingHints.putAll(hints); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the {@link TransformModel}. |
||||||
|
* |
||||||
|
* @return the {@link TransformModel} |
||||||
|
* @see #setModel(TransformModel) |
||||||
|
*/ |
||||||
|
@Contract(pure = true) |
||||||
|
@Nullable |
||||||
|
public final TransformModel getModel() { |
||||||
|
return transformModel; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set a new {@link TransformModel}. The new model may not be {@code null}. |
||||||
|
* |
||||||
|
* @param transformModel the new model |
||||||
|
* @throws NullPointerException if transformModel is {@code null} |
||||||
|
* @see #getModel() |
||||||
|
*/ |
||||||
|
@Contract("null -> fail") |
||||||
|
public final void setModel(@Nullable final TransformModel transformModel) throws NullPointerException { |
||||||
|
if (transformModel == null) { |
||||||
|
throw new NullPointerException("The TransformModel may not be null"); |
||||||
|
} |
||||||
|
if (this.transformModel != null) { |
||||||
|
this.transformModel.removeChangeListener(this.changeListener); |
||||||
|
} |
||||||
|
this.transformModel = transformModel; |
||||||
|
this.transformModel.addChangeListener(this.changeListener); |
||||||
|
revalidateLayer(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get a preferred {@link AffineTransform}. This method will typically be invoked by programs that |
||||||
|
* calculate a preferred size. |
||||||
|
* |
||||||
|
* <p>The {@code size} argument will be used to compute anchor values for some types of |
||||||
|
* transformations. If the {@code size} argument is {@code null} a value of (0,0) is used for the |
||||||
|
* anchor. |
||||||
|
* |
||||||
|
* <p>In {@code enabled} state this method is delegated to the {@link TransformModel} that has |
||||||
|
* been set. Otherwise {@code null} will be returned. |
||||||
|
* |
||||||
|
* @param size a {@link Dimension} instance to be used for an anchor or {@code null} |
||||||
|
* @param layer the {@link JXLayer}. |
||||||
|
* @return a {@link AffineTransform} instance or {@code null} |
||||||
|
*/ |
||||||
|
@NotNull |
||||||
|
public AffineTransform getPreferredTransform( |
||||||
|
final Dimension size, final JXLayer<? extends JComponent> layer) { |
||||||
|
|
||||||
|
return this.transformModel.getPreferredTransform(size, layer); |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
{@inheritDoc} |
||||||
|
<p> |
||||||
|
This implementation does the following: |
||||||
|
<ol> |
||||||
|
<li> |
||||||
|
A {@link BufferedImage} is created the size of the clip bounds of the |
||||||
|
argument graphics object.</li> |
||||||
|
<li> |
||||||
|
A Graphics object is obtained from the image.</li> |
||||||
|
<li> |
||||||
|
The image is filled with a background color.</li> |
||||||
|
<li> |
||||||
|
The image graphics is translated according to x and y of the clip bounds. |
||||||
|
</li> |
||||||
|
<li> |
||||||
|
The clip from the argument graphics object is set to the image graphics.</li> |
||||||
|
<li> |
||||||
|
{@link #configureGraphics(Graphics2D, JXLayer)} is invoked with the image |
||||||
|
graphics as an argument.</li> |
||||||
|
<li> |
||||||
|
{@link #paintLayer(Graphics2D, JXLayer)} is invoked with the image |
||||||
|
graphics as an argument.</li> |
||||||
|
<li> |
||||||
|
The image graphics is disposed.</li> |
||||||
|
<li> |
||||||
|
The image is drawn on the argument graphics object.</li> |
||||||
|
</ol> |
||||||
|
*/ |
||||||
|
/* @SuppressWarnings("unchecked") |
||||||
|
@Override |
||||||
|
public final void paint(Graphics g, JComponent component) { |
||||||
|
Graphics2D g2 = (Graphics2D) g; |
||||||
|
JXLayer<? extends JComponent> layer = (JXLayer<? extends JComponent>) component; |
||||||
|
Shape clip = g2.getClip(); |
||||||
|
Rectangle clipBounds = g2.getClipBounds(); |
||||||
|
BufferedImage buffer = layer.getGraphicsConfiguration() |
||||||
|
.createCompatibleImage(clipBounds.width, clipBounds.height, |
||||||
|
Transparency.OPAQUE);//
|
||||||
|
Graphics2D g3 = buffer.createGraphics(); |
||||||
|
try { |
||||||
|
g3.setColor(this.getBackgroundColor(layer)); |
||||||
|
g3.fillRect(0, 0, buffer.getWidth(), buffer.getHeight()); |
||||||
|
g3.translate(-clipBounds.x, -clipBounds.y); |
||||||
|
g3.setClip(clip); |
||||||
|
configureGraphics(g3, layer); |
||||||
|
paintLayer(g3, layer); |
||||||
|
} catch (Throwable t) {*/ |
||||||
|
/* |
||||||
|
* Under some rare circumstances, the graphics engine may throw a |
||||||
|
* transformation exception like this: |
||||||
|
* |
||||||
|
* sun.dc.pr.PRError: setPenT4: invalid pen transformation |
||||||
|
* (singular) |
||||||
|
* |
||||||
|
* As far as I understand this happens when the result of the |
||||||
|
* transformation has a zero sized surface. |
||||||
|
* |
||||||
|
* It will happen for example when shear X and shear Y are both set |
||||||
|
* to 1. |
||||||
|
* |
||||||
|
* It will also happen when scale X or scale Y are set to 0. |
||||||
|
* |
||||||
|
* Since this Exception only seems to be thrown under the condition |
||||||
|
* of a zero sized painting surface, no harm is done. Therefore the |
||||||
|
* error logging below has been commented out, but remain in the |
||||||
|
* source for the case that someone wants to investigate this |
||||||
|
* phenomenon in more depth. |
||||||
|
* |
||||||
|
* The Exception however MUST be caught, not only to be able dispose |
||||||
|
* the image's graphics object, but also to prevent that JXLayer |
||||||
|
* enters a problematic state (the isPainting flag would not be |
||||||
|
* reset). |
||||||
|
*/ |
||||||
|
// System.err.println(t);
|
||||||
|
// AffineTransform at = g3.getTransform();
|
||||||
|
// System.err.println(at);
|
||||||
|
// System.err.println("scaleX = " + at.getScaleX() + " scaleY = "
|
||||||
|
// + at.getScaleY() + " shearX = " + at.getShearX()
|
||||||
|
// + " shearY = " + at.getShearY());
|
||||||
|
/*} finally { |
||||||
|
g3.dispose(); |
||||||
|
} |
||||||
|
g2.drawImage(buffer, clipBounds.x, clipBounds.y, null); |
||||||
|
setDirty(false); |
||||||
|
}*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Overridden to replace the {@link LayoutManager}, to add some listeners and to ensure that an |
||||||
|
* appropriate {@link RepaintManager} is installed. |
||||||
|
* |
||||||
|
* @see #uninstallUI(JComponent) |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public void installUI(@NotNull final JComponent component) { |
||||||
|
super.installUI(component); |
||||||
|
JXLayer<? extends JComponent> installedLayer = this.getInstalledLayer(); |
||||||
|
originalLayout = installedLayer.getLayout(); |
||||||
|
installedLayer.addPropertyChangeListener(KEY_VIEW, this.viewChangeListener); |
||||||
|
installedLayer.setLayout(transformLayout); |
||||||
|
setView(installedLayer.getView()); |
||||||
|
if (!delegatePossible) { |
||||||
|
RepaintManagerUtils.ensureRepaintManagerSet(installedLayer, rpmProvider); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Overridden to restore the original {@link LayoutManager} and remove some listeners. |
||||||
|
* |
||||||
|
* @param c the component. |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public void uninstallUI(@NotNull final JComponent c) { |
||||||
|
JXLayer<? extends JComponent> installedLayer = this.getInstalledLayer(); |
||||||
|
Objects.requireNonNull(installedLayer) |
||||||
|
.removePropertyChangeListener(KEY_VIEW, this.viewChangeListener); |
||||||
|
installedLayer.setLayout(originalLayout); |
||||||
|
setView(null); |
||||||
|
super.uninstallUI(c); |
||||||
|
} |
||||||
|
|
||||||
|
private void setView(final JComponent view) { |
||||||
|
if (delegatePossible) { |
||||||
|
if (this.view != null) { |
||||||
|
SwingUtilities3.setDelegateRepaintManager(this.view, null); |
||||||
|
} |
||||||
|
} |
||||||
|
this.view = view; |
||||||
|
if (delegatePossible) { |
||||||
|
if (this.view != null) { |
||||||
|
SwingUtilities3.setDelegateRepaintManager(this.view, wrappedManager); |
||||||
|
} |
||||||
|
} |
||||||
|
setDirty(true); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Replace the currently active rendering hints with new hints. |
||||||
|
* |
||||||
|
* @param hints the new rendering hints or {@code null} to clear all rendering hints |
||||||
|
*/ |
||||||
|
public void setRenderingHints(@Nullable final Map<RenderingHints.Key, Object> hints) { |
||||||
|
this.renderingHints.clear(); |
||||||
|
if (hints != null) { |
||||||
|
this.renderingHints.putAll(hints); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Primarily intended for use by {@link RepaintManager}. |
||||||
|
* |
||||||
|
* @param rect a rectangle |
||||||
|
* @param layer the layer |
||||||
|
* @return the argument rectangle if no {@link AffineTransform} is available, else a new rectangle |
||||||
|
*/ |
||||||
|
public final Rectangle transform(@NotNull final Rectangle rect, final JXLayer<? extends JComponent> layer) { |
||||||
|
AffineTransform at = getTransform(layer); |
||||||
|
if (at == null) { |
||||||
|
return rect; |
||||||
|
} else { |
||||||
|
Area area = new Area(rect); |
||||||
|
area.transform(at); |
||||||
|
return area.getBounds(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Mark {@link TransformUI} as dirty if the LookAndFeel was changed. |
||||||
|
* |
||||||
|
* @param layer the {@link JXLayer} this {@link TransformUI} is set to |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public void updateUI(final JXLayer<? extends JComponent> layer) { |
||||||
|
setDirty(true); |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
* Get the most suitable background color. |
||||||
|
*/ |
||||||
|
private Color getBackgroundColor(@NotNull final JXLayer<? extends JComponent> layer) { |
||||||
|
Container colorProvider = layer.getView() == null ? layer : layer.getView(); |
||||||
|
while (colorProvider != null && !colorProvider.isOpaque()) { |
||||||
|
colorProvider = colorProvider.getParent(); |
||||||
|
} |
||||||
|
return colorProvider == null ? SystemColor.desktop : colorProvider.getBackground(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set a complete hierarchy to non double buffered and remember the components that were double |
||||||
|
* buffered. |
||||||
|
* |
||||||
|
* @param component the component. |
||||||
|
*/ |
||||||
|
private void setToNoDoubleBuffering(final Component component) { |
||||||
|
if (component instanceof JComponent) { |
||||||
|
JComponent comp = (JComponent) component; |
||||||
|
if (comp.isDoubleBuffered()) { |
||||||
|
originalDoubleBuffered.add(comp); |
||||||
|
comp.setDoubleBuffered(false); |
||||||
|
} |
||||||
|
} |
||||||
|
if (component instanceof Container) { |
||||||
|
Container container = (Container) component; |
||||||
|
for (int index = 0; index < container.getComponentCount(); index++) { |
||||||
|
setToNoDoubleBuffering(container.getComponent(index)); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* If the view of the {@link JXLayer} is (partly) obscured by its parent (this is the case when |
||||||
|
* the size of the view (in component space) is larger than the size of the {@link JXLayer}), the |
||||||
|
* obscured parts will not be painted by the super implementation. Therefore, only under this |
||||||
|
* condition, a special painting technique is executed: |
||||||
|
* |
||||||
|
* <ol> |
||||||
|
* <li>All descendants of the {@link JXLayer} are temporarily set to non double buffered. |
||||||
|
* <li>The graphics object is translated for the X and Y coordinates of the view. |
||||||
|
* <li>The view is painted. |
||||||
|
* <li>The original double buffered property is restored for all descendants. |
||||||
|
* </ol> |
||||||
|
* |
||||||
|
* <p>In all other cases, the super method is invoked. |
||||||
|
* |
||||||
|
* <p>The {@code g2} argument is a graphics object obtained from a {@link BufferedImage}. |
||||||
|
* |
||||||
|
* @see #paint(Graphics, JComponent) |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
protected final void paintLayer( |
||||||
|
@NotNull final Graphics2D g2, @NotNull final JXLayer<? extends JComponent> layer) { |
||||||
|
JComponent view = layer.getView(); |
||||||
|
if (view != null) { |
||||||
|
if (view.getX() < 0 || view.getY() < 0) { |
||||||
|
setToNoDoubleBuffering(view); |
||||||
|
g2.translate(view.getX(), view.getY()); |
||||||
|
view.paint(g2); |
||||||
|
for (JComponent comp : originalDoubleBuffered) { |
||||||
|
comp.setDoubleBuffered(true); |
||||||
|
} |
||||||
|
originalDoubleBuffered.clear(); |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
super.paintLayer(g2, layer); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the {@link AffineTransform} customized for the {@code layer} argument. |
||||||
|
* |
||||||
|
* <p>In {@code enabled} state this method is delegated to the {@link TransformModel} that has |
||||||
|
* been set. Otherwise {@code null} will be returned. |
||||||
|
*/ |
||||||
|
@NotNull |
||||||
|
@Override |
||||||
|
protected final AffineTransform getTransform(final JXLayer<? extends JComponent> layer) { |
||||||
|
return transformModel.getTransform(layer); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the rendering hints. |
||||||
|
* |
||||||
|
* @return the rendering hints |
||||||
|
* @see #setRenderingHints(Map) |
||||||
|
* @see #addRenderingHints(Map) |
||||||
|
* @see #addRenderingHint(RenderingHints.Key, Object) |
||||||
|
*/ |
||||||
|
@NotNull |
||||||
|
@Override |
||||||
|
protected Map<RenderingHints.Key, Object> getRenderingHints(final JXLayer<? extends JComponent> layer) { |
||||||
|
return renderingHints; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* A delegate {@link RepaintManager} that can be set on the view of a {@link JXLayer} in Java |
||||||
|
* versions starting with Java 6u10. |
||||||
|
* |
||||||
|
* <p>For older Java versions, {@link RepaintManager#setCurrentManager(RepaintManager)} will be |
||||||
|
* used with either {@link TransformRPMFallBack} or {@link TransformRPMSwingX}. |
||||||
|
*/ |
||||||
|
protected static final class TransformRepaintManager extends RepaintManager { |
||||||
|
|
||||||
|
private TransformRepaintManager() { |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Finds the JXLayer ancestor and have ancestor marked invalid via the current {@link |
||||||
|
* RepaintManager}. |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public void addInvalidComponent(final JComponent invalidComponent) { |
||||||
|
JXLayer<? extends JComponent> layer = findJXLayer(invalidComponent); |
||||||
|
RepaintManager.currentManager(layer).addInvalidComponent(layer); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Finds the JXLayer ancestor and have the ancestor marked as dirty with the transformed |
||||||
|
* rectangle via the current {@link RepaintManager}. |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public void addDirtyRegion(@NotNull final JComponent c, final int x, final int y, final int w, final int h) { |
||||||
|
if (c.isShowing()) { |
||||||
|
JXLayer<? extends JComponent> layer = findJXLayer(c); |
||||||
|
TransformUI ui = (TransformUI) layer.getUI(); |
||||||
|
Point point = c.getLocationOnScreen(); |
||||||
|
SwingUtilities.convertPointFromScreen(point, layer); |
||||||
|
Rectangle transformPortRegion = ui.transform(new Rectangle(x + point.x, y + point.y, w, h), |
||||||
|
layer); |
||||||
|
RepaintManager.currentManager(layer).addDirtyRegion(layer, |
||||||
|
transformPortRegion.x, |
||||||
|
transformPortRegion.y, |
||||||
|
transformPortRegion.width, |
||||||
|
transformPortRegion.height); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Find the ancestor {@link JXLayer} instance. |
||||||
|
* |
||||||
|
* @param c a component |
||||||
|
* @return the ancestor {@link JXLayer} instance |
||||||
|
*/ |
||||||
|
@NotNull |
||||||
|
@SuppressWarnings("unchecked") |
||||||
|
private JXLayer<? extends JComponent> findJXLayer(final JComponent c) { |
||||||
|
JXLayer<?> layer = (JXLayer<?>) SwingUtilities.getAncestorOfClass(JXLayer.class, c); |
||||||
|
if (layer != null) { |
||||||
|
LayerUI<?> layerUI = layer.getUI(); |
||||||
|
if (layerUI instanceof TransformUI) { |
||||||
|
return (JXLayer<? extends JComponent>) layer; |
||||||
|
} else { |
||||||
|
return findJXLayer(layer); |
||||||
|
} |
||||||
|
} |
||||||
|
throw new Error("No parent JXLayer with TransformUI found"); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,116 @@ |
|||||||
|
package org.pbjar.jxlayer.plaf.ext.transform; |
||||||
|
/* |
||||||
|
* Copyright (c) 2009, Piet Blok |
||||||
|
* All rights reserved. |
||||||
|
* <p> |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions |
||||||
|
* are met: |
||||||
|
* <p> |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* * Redistributions in binary form must reproduce the above |
||||||
|
* copyright notice, this list of conditions and the following |
||||||
|
* disclaimer in the documentation and/or other materials provided |
||||||
|
* with the distribution. |
||||||
|
* * Neither the name of the copyright holder nor the names of the |
||||||
|
* contributors may be used to endorse or promote products derived |
||||||
|
* from this software without specific prior written permission. |
||||||
|
* <p> |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
*/ |
||||||
|
|
||||||
|
import org.jdesktop.jxlayer.JXLayer; |
||||||
|
import org.jetbrains.annotations.NotNull; |
||||||
|
|
||||||
|
import java.awt.*; |
||||||
|
import java.io.Serializable; |
||||||
|
|
||||||
|
/** |
||||||
|
* A copy of the private static inner class in JXLayer. |
||||||
|
* |
||||||
|
* @author Piet Blok |
||||||
|
*/ |
||||||
|
public class DefaultLayerLayout implements LayoutManager, Serializable { |
||||||
|
/** |
||||||
|
* {@inheritDoc} |
||||||
|
*/ |
||||||
|
public void addLayoutComponent(final String name, final Component comp) { |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* {@inheritDoc} |
||||||
|
*/ |
||||||
|
public void removeLayoutComponent(final Component comp) { |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* {@inheritDoc} |
||||||
|
*/ |
||||||
|
@NotNull |
||||||
|
public Dimension preferredLayoutSize(final Container parent) { |
||||||
|
JXLayer<?> layer = (JXLayer<?>) parent; |
||||||
|
Insets insets = layer.getInsets(); |
||||||
|
Dimension ret = new Dimension(insets.left + insets.right, insets.top + insets.bottom); |
||||||
|
Component view = layer.getView(); |
||||||
|
if (view != null) { |
||||||
|
Dimension size = view.getPreferredSize(); |
||||||
|
if (size.width > 0 && size.height > 0) { |
||||||
|
ret.width += size.width; |
||||||
|
ret.height += size.height; |
||||||
|
} |
||||||
|
} |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* {@inheritDoc} |
||||||
|
*/ |
||||||
|
@NotNull |
||||||
|
public Dimension minimumLayoutSize(final Container parent) { |
||||||
|
JXLayer<?> layer = (JXLayer<?>) parent; |
||||||
|
Insets insets = layer.getInsets(); |
||||||
|
Dimension ret = new Dimension(insets.left + insets.right, insets.top + insets.bottom); |
||||||
|
Component view = layer.getView(); |
||||||
|
if (view != null) { |
||||||
|
Dimension size = view.getMinimumSize(); |
||||||
|
ret.width += size.width; |
||||||
|
ret.height += size.height; |
||||||
|
} |
||||||
|
if (ret.width == 0 || ret.height == 0) { |
||||||
|
ret.width = 4; |
||||||
|
ret.height = 4; |
||||||
|
} |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* {@inheritDoc} |
||||||
|
*/ |
||||||
|
public void layoutContainer(final Container parent) { |
||||||
|
JXLayer<?> layer = (JXLayer<?>) parent; |
||||||
|
Component view = layer.getView(); |
||||||
|
Component glassPane = layer.getGlassPane(); |
||||||
|
if (view != null) { |
||||||
|
Insets insets = layer.getInsets(); |
||||||
|
view.setLocation(insets.left, insets.top); |
||||||
|
view.setSize( |
||||||
|
layer.getWidth() - insets.left - insets.right, |
||||||
|
layer.getHeight() - insets.top - insets.bottom); |
||||||
|
} |
||||||
|
if (glassPane != null) { |
||||||
|
glassPane.setLocation(0, 0); |
||||||
|
glassPane.setSize(layer.getWidth(), layer.getHeight()); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,495 @@ |
|||||||
|
package org.pbjar.jxlayer.plaf.ext.transform; |
||||||
|
/* |
||||||
|
* Copyright (c) 2009, Piet Blok All rights reserved. |
||||||
|
* <p> |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are met: |
||||||
|
* <p> |
||||||
|
* * Redistributions of source code must retain the above copyright notice, |
||||||
|
* this list of conditions and the following disclaimer. * Redistributions in |
||||||
|
* binary form must reproduce the above copyright notice, this list of |
||||||
|
* conditions and the following disclaimer in the documentation and/or other |
||||||
|
* materials provided with the distribution. * Neither the name of the copyright |
||||||
|
* holder nor the names of the contributors may be used to endorse or promote |
||||||
|
* products derived from this software without specific prior written |
||||||
|
* permission. |
||||||
|
* <p> |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
||||||
|
* POSSIBILITY OF SUCH DAMAGE. |
||||||
|
*/ |
||||||
|
|
||||||
|
import org.jdesktop.jxlayer.JXLayer; |
||||||
|
import org.jetbrains.annotations.Contract; |
||||||
|
import org.jetbrains.annotations.NotNull; |
||||||
|
import org.jetbrains.annotations.Nullable; |
||||||
|
|
||||||
|
import javax.swing.*; |
||||||
|
import javax.swing.event.ChangeEvent; |
||||||
|
import javax.swing.event.ChangeListener; |
||||||
|
import java.awt.*; |
||||||
|
import java.awt.geom.AffineTransform; |
||||||
|
import java.awt.geom.Area; |
||||||
|
import java.awt.geom.Point2D; |
||||||
|
import java.awt.geom.Rectangle2D; |
||||||
|
import java.util.Arrays; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.WeakHashMap; |
||||||
|
import java.util.function.Function; |
||||||
|
|
||||||
|
/** |
||||||
|
* This is an implementation of {@link TransformModel} with methods to explicitly set transformation |
||||||
|
* values. |
||||||
|
* |
||||||
|
* @author Piet Blok |
||||||
|
*/ |
||||||
|
public class DefaultTransformModel implements TransformModel { |
||||||
|
|
||||||
|
private final Map<ChangeListener, Object> listeners = new WeakHashMap<>(); |
||||||
|
/** |
||||||
|
* The transform object that will be recalculated upon any change. |
||||||
|
*/ |
||||||
|
private final AffineTransform transform = new AffineTransform(); |
||||||
|
/** |
||||||
|
* Is populated with the current values. |
||||||
|
*/ |
||||||
|
private final Object[] values = Type.createArray(); |
||||||
|
/** |
||||||
|
* Is populated with the previous values. |
||||||
|
*/ |
||||||
|
private final Object[] prevValues = Type.createArray(); |
||||||
|
|
||||||
|
private Point2D rotationCenter; |
||||||
|
private Function<Dimension, Point2D> supplier; |
||||||
|
private boolean valid = false; |
||||||
|
|
||||||
|
@Override |
||||||
|
public void addChangeListener(final ChangeListener listener) { |
||||||
|
listeners.put(listener, null); |
||||||
|
} |
||||||
|
|
||||||
|
@NotNull |
||||||
|
@Override |
||||||
|
public AffineTransform getPreferredTransform(@Nullable final Dimension size, final JXLayer<?> layer) { |
||||||
|
var p = getRotationCenter(size); |
||||||
|
double centerX = p.getX(); |
||||||
|
double centerY = p.getY(); |
||||||
|
AffineTransform transform = transformNoScale(centerX, centerY); |
||||||
|
double scaleX = getValue(Type.PreferredScale); |
||||||
|
transform.translate(centerX, centerY); |
||||||
|
transform.scale(getValue(Type.Mirror) ? -scaleX : scaleX, scaleX); |
||||||
|
transform.translate(-centerX, -centerY); |
||||||
|
return transform; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the rotation center corresponding to the size. |
||||||
|
* |
||||||
|
* @param size the size. |
||||||
|
* @return center of rotation. |
||||||
|
*/ |
||||||
|
public Point2D getRotationCenter(@Nullable final Dimension size) { |
||||||
|
if (supplier != null) { |
||||||
|
return supplier.apply(size); |
||||||
|
} |
||||||
|
double centerX = size == null ? 0 : size.getWidth() / 2.0; |
||||||
|
double centerY = size == null ? 0 : size.getHeight() / 2.0; |
||||||
|
return rotationCenter == null ? new Point2D.Double(centerX, centerY) : rotationCenter; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Apply the prescribed transformations, excluding the scale. |
||||||
|
* |
||||||
|
* @param centerX a center X |
||||||
|
* @param centerY a center Y |
||||||
|
* @return a new {@link AffineTransform} |
||||||
|
*/ |
||||||
|
@NotNull |
||||||
|
protected AffineTransform transformNoScale(final double centerX, final double centerY) { |
||||||
|
AffineTransform at = new AffineTransform(); |
||||||
|
at.translate(centerX, centerY); |
||||||
|
at.rotate(getRotation()); |
||||||
|
at.quadrantRotate(getQuadrantRotation()); |
||||||
|
at.shear(getShearX(), getShearY()); |
||||||
|
at.translate(-centerX, -centerY); |
||||||
|
return at; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the rotation value in radians as set by {@link #setRotation(double)}. The default value is |
||||||
|
* {@code 0}. |
||||||
|
* |
||||||
|
* @return the rotation value. |
||||||
|
* @see #setRotation(double) |
||||||
|
*/ |
||||||
|
public double getRotation() { |
||||||
|
return (Double) getValue(Type.Rotation); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the quadrant rotation value. The default value is {@code 0}. |
||||||
|
* |
||||||
|
* @return the quadrant rotation value |
||||||
|
* @see #setQuadrantRotation(int) |
||||||
|
*/ |
||||||
|
public int getQuadrantRotation() { |
||||||
|
return (Integer) getValue(Type.QuadrantRotation); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the rotation in quadrants. The default value is {@code 0}. |
||||||
|
* |
||||||
|
* @param newValue the number of quadrants |
||||||
|
* @see #getQuadrantRotation() |
||||||
|
*/ |
||||||
|
public void setQuadrantRotation(final int newValue) { |
||||||
|
setValue(Type.QuadrantRotation, newValue); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the shearX value as set by {@link #setShearX(double)}; The default value is {@code 0}. |
||||||
|
* |
||||||
|
* @return the shear x value |
||||||
|
* @see #setShearX(double) |
||||||
|
*/ |
||||||
|
public double getShearX() { |
||||||
|
return (Double) getValue(Type.ShearX); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the shearX value. The default value is {@code 0}. |
||||||
|
* |
||||||
|
* @param newValue the shear x |
||||||
|
* @see #getShearX() |
||||||
|
*/ |
||||||
|
public void setShearX(final double newValue) { |
||||||
|
setValue(Type.ShearX, newValue); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the shearY value as set by {@link #setShearY(double)}; The default value is {@code 0}. |
||||||
|
* |
||||||
|
* @return the shear y value |
||||||
|
* @see #setShearY(double) |
||||||
|
*/ |
||||||
|
public double getShearY() { |
||||||
|
return (Double) getValue(Type.ShearY); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the shearY value. The default value is {@code 0}. |
||||||
|
* |
||||||
|
* @param newValue the shear y |
||||||
|
* @see #getShearY() |
||||||
|
*/ |
||||||
|
public void setShearY(final double newValue) { |
||||||
|
setValue(Type.ShearY, newValue); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the rotation in radians. The default value is {@code 0}. |
||||||
|
* |
||||||
|
* @param newValue the rotation in radians |
||||||
|
* @see #getRotation() |
||||||
|
*/ |
||||||
|
public void setRotation(final double newValue) { |
||||||
|
setValue(Type.Rotation, newValue); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Return the currently active {@link AffineTransform}. Recalculate if needed. |
||||||
|
* |
||||||
|
* @return the currently active {@link AffineTransform} |
||||||
|
*/ |
||||||
|
@NotNull |
||||||
|
@Override |
||||||
|
public AffineTransform getTransform(@Nullable final JXLayer<? extends JComponent> layer) { |
||||||
|
JComponent view = layer == null ? null : layer.getView(); |
||||||
|
/* |
||||||
|
* Set the current actual program values in addition to the user options. |
||||||
|
*/ |
||||||
|
setValue(Type.LayerWidth, layer == null ? 0 : layer.getWidth()); |
||||||
|
setValue(Type.LayerHeight, layer == null ? 0 : layer.getHeight()); |
||||||
|
setValue(Type.ViewWidth, view == null ? 0 : view.getWidth()); |
||||||
|
setValue(Type.ViewHeight, view == null ? 0 : view.getHeight()); |
||||||
|
/* |
||||||
|
* If any change to previous values, recompute the transform. |
||||||
|
*/ |
||||||
|
if (!Arrays.equals(prevValues, values) || !valid) { |
||||||
|
System.arraycopy(values, 0, prevValues, 0, values.length); |
||||||
|
transform.setToIdentity(); |
||||||
|
if (view != null) { |
||||||
|
var p = getRotationCenter(layer == null ? null : layer.getSize()); |
||||||
|
double centerX = p.getX(); |
||||||
|
double centerY = p.getY(); |
||||||
|
|
||||||
|
AffineTransform nonScaledTransform = transformNoScale(centerX, centerY); |
||||||
|
|
||||||
|
double scaleX; |
||||||
|
double scaleY; |
||||||
|
if (getValue(Type.ScaleToPreferredSize)) { |
||||||
|
scaleX = getValue(Type.PreferredScale); |
||||||
|
scaleY = scaleX; |
||||||
|
} else { |
||||||
|
Area area = new Area(new Rectangle2D.Double(0, 0, view.getWidth(), view.getHeight())); |
||||||
|
area.transform(nonScaledTransform); |
||||||
|
Rectangle2D bounds = area.getBounds2D(); |
||||||
|
scaleX = layer == null ? 0 : layer.getWidth() / bounds.getWidth(); |
||||||
|
scaleY = layer == null ? 0 : layer.getHeight() / bounds.getHeight(); |
||||||
|
|
||||||
|
if (getValue(Type.PreserveAspectRatio)) { |
||||||
|
scaleX = Math.min(scaleX, scaleY); |
||||||
|
scaleY = scaleX; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
transform.translate(centerX, centerY); |
||||||
|
transform.scale(getValue(Type.Mirror) ? -scaleX : scaleX, scaleY); |
||||||
|
transform.translate(-centerX, -centerY); |
||||||
|
transform.concatenate(nonScaledTransform); |
||||||
|
} |
||||||
|
} |
||||||
|
valid = true; |
||||||
|
return transform; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void removeChangeListener(final ChangeListener listener) { |
||||||
|
listeners.remove(listener); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the scale. |
||||||
|
* |
||||||
|
* @return the scale |
||||||
|
* @see #setScale(double) |
||||||
|
*/ |
||||||
|
public double getScale() { |
||||||
|
return (Double) getValue(Type.PreferredScale); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set a scale. |
||||||
|
* |
||||||
|
* <p>The scale is primarily used to calculate a preferred size. Unless {@code |
||||||
|
* ScaleToPreferredSize} is set to {@code true} (see {@link #setScaleToPreferredSize(boolean)} and |
||||||
|
* {@link #isScaleToPreferredSize()}), actual scaling itself is calculated such that the view |
||||||
|
* occupies as much space as possible on the {@link JXLayer}. |
||||||
|
* |
||||||
|
* <p>The default value is 1. |
||||||
|
* |
||||||
|
* @param newValue the preferred scale |
||||||
|
* @throws IllegalArgumentException when the argument value is 0 |
||||||
|
* @see #getScale() |
||||||
|
*/ |
||||||
|
public void setScale(final double newValue) throws IllegalArgumentException { |
||||||
|
if (newValue == 0.0) { |
||||||
|
throw new IllegalArgumentException("Preferred scale can not be set to 0"); |
||||||
|
} |
||||||
|
setValue(Type.PreferredScale, newValue); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set a value and fire a PropertyChange. |
||||||
|
* |
||||||
|
* @param type the value type |
||||||
|
* @param newValue the new value |
||||||
|
*/ |
||||||
|
private void setValue(@NotNull final Type type, final Object newValue) { |
||||||
|
Object oldValue = values[type.ordinal()]; |
||||||
|
values[type.ordinal()] = newValue; |
||||||
|
fireChangeEvent(oldValue, newValue); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* If {!oldValue.equals(newValue)}, a {@link ChangeEvent} will be fired. |
||||||
|
* |
||||||
|
* @param oldValue an old value |
||||||
|
* @param newValue a new value |
||||||
|
*/ |
||||||
|
protected void fireChangeEvent(@NotNull final Object oldValue, final Object newValue) { |
||||||
|
if (!oldValue.equals(newValue)) { |
||||||
|
ChangeEvent event = new ChangeEvent(this); |
||||||
|
for (ChangeListener listener : listeners.keySet()) { |
||||||
|
listener.stateChanged(event); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@NotNull |
||||||
|
@SuppressWarnings("unchecked") |
||||||
|
protected <T> T getValue(@NotNull final Type type) { |
||||||
|
return (T) values[type.ordinal()]; |
||||||
|
} |
||||||
|
|
||||||
|
public void setRotationCenter(final Point2D rotationCenter) { |
||||||
|
this.rotationCenter = rotationCenter; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the mirror property. |
||||||
|
* |
||||||
|
* <p>The default value is {@code false}. |
||||||
|
* |
||||||
|
* @return {@code true} if the transformation will mirror the view. |
||||||
|
* @see #setMirror(boolean) |
||||||
|
*/ |
||||||
|
public boolean isMirror() { |
||||||
|
return (Boolean) getValue(Type.Mirror); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the mirror property. |
||||||
|
* |
||||||
|
* <p>The default value is {@code false} |
||||||
|
* |
||||||
|
* @param newValue the new value |
||||||
|
* @see #isMirror() |
||||||
|
*/ |
||||||
|
public void setMirror(final boolean newValue) { |
||||||
|
setValue(Type.Mirror, newValue); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the preserve aspect ratio value. |
||||||
|
* |
||||||
|
* <p>The default value is {@code true}. |
||||||
|
* |
||||||
|
* @return {@code true} if preserving aspect ratio, {@code false} otherwise |
||||||
|
* @see #setPreserveAspectRatio(boolean) |
||||||
|
*/ |
||||||
|
public boolean isPreserveAspectRatio() { |
||||||
|
return (Boolean) getValue(Type.PreserveAspectRatio); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set preserve aspect ratio. |
||||||
|
* |
||||||
|
* <p>The default value is {@code true}. |
||||||
|
* |
||||||
|
* @param newValue the new value |
||||||
|
* @see #isPreserveAspectRatio() |
||||||
|
*/ |
||||||
|
public void setPreserveAspectRatio(final boolean newValue) { |
||||||
|
setValue(Type.PreserveAspectRatio, newValue); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the scale to preferred size value. |
||||||
|
* |
||||||
|
* <p>The default value is {@code false}. |
||||||
|
* |
||||||
|
* <p>When {@code true}, the view is scaled according to the preferred scale, regardless of the |
||||||
|
* size of the {@link JXLayer}. |
||||||
|
* |
||||||
|
* <p>When {@code false}, the view is scaled to occupy as much as possible of the size of the |
||||||
|
* {@link JXLayer}. |
||||||
|
* |
||||||
|
* @return {@code true} if scale to preferred size, {@code false} otherwise |
||||||
|
* @see #setScaleToPreferredSize(boolean) |
||||||
|
*/ |
||||||
|
public boolean isScaleToPreferredSize() { |
||||||
|
return (Boolean) getValue(Type.ScaleToPreferredSize); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set scaleToPreferredSize. |
||||||
|
* |
||||||
|
* <p>The default value is {@code false}. |
||||||
|
* |
||||||
|
* <p>When {@code true}, the view is scaled according to the preferred scale, regardless of the |
||||||
|
* size of the {@link JXLayer}. |
||||||
|
* |
||||||
|
* <p>When {@code false}, the view is scaled to occupy as much as possible of the size of the |
||||||
|
* {@link JXLayer}. |
||||||
|
* |
||||||
|
* @param newValue the new value |
||||||
|
* @see #isScaleToPreferredSize() |
||||||
|
*/ |
||||||
|
public void setScaleToPreferredSize(final boolean newValue) { |
||||||
|
setValue(Type.ScaleToPreferredSize, newValue); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the supplier for calculating the center of rotation. |
||||||
|
* |
||||||
|
* @param supplier the supplier. |
||||||
|
*/ |
||||||
|
public void setRotationCenterSupplier(final Function<Dimension, Point2D> supplier) { |
||||||
|
{ |
||||||
|
this.supplier = supplier; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Force the translation to be recalculated the next time it is needed. |
||||||
|
*/ |
||||||
|
public void invalidate() { |
||||||
|
valid = false; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Enum for internal convenience. |
||||||
|
* |
||||||
|
* <p>Describes the values that on change trigger recalculation of the transform. All have a |
||||||
|
* default value, used for initializing arrays. |
||||||
|
* |
||||||
|
* <p>These enums are used for two purposes: |
||||||
|
* |
||||||
|
* <p>1: To easily detect a change that requires renewed calculation of the transform(both program |
||||||
|
* values and user options). |
||||||
|
* |
||||||
|
* <p>2: To generalize setters (both program values and user options) and getters (only |
||||||
|
* userOptions) for the various values. |
||||||
|
* |
||||||
|
* <p>There are two groups: |
||||||
|
* |
||||||
|
* <p>1: Program values that reflect the current size etc. of affected components |
||||||
|
* |
||||||
|
* <p>2: User options |
||||||
|
*/ |
||||||
|
protected enum Type { |
||||||
|
/* |
||||||
|
* Program values |
||||||
|
*/ |
||||||
|
LayerWidth(0), |
||||||
|
LayerHeight(0), |
||||||
|
ViewWidth(0), |
||||||
|
ViewHeight(0), |
||||||
|
/* |
||||||
|
* User options |
||||||
|
*/ |
||||||
|
PreferredScale(1.0), |
||||||
|
Rotation(0.0), |
||||||
|
ShearX(0.0), |
||||||
|
ShearY(0.0), |
||||||
|
QuadrantRotation(0), |
||||||
|
PreserveAspectRatio(Boolean.TRUE), |
||||||
|
ScaleToPreferredSize(Boolean.FALSE), |
||||||
|
Mirror(Boolean.FALSE); |
||||||
|
|
||||||
|
private final Object defaultValue; |
||||||
|
|
||||||
|
@Contract(pure = true) |
||||||
|
Type(final Object defaultValue) { |
||||||
|
this.defaultValue = defaultValue; |
||||||
|
} |
||||||
|
|
||||||
|
@NotNull |
||||||
|
public static Object[] createArray() { |
||||||
|
Object[] array = new Object[values().length]; |
||||||
|
for (Type type : values()) { |
||||||
|
array[type.ordinal()] = type.defaultValue; |
||||||
|
} |
||||||
|
return array; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,140 @@ |
|||||||
|
/* |
||||||
|
Copyright (c) 2009, Piet Blok |
||||||
|
All rights reserved. |
||||||
|
<p> |
||||||
|
Redistribution and use in source and binary forms, with or without |
||||||
|
modification, are permitted provided that the following conditions |
||||||
|
are met: |
||||||
|
<p> |
||||||
|
* Redistributions of source code must retain the above copyright |
||||||
|
notice, this list of conditions and the following disclaimer. |
||||||
|
* Redistributions in binary form must reproduce the above |
||||||
|
copyright notice, this list of conditions and the following |
||||||
|
disclaimer in the documentation and/or other materials provided |
||||||
|
with the distribution. |
||||||
|
* Neither the name of the copyright holder nor the names of the |
||||||
|
contributors may be used to endorse or promote products derived |
||||||
|
from this software without specific prior written permission. |
||||||
|
<p> |
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.pbjar.jxlayer.plaf.ext.transform; |
||||||
|
|
||||||
|
import org.jdesktop.jxlayer.JXLayer; |
||||||
|
import org.jdesktop.jxlayer.plaf.LayerUI; |
||||||
|
import org.jetbrains.annotations.NotNull; |
||||||
|
import org.pbjar.jxlayer.plaf.ext.TransformUI; |
||||||
|
|
||||||
|
import javax.swing.*; |
||||||
|
import java.awt.*; |
||||||
|
import java.awt.geom.AffineTransform; |
||||||
|
import java.awt.geom.Area; |
||||||
|
import java.awt.geom.Rectangle2D; |
||||||
|
|
||||||
|
/** |
||||||
|
* A specialized layout manager for {@link JXLayer} in combination with the {@link TransformUI}. |
||||||
|
* |
||||||
|
* <p>It extends {@link DefaultLayerLayout} and, as long as no enabled {@link TransformUI} is set to |
||||||
|
* {@link JXLayer}, will act exactly the same as its super class. |
||||||
|
* |
||||||
|
* <p>However, when the above conditions are all true, its behavior becomes different: |
||||||
|
* |
||||||
|
* <ol> |
||||||
|
* <li>Instead of setting the view's size to the layer's calculated inner area, it will set the |
||||||
|
* view's size to its preferred size. |
||||||
|
* <li>Instead of setting the view's bounds to the calculated inner area, it will center the view |
||||||
|
* in that inner area. This may result in some parts of the view formally obscured, or, some |
||||||
|
* parts of the inner area not covered by the view. |
||||||
|
* <li>The preferred size will first be computed by the super implementation. Then, before |
||||||
|
* returning, the calculated size will be transformed with the {@link AffineTransform} |
||||||
|
* returned by {@link TransformUI#getPreferredTransform(Dimension, JXLayer)}; |
||||||
|
* <li>The minimum size will first be computed by the super implementation. Then, before |
||||||
|
* returning, the calculated size will be transformed with the {@link AffineTransform} |
||||||
|
* returned by {@link TransformUI#getPreferredTransform(Dimension, JXLayer)}; |
||||||
|
* </ol> |
||||||
|
* |
||||||
|
* @see JXLayer#getView() |
||||||
|
* @see JXLayer#getGlassPane() |
||||||
|
* @see TransformUI |
||||||
|
*/ |
||||||
|
public class TransformLayout extends DefaultLayerLayout { |
||||||
|
|
||||||
|
/** |
||||||
|
* Overridden to apply a different layout when the {@link LayerUI} is an instance of {@link |
||||||
|
* TransformUI}. If this is not the case, the super implementation will be invoked. |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public void layoutContainer(final Container parent) { |
||||||
|
JXLayer<?> layer = (JXLayer<?>) parent; |
||||||
|
LayerUI<?> layerUI = layer.getUI(); |
||||||
|
if (layerUI instanceof TransformUI) { |
||||||
|
JComponent view = (JComponent) layer.getView(); |
||||||
|
JComponent glassPane = layer.getGlassPane(); |
||||||
|
if (view != null) { |
||||||
|
Rectangle innerArea = new Rectangle(); |
||||||
|
SwingUtilities.calculateInnerArea(layer, innerArea); |
||||||
|
view.setSize(view.getPreferredSize()); |
||||||
|
Rectangle viewRect = new Rectangle(0, 0, view.getWidth(), view.getHeight()); |
||||||
|
int x = (int) Math.round(innerArea.getCenterX() - viewRect.getCenterX()); |
||||||
|
int y = (int) Math.round(innerArea.getCenterY() - viewRect.getCenterY()); |
||||||
|
viewRect.translate(x, y); |
||||||
|
view.setBounds(viewRect); |
||||||
|
} |
||||||
|
if (glassPane != null) { |
||||||
|
glassPane.setLocation(0, 0); |
||||||
|
glassPane.setSize(layer.getWidth(), layer.getHeight()); |
||||||
|
} |
||||||
|
return; |
||||||
|
} |
||||||
|
super.layoutContainer(parent); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Overridden to apply a preferred transform on the {@link Dimension} object returned from the |
||||||
|
* super implementation. |
||||||
|
*/ |
||||||
|
@NotNull |
||||||
|
@Override |
||||||
|
public Dimension minimumLayoutSize(final Container parent) { |
||||||
|
return transform(parent, super.minimumLayoutSize(parent)); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Overridden to apply a preferred transform on the {@link Dimension} object returned from the |
||||||
|
* super implementation. |
||||||
|
*/ |
||||||
|
@NotNull |
||||||
|
@Override |
||||||
|
public Dimension preferredLayoutSize(final Container parent) { |
||||||
|
return transform(parent, super.preferredLayoutSize(parent)); |
||||||
|
} |
||||||
|
|
||||||
|
@NotNull |
||||||
|
@SuppressWarnings("unchecked") |
||||||
|
private Dimension transform(final Container parent, @NotNull final Dimension size) { |
||||||
|
JXLayer<JComponent> layer = (JXLayer<JComponent>) parent; |
||||||
|
LayerUI<?> ui = layer.getUI(); |
||||||
|
if (ui instanceof TransformUI) { |
||||||
|
TransformUI transformUI = (TransformUI) ui; |
||||||
|
AffineTransform transform = transformUI.getPreferredTransform(size, layer); |
||||||
|
if (transform != null) { |
||||||
|
Area area = new Area(new Rectangle2D.Double(0, 0, size.getWidth(), size.getHeight())); |
||||||
|
area.transform(transform); |
||||||
|
Rectangle2D bounds = area.getBounds2D(); |
||||||
|
size.setSize(bounds.getWidth(), bounds.getHeight()); |
||||||
|
} |
||||||
|
} |
||||||
|
return size; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,92 @@ |
|||||||
|
/* |
||||||
|
Copyright (c) 2009, Piet Blok |
||||||
|
All rights reserved. |
||||||
|
<p> |
||||||
|
Redistribution and use in source and binary forms, with or without |
||||||
|
modification, are permitted provided that the following conditions |
||||||
|
are met: |
||||||
|
<p> |
||||||
|
* Redistributions of source code must retain the above copyright |
||||||
|
notice, this list of conditions and the following disclaimer. |
||||||
|
* Redistributions in binary form must reproduce the above |
||||||
|
copyright notice, this list of conditions and the following |
||||||
|
disclaimer in the documentation and/or other materials provided |
||||||
|
with the distribution. |
||||||
|
* Neither the name of the copyright holder nor the names of the |
||||||
|
contributors may be used to endorse or promote products derived |
||||||
|
from this software without specific prior written permission. |
||||||
|
<p> |
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.pbjar.jxlayer.plaf.ext.transform; |
||||||
|
|
||||||
|
import org.jdesktop.jxlayer.JXLayer; |
||||||
|
import org.jetbrains.annotations.NotNull; |
||||||
|
import org.pbjar.jxlayer.plaf.ext.TransformUI; |
||||||
|
|
||||||
|
import javax.swing.*; |
||||||
|
import javax.swing.event.ChangeListener; |
||||||
|
import java.awt.*; |
||||||
|
import java.awt.geom.AffineTransform; |
||||||
|
|
||||||
|
/** |
||||||
|
* The {@link TransformModel} interface specifies the methods the {@link TransformUI} will use to |
||||||
|
* interrogate a transformation model. |
||||||
|
* |
||||||
|
* @author Piet Blok |
||||||
|
*/ |
||||||
|
public interface TransformModel { |
||||||
|
|
||||||
|
/** |
||||||
|
* Add a {@link ChangeListener} that will be notified when the internal state of this model |
||||||
|
* changes. |
||||||
|
* |
||||||
|
* @param listener a {@link ChangeListener} |
||||||
|
* @see #removeChangeListener(ChangeListener) |
||||||
|
*/ |
||||||
|
void addChangeListener(ChangeListener listener); |
||||||
|
|
||||||
|
/** |
||||||
|
* Get a preferred {@link AffineTransform}. This method will typically be invoked by programs that |
||||||
|
* calculate a preferred size. |
||||||
|
* |
||||||
|
* <p>The {@code size} argument will be used to compute anchor values for some types of |
||||||
|
* transformations. If the {@code size} argument is {@code null} a value of (0,0) is used for the |
||||||
|
* anchor. |
||||||
|
* |
||||||
|
* @param size a {@link Dimension} instance to be used for an anchor or {@code null} |
||||||
|
* @param layer the {@link JXLayer}. |
||||||
|
* @return a {@link AffineTransform} instance or {@code null} |
||||||
|
*/ |
||||||
|
@NotNull |
||||||
|
AffineTransform getPreferredTransform(Dimension size, JXLayer<?> layer); |
||||||
|
|
||||||
|
/** |
||||||
|
* Get a {@link AffineTransform}. This method will typically be invoked by programs that are about |
||||||
|
* to prepare a {@link Graphics} object. |
||||||
|
* |
||||||
|
* @param layer the {@link JXLayer} |
||||||
|
* @return a {@link AffineTransform} or {@code null} |
||||||
|
*/ |
||||||
|
@NotNull |
||||||
|
AffineTransform getTransform(JXLayer<? extends JComponent> layer); |
||||||
|
|
||||||
|
/** |
||||||
|
* Remove a {@link ChangeListener}. |
||||||
|
* |
||||||
|
* @param listener a {@link ChangeListener} |
||||||
|
* @see #addChangeListener(ChangeListener) |
||||||
|
*/ |
||||||
|
void removeChangeListener(ChangeListener listener); |
||||||
|
} |
@ -0,0 +1,50 @@ |
|||||||
|
/* |
||||||
|
Copyright (c) 2009, Piet Blok |
||||||
|
All rights reserved. |
||||||
|
<p> |
||||||
|
Redistribution and use in source and binary forms, with or without |
||||||
|
modification, are permitted provided that the following conditions |
||||||
|
are met: |
||||||
|
<p> |
||||||
|
* Redistributions of source code must retain the above copyright |
||||||
|
notice, this list of conditions and the following disclaimer. |
||||||
|
* Redistributions in binary form must reproduce the above |
||||||
|
copyright notice, this list of conditions and the following |
||||||
|
disclaimer in the documentation and/or other materials provided |
||||||
|
with the distribution. |
||||||
|
* Neither the name of the copyright holder nor the names of the |
||||||
|
contributors may be used to endorse or promote products derived |
||||||
|
from this software without specific prior written permission. |
||||||
|
<p> |
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.pbjar.jxlayer.plaf.ext.transform; |
||||||
|
|
||||||
|
import org.pbjar.jxlayer.plaf.ext.TransformUI; |
||||||
|
|
||||||
|
import javax.swing.*; |
||||||
|
import java.lang.annotation.ElementType; |
||||||
|
import java.lang.annotation.Retention; |
||||||
|
import java.lang.annotation.RetentionPolicy; |
||||||
|
import java.lang.annotation.Target; |
||||||
|
|
||||||
|
/** |
||||||
|
* A marker for an adequate {@link RepaintManager} for the {@link TransformUI}. |
||||||
|
* |
||||||
|
* @author Piet Blok |
||||||
|
*/ |
||||||
|
@Retention(RetentionPolicy.RUNTIME) |
||||||
|
@Target(ElementType.TYPE) |
||||||
|
public @interface TransformRPMAnnotation { |
||||||
|
} |
@ -0,0 +1,76 @@ |
|||||||
|
/* |
||||||
|
Copyright (c) 2008-2009, Piet Blok |
||||||
|
All rights reserved. |
||||||
|
<p> |
||||||
|
Redistribution and use in source and binary forms, with or without |
||||||
|
modification, are permitted provided that the following conditions |
||||||
|
are met: |
||||||
|
<p> |
||||||
|
* Redistributions of source code must retain the above copyright |
||||||
|
notice, this list of conditions and the following disclaimer. |
||||||
|
* Redistributions in binary form must reproduce the above |
||||||
|
copyright notice, this list of conditions and the following |
||||||
|
disclaimer in the documentation and/or other materials provided |
||||||
|
with the distribution. |
||||||
|
* Neither the name of the copyright holder nor the names of the |
||||||
|
contributors may be used to endorse or promote products derived |
||||||
|
from this software without specific prior written permission. |
||||||
|
<p> |
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.pbjar.jxlayer.plaf.ext.transform; |
||||||
|
|
||||||
|
import org.jdesktop.swingx.ForwardingRepaintManager; |
||||||
|
import org.jetbrains.annotations.NotNull; |
||||||
|
import org.pbjar.jxlayer.repaint.RepaintManagerProvider; |
||||||
|
import org.pbjar.jxlayer.repaint.RepaintManagerUtils; |
||||||
|
import org.pbjar.jxlayer.repaint.WrappedRepaintManager; |
||||||
|
|
||||||
|
import javax.swing.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* A specialized {@link RepaintManager} that checks for every JComponent that is being set dirty, if |
||||||
|
* it has a JXLayer ancestor, equipped with a TransformUI. In that case, the transformed region on |
||||||
|
* the JXLayer is also marked dirty. |
||||||
|
* |
||||||
|
* <p>A fall back class if the {@link ForwardingRepaintManager} cannot be instantiated because the |
||||||
|
* SwingX packages are not on the class path. |
||||||
|
* |
||||||
|
* @see RepaintManagerProvider |
||||||
|
* @see RepaintManagerUtils |
||||||
|
* @see TransformRPMSwingX |
||||||
|
*/ |
||||||
|
@TransformRPMAnnotation |
||||||
|
public class TransformRPMFallBack extends WrappedRepaintManager { |
||||||
|
|
||||||
|
/** |
||||||
|
* Sole constructor. |
||||||
|
* |
||||||
|
* @param delegate the delegate {@link RepaintManager} |
||||||
|
*/ |
||||||
|
public TransformRPMFallBack(final RepaintManager delegate) { |
||||||
|
super(delegate); |
||||||
|
TransformRPMImpl.hackInitialization(delegate, this); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Delegates and then marks a JXLayer ancestor as dirty with the transformed rectangle. |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public void addDirtyRegion(@NotNull final JComponent c, final int x, final int y, final int w, final int h) { |
||||||
|
if (!TransformRPMImpl.addDirtyRegion(c, x, y, w, h, this)) { |
||||||
|
super.addDirtyRegion(c, x, y, w, h); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,197 @@ |
|||||||
|
/* |
||||||
|
Copyright (c) 2009, Piet Blok |
||||||
|
All rights reserved. |
||||||
|
<p> |
||||||
|
Redistribution and use in source and binary forms, with or without |
||||||
|
modification, are permitted provided that the following conditions |
||||||
|
are met: |
||||||
|
<p> |
||||||
|
* Redistributions of source code must retain the above copyright |
||||||
|
notice, this list of conditions and the following disclaimer. |
||||||
|
* Redistributions in binary form must reproduce the above |
||||||
|
copyright notice, this list of conditions and the following |
||||||
|
disclaimer in the documentation and/or other materials provided |
||||||
|
with the distribution. |
||||||
|
* Neither the name of the copyright holder nor the names of the |
||||||
|
contributors may be used to endorse or promote products derived |
||||||
|
from this software without specific prior written permission. |
||||||
|
<p> |
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.pbjar.jxlayer.plaf.ext.transform; |
||||||
|
|
||||||
|
import com.weis.darklaf.LogFormatter; |
||||||
|
import org.jdesktop.jxlayer.JXLayer; |
||||||
|
import org.jdesktop.jxlayer.plaf.LayerUI; |
||||||
|
import org.jetbrains.annotations.NotNull; |
||||||
|
import org.jetbrains.annotations.Nullable; |
||||||
|
import org.pbjar.jxlayer.plaf.ext.TransformUI; |
||||||
|
|
||||||
|
import javax.swing.*; |
||||||
|
import java.awt.*; |
||||||
|
import java.lang.reflect.Field; |
||||||
|
import java.lang.reflect.Method; |
||||||
|
import java.util.logging.ConsoleHandler; |
||||||
|
import java.util.logging.Logger; |
||||||
|
|
||||||
|
/** |
||||||
|
* To avoid duplicate code, this class implements the actual logic for {@link TransformRPMSwingX} |
||||||
|
* and {@link TransformRPMFallBack}. |
||||||
|
* |
||||||
|
* @author Piet Blok |
||||||
|
*/ |
||||||
|
public final class TransformRPMImpl { |
||||||
|
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(TransformRPMImpl.class.getName()); |
||||||
|
/** |
||||||
|
* A flag, indicating whether or not a very dirty initialization on created {@link |
||||||
|
* RepaintManager}s must be performed. |
||||||
|
* |
||||||
|
* @see #hackInitialization(RepaintManager, RepaintManager) |
||||||
|
*/ |
||||||
|
public static boolean hack = false; |
||||||
|
|
||||||
|
static { |
||||||
|
LOGGER.setUseParentHandlers(false); |
||||||
|
ConsoleHandler handler = new ConsoleHandler(); |
||||||
|
handler.setFormatter(new LogFormatter()); |
||||||
|
LOGGER.addHandler(handler); |
||||||
|
} |
||||||
|
|
||||||
|
private TransformRPMImpl() { |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Searches upwards in the component hierarchy for a {@link JXLayer} ancestor with an enabled |
||||||
|
* {@link TransformUI}. |
||||||
|
* <p> |
||||||
|
* If found, the dirty rectangle is transformed to a rectangle targeted at that {@link JXLayer} |
||||||
|
* and the argument manager's {@link RepaintManager#addDirtyRegion(JComponent, int, int, int, |
||||||
|
* int)} is invoked. {@code true} is returned. |
||||||
|
* </p> |
||||||
|
* <p> |
||||||
|
* Else, (@code false} is returned. |
||||||
|
* </p> |
||||||
|
* |
||||||
|
* @param aComponent a component |
||||||
|
* @param x the X of the dirty region |
||||||
|
* @param y the Y of the dirty region |
||||||
|
* @param w the width of the dirty region |
||||||
|
* @param h the height of the dirty region |
||||||
|
* @param manager the current {@link RepaintManager} |
||||||
|
* @return {@code true} if the call is delegated to the manager with a transformed rectangle, |
||||||
|
* {@code false} otherwise |
||||||
|
*/ |
||||||
|
@SuppressWarnings("unchecked") |
||||||
|
public static boolean addDirtyRegion(@NotNull final JComponent aComponent, final int x, final int y, |
||||||
|
final int w, final int h, @NotNull final RepaintManager manager) { |
||||||
|
if (aComponent.isShowing()) { |
||||||
|
JXLayer<?> layer = findJXLayer(aComponent); |
||||||
|
if (layer != null) { |
||||||
|
LayerUI<?> layerUI = layer.getUI(); |
||||||
|
TransformUI ui = (TransformUI) layerUI; |
||||||
|
Point point = aComponent.getLocationOnScreen(); |
||||||
|
SwingUtilities.convertPointFromScreen(point, layer); |
||||||
|
Rectangle transformPortRegion = ui.transform(new Rectangle(x + point.x, y + point.y, w, h), |
||||||
|
(JXLayer<JComponent>) layer); |
||||||
|
manager.addDirtyRegion(layer, |
||||||
|
transformPortRegion.x, transformPortRegion.y, |
||||||
|
transformPortRegion.width, transformPortRegion.height); |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* If {@link #hack} is {@code true}, the private fields {@code paintManager} and {@code |
||||||
|
* bufferStrategyType} are copied via reflection from the source manager into the destination |
||||||
|
* manager. |
||||||
|
* |
||||||
|
* @param sourceManager the source manager |
||||||
|
* @param destinationManager the destination manager |
||||||
|
*/ |
||||||
|
public static void hackInitialization(final RepaintManager sourceManager, |
||||||
|
final RepaintManager destinationManager) { |
||||||
|
if (hack) { |
||||||
|
Class<RepaintManager> rpmClass = RepaintManager.class; |
||||||
|
try { |
||||||
|
|
||||||
|
Field fieldBufferStrategyType = rpmClass.getDeclaredField("bufferStrategyType"); |
||||||
|
Field fieldPaintManager = rpmClass.getDeclaredField("paintManager"); |
||||||
|
Method methodGetPaintManager = rpmClass.getDeclaredMethod("getPaintManager"); |
||||||
|
|
||||||
|
fieldBufferStrategyType.setAccessible(true); |
||||||
|
fieldPaintManager.setAccessible(true); |
||||||
|
methodGetPaintManager.setAccessible(true); |
||||||
|
|
||||||
|
Object paintManager = methodGetPaintManager.invoke(sourceManager); |
||||||
|
short bufferStrategyType = (Short) fieldBufferStrategyType.get(sourceManager); |
||||||
|
|
||||||
|
fieldBufferStrategyType.set(destinationManager, bufferStrategyType); |
||||||
|
fieldPaintManager.set(destinationManager, paintManager); |
||||||
|
|
||||||
|
fieldBufferStrategyType.setAccessible(false); |
||||||
|
fieldPaintManager.setAccessible(false); |
||||||
|
methodGetPaintManager.setAccessible(false); |
||||||
|
|
||||||
|
LOGGER.warning("Copied paintManager of type: " + paintManager.getClass().getName()); |
||||||
|
switch (bufferStrategyType) { |
||||||
|
case (0): |
||||||
|
LOGGER.warning("Copied bufferStrategyType " |
||||||
|
+ bufferStrategyType |
||||||
|
+ ": BUFFER_STRATEGY_NOT_SPECIFIED"); |
||||||
|
break; |
||||||
|
case (1): |
||||||
|
LOGGER.warning("Copied bufferStrategyType " |
||||||
|
+ bufferStrategyType |
||||||
|
+ ": BUFFER_STRATEGY_SPECIFIED_ON"); |
||||||
|
break; |
||||||
|
case (2): |
||||||
|
LOGGER.warning("Copied bufferStrategyType " |
||||||
|
+ bufferStrategyType |
||||||
|
+ ": BUFFER_STRATEGY_SPECIFIED_OFF"); |
||||||
|
break; |
||||||
|
default: |
||||||
|
LOGGER.warning("Copied bufferStrategyType " |
||||||
|
+ bufferStrategyType + ": ???"); |
||||||
|
break; |
||||||
|
} |
||||||
|
} catch (Throwable t) { |
||||||
|
t.printStackTrace(System.out); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Find the first ancestor {@link JXLayer} with an enabled {@link TransformUI}. |
||||||
|
* |
||||||
|
* @param aComponent some component |
||||||
|
* @return a {@link JXLayer} instance or {@code null} |
||||||
|
*/ |
||||||
|
@Nullable |
||||||
|
private static JXLayer<?> findJXLayer(final JComponent aComponent) { |
||||||
|
|
||||||
|
JXLayer<?> layer = (JXLayer<?>) SwingUtilities.getAncestorOfClass( |
||||||
|
JXLayer.class, aComponent); |
||||||
|
if (layer != null) { |
||||||
|
LayerUI<?> ui = ((JXLayer<?>) layer).getUI(); |
||||||
|
if (ui instanceof TransformUI) { |
||||||
|
return layer; |
||||||
|
} |
||||||
|
return findJXLayer(layer); |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,76 @@ |
|||||||
|
/* |
||||||
|
Copyright (c) 2009, Piet Blok |
||||||
|
All rights reserved. |
||||||
|
<p> |
||||||
|
Redistribution and use in source and binary forms, with or without |
||||||
|
modification, are permitted provided that the following conditions |
||||||
|
are met: |
||||||
|
<p> |
||||||
|
* Redistributions of source code must retain the above copyright |
||||||
|
notice, this list of conditions and the following disclaimer. |
||||||
|
* Redistributions in binary form must reproduce the above |
||||||
|
copyright notice, this list of conditions and the following |
||||||
|
disclaimer in the documentation and/or other materials provided |
||||||
|
with the distribution. |
||||||
|
* Neither the name of the copyright holder nor the names of the |
||||||
|
contributors may be used to endorse or promote products derived |
||||||
|
from this software without specific prior written permission. |
||||||
|
<p> |
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.pbjar.jxlayer.plaf.ext.transform; |
||||||
|
|
||||||
|
import org.jdesktop.swingx.ForwardingRepaintManager; |
||||||
|
import org.jetbrains.annotations.NotNull; |
||||||
|
import org.pbjar.jxlayer.repaint.RepaintManagerProvider; |
||||||
|
import org.pbjar.jxlayer.repaint.RepaintManagerUtils; |
||||||
|
|
||||||
|
import javax.swing.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* A specialized {@link RepaintManager} that checks for every JComponent that is being set dirty, if |
||||||
|
* it has a JXLayer ancestor, equipped with a TransformUI. In that case, the transformed region on |
||||||
|
* the JXLayer is also marked dirty. |
||||||
|
* |
||||||
|
* <p>If this class cannot be instantiated because the SwingX packages are not on the class path, |
||||||
|
* use {@link TransformRPMFallBack} |
||||||
|
* |
||||||
|
* @author Piet Blok |
||||||
|
* @see TransformRPMFallBack |
||||||
|
* @see RepaintManagerProvider |
||||||
|
* @see RepaintManagerUtils |
||||||
|
*/ |
||||||
|
@TransformRPMAnnotation |
||||||
|
public class TransformRPMSwingX extends ForwardingRepaintManager { |
||||||
|
|
||||||
|
/** |
||||||
|
* Sole constructor. |
||||||
|
* |
||||||
|
* @param delegate the delegate {@link RepaintManager} |
||||||
|
*/ |
||||||
|
public TransformRPMSwingX(@NotNull final RepaintManager delegate) { |
||||||
|
super(delegate); |
||||||
|
TransformRPMImpl.hackInitialization(delegate, this); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Delegates and then marks a JXLayer ancestor as dirty with the transformed rectangle. |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public void addDirtyRegion(@NotNull final JComponent c, final int x, final int y, final int w, final int h) { |
||||||
|
if (!TransformRPMImpl.addDirtyRegion(c, x, y, w, h, this)) { |
||||||
|
super.addDirtyRegion(c, x, y, w, h); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,122 @@ |
|||||||
|
/* |
||||||
|
Copyright (c) 2009, Piet Blok |
||||||
|
All rights reserved. |
||||||
|
<p> |
||||||
|
Redistribution and use in source and binary forms, with or without |
||||||
|
modification, are permitted provided that the following conditions |
||||||
|
are met: |
||||||
|
<p> |
||||||
|
* Redistributions of source code must retain the above copyright |
||||||
|
notice, this list of conditions and the following disclaimer. |
||||||
|
* Redistributions in binary form must reproduce the above |
||||||
|
copyright notice, this list of conditions and the following |
||||||
|
disclaimer in the documentation and/or other materials provided |
||||||
|
with the distribution. |
||||||
|
* Neither the name of the copyright holder nor the names of the |
||||||
|
contributors may be used to endorse or promote products derived |
||||||
|
from this software without specific prior written permission. |
||||||
|
<p> |
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.pbjar.jxlayer.plaf.ext.transform; |
||||||
|
|
||||||
|
import org.jdesktop.jxlayer.JXLayer; |
||||||
|
import org.jetbrains.annotations.Contract; |
||||||
|
import org.jetbrains.annotations.NotNull; |
||||||
|
import org.pbjar.jxlayer.plaf.ext.TransformUI; |
||||||
|
|
||||||
|
import javax.swing.*; |
||||||
|
import java.awt.*; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
/** |
||||||
|
* Some convenience methods to create a populated transforming {@link JXLayer}. |
||||||
|
* |
||||||
|
* @author Piet Blok |
||||||
|
*/ |
||||||
|
public final class TransformUtils { |
||||||
|
|
||||||
|
@Contract(pure = true) |
||||||
|
private TransformUtils() { |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Create a Transform JXLayer. |
||||||
|
* |
||||||
|
* @param component the component. |
||||||
|
* @return the JXLayer. |
||||||
|
*/ |
||||||
|
@NotNull |
||||||
|
public static JXLayer<JComponent> createTransformJXLayer(final JComponent component) { |
||||||
|
return createTransformJXLayer(component, 1.0, null); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Create a Transform JXLayer. |
||||||
|
* |
||||||
|
* @param component the component. |
||||||
|
* @param scale the scaling |
||||||
|
* @param hints the rendering hints. |
||||||
|
* @return the JXLayer. |
||||||
|
*/ |
||||||
|
@NotNull |
||||||
|
public static JXLayer<JComponent> createTransformJXLayer( |
||||||
|
final JComponent component, final double scale, final Map<RenderingHints.Key, Object> hints) { |
||||||
|
DefaultTransformModel model = new DefaultTransformModel(); |
||||||
|
model.setScale(scale); |
||||||
|
return createTransformJXLayer(component, model, hints); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Create a Transform JXLayer. |
||||||
|
* |
||||||
|
* @param component the component. |
||||||
|
* @param model the transform model. |
||||||
|
* @param hints the rendering hints. |
||||||
|
* @return the JXLayer. |
||||||
|
*/ |
||||||
|
@Contract("_, _, _ -> new") |
||||||
|
@NotNull |
||||||
|
public static JXLayer<JComponent> createTransformJXLayer( |
||||||
|
final JComponent component, final TransformModel model, final Map<RenderingHints.Key, Object> hints) { |
||||||
|
TransformUI ui = new TransformUI(model); |
||||||
|
ui.setRenderingHints(hints); |
||||||
|
return new JXLayer<>(component, ui); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Create a Transform JXLayer. |
||||||
|
* |
||||||
|
* @param component the component. |
||||||
|
* @param scale the scaling |
||||||
|
* @return the JXLayer. |
||||||
|
*/ |
||||||
|
@NotNull |
||||||
|
public static JXLayer<JComponent> createTransformJXLayer(final JComponent component, final double scale) { |
||||||
|
return createTransformJXLayer(component, scale, null); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Create a Transform JXLayer. |
||||||
|
* |
||||||
|
* @param component the component. |
||||||
|
* @param model the transform model. |
||||||
|
* @return the JXLayer. |
||||||
|
*/ |
||||||
|
@NotNull |
||||||
|
public static JXLayer<JComponent> createTransformJXLayer( |
||||||
|
final JComponent component, final TransformModel model) { |
||||||
|
return createTransformJXLayer(component, model, null); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,76 @@ |
|||||||
|
/* |
||||||
|
Copyright (c) 2009, Piet Blok |
||||||
|
All rights reserved. |
||||||
|
<p> |
||||||
|
Redistribution and use in source and binary forms, with or without |
||||||
|
modification, are permitted provided that the following conditions |
||||||
|
are met: |
||||||
|
<p> |
||||||
|
* Redistributions of source code must retain the above copyright |
||||||
|
notice, this list of conditions and the following disclaimer. |
||||||
|
* Redistributions in binary form must reproduce the above |
||||||
|
copyright notice, this list of conditions and the following |
||||||
|
disclaimer in the documentation and/or other materials provided |
||||||
|
with the distribution. |
||||||
|
* Neither the name of the copyright holder nor the names of the |
||||||
|
contributors may be used to endorse or promote products derived |
||||||
|
from this software without specific prior written permission. |
||||||
|
<p> |
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.pbjar.jxlayer.repaint; |
||||||
|
|
||||||
|
import org.jdesktop.swingx.ForwardingRepaintManager; |
||||||
|
import org.jetbrains.annotations.NotNull; |
||||||
|
|
||||||
|
import javax.swing.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* To be implemented by classes that provide for a custom RepaintManager. |
||||||
|
* |
||||||
|
* @author Piet Blok |
||||||
|
* @see RepaintManagerUtils |
||||||
|
*/ |
||||||
|
public interface RepaintManagerProvider { |
||||||
|
/** |
||||||
|
* Get the class of a {@link RepaintManager} that extends {@link ForwardingRepaintManager}. |
||||||
|
* |
||||||
|
* <p><b>Note:</b> the class must provide for a public constructor that takes a delegate {@link |
||||||
|
* RepaintManager} as its only argument. |
||||||
|
* |
||||||
|
* @return a class object |
||||||
|
*/ |
||||||
|
@NotNull |
||||||
|
Class<? extends ForwardingRepaintManager> getForwardingRepaintManagerClass(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the class of a {@link RepaintManager} that extends {@link WrappedRepaintManager}. |
||||||
|
* |
||||||
|
* <p><b>Note:</b> the class must provide for a public constructor that takes a delegate {@link |
||||||
|
* RepaintManager} as its only argument. |
||||||
|
* |
||||||
|
* @return a class object |
||||||
|
*/ |
||||||
|
@NotNull |
||||||
|
Class<? extends WrappedRepaintManager> getWrappedRepaintManagerClass(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Checks whether or not the argument class is a {@link RepaintManager} class that will do the |
||||||
|
* required job. |
||||||
|
* |
||||||
|
* @param rpm a {@link RepaintManager} class
|
||||||
|
* @return {@code true} if the argument class will do the required job, {@code false} otherwise |
||||||
|
*/ |
||||||
|
boolean isAdequate(Class<? extends RepaintManager> rpm); |
||||||
|
} |
@ -0,0 +1,230 @@ |
|||||||
|
/* |
||||||
|
Copyright (c) 2009, Piet Blok |
||||||
|
All rights reserved. |
||||||
|
<p> |
||||||
|
Redistribution and use in source and binary forms, with or without |
||||||
|
modification, are permitted provided that the following conditions |
||||||
|
are met: |
||||||
|
<p> |
||||||
|
* Redistributions of source code must retain the above copyright |
||||||
|
notice, this list of conditions and the following disclaimer. |
||||||
|
* Redistributions in binary form must reproduce the above |
||||||
|
copyright notice, this list of conditions and the following |
||||||
|
disclaimer in the documentation and/or other materials provided |
||||||
|
with the distribution. |
||||||
|
* Neither the name of the copyright holder nor the names of the |
||||||
|
contributors may be used to endorse or promote products derived |
||||||
|
from this software without specific prior written permission. |
||||||
|
<p> |
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.pbjar.jxlayer.repaint; |
||||||
|
|
||||||
|
import com.weis.darklaf.LogFormatter; |
||||||
|
import org.jdesktop.swingx.ForwardingRepaintManager; |
||||||
|
import org.jetbrains.annotations.Contract; |
||||||
|
import org.jetbrains.annotations.NotNull; |
||||||
|
import org.jetbrains.annotations.Nullable; |
||||||
|
|
||||||
|
import javax.swing.*; |
||||||
|
import java.awt.*; |
||||||
|
import java.awt.event.ActionEvent; |
||||||
|
import java.io.PrintWriter; |
||||||
|
import java.io.StringWriter; |
||||||
|
import java.util.logging.ConsoleHandler; |
||||||
|
import java.util.logging.Logger; |
||||||
|
|
||||||
|
/** |
||||||
|
* Utility class that ensures that a correct {@link RepaintManager} is set. |
||||||
|
* |
||||||
|
* @author Piet Blok |
||||||
|
*/ |
||||||
|
public final class RepaintManagerUtils { |
||||||
|
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(RepaintManagerUtils.class.getName()); |
||||||
|
/** |
||||||
|
* Indicates the availability of SwingX on the class path. |
||||||
|
*/ |
||||||
|
private static final boolean swingX = isSwingXAvailable(); |
||||||
|
|
||||||
|
static { |
||||||
|
LOGGER.setUseParentHandlers(false); |
||||||
|
ConsoleHandler handler = new ConsoleHandler(); |
||||||
|
handler.setFormatter(new LogFormatter()); |
||||||
|
LOGGER.addHandler(handler); |
||||||
|
} |
||||||
|
|
||||||
|
@Contract(pure = true) |
||||||
|
private RepaintManagerUtils() { |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Create and return an {@link Action} that will display the delegate structure of the current |
||||||
|
* {@link RepaintManager}. |
||||||
|
* |
||||||
|
* @return an {@link Action} object |
||||||
|
*/ |
||||||
|
@Contract(" -> new") |
||||||
|
@NotNull |
||||||
|
public static Action createRPDisplayAction() { |
||||||
|
return new DisplayAction(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Ensure that a specific {@link RepaintManager} is set according to the requirements of the |
||||||
|
* {@link RepaintManagerProvider}. |
||||||
|
* |
||||||
|
* @param c a component from which the current repaint manager can be obtained. |
||||||
|
* @param provider the provider |
||||||
|
*/ |
||||||
|
public static void ensureRepaintManagerSet( |
||||||
|
final Component c, @NotNull final RepaintManagerProvider provider) { |
||||||
|
ensureImpl(RepaintManager.currentManager(c), provider); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* The actual implementation of ensure. |
||||||
|
* |
||||||
|
* @param delegate a delegate RepaintManager |
||||||
|
* @param provider the provider that provides for the type and implementation of a delegated |
||||||
|
* RepaintManager |
||||||
|
*/ |
||||||
|
private static void ensureImpl( |
||||||
|
@NotNull final RepaintManager delegate, @NotNull final RepaintManagerProvider provider) { |
||||||
|
/* |
||||||
|
* Setup a traversal variable. |
||||||
|
*/ |
||||||
|
RepaintManager manager = delegate; |
||||||
|
|
||||||
|
while (!provider.isAdequate(manager.getClass())) { |
||||||
|
if (swingX) { |
||||||
|
if (manager instanceof ForwardingRepaintManager) { |
||||||
|
manager = ((ForwardingRepaintManager) manager).getDelegateManager(); |
||||||
|
} else { |
||||||
|
RepaintManager.setCurrentManager( |
||||||
|
createManager(provider.getForwardingRepaintManagerClass(), delegate)); |
||||||
|
break; |
||||||
|
} |
||||||
|
} else { |
||||||
|
if (manager instanceof WrappedRepaintManager) { |
||||||
|
manager = ((WrappedRepaintManager) manager).getDelegateManager(); |
||||||
|
} else { |
||||||
|
RepaintManager.setCurrentManager( |
||||||
|
createManager(provider.getWrappedRepaintManagerClass(), delegate)); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@NotNull |
||||||
|
private static RepaintManager createManager( |
||||||
|
@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); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Ensure that a specific {@link RepaintManager} is set according to the requirements of the |
||||||
|
* {@link RepaintManagerProvider}. |
||||||
|
* |
||||||
|
* @param c a component from which the current repaint manager can be obtained. |
||||||
|
* @param provider the provider |
||||||
|
*/ |
||||||
|
public static void ensureRepaintManagerSet( |
||||||
|
final JComponent c, @NotNull final RepaintManagerProvider provider) { |
||||||
|
ensureImpl(RepaintManager.currentManager(c), provider); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Detect the availability of the ForwardingRepaintManager class. |
||||||
|
* |
||||||
|
* @return {@code} true if available, {@code false} otherwise |
||||||
|
*/ |
||||||
|
private static boolean isSwingXAvailable() { |
||||||
|
try { |
||||||
|
Class<?> clazz = ForwardingRepaintManager.class; |
||||||
|
LOGGER.info("SwingX is available"); |
||||||
|
return clazz != null; |
||||||
|
} catch (Throwable t) { |
||||||
|
LOGGER.info("SwingX is not available"); |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static class DisplayAction extends AbstractAction { |
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L; |
||||||
|
|
||||||
|
public DisplayAction() { |
||||||
|
super("RPM tree"); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void actionPerformed(@NotNull final ActionEvent e) { |
||||||
|
JComponent c = (JComponent) e.getSource(); |
||||||
|
StringWriter sw = new StringWriter(); |
||||||
|
PrintWriter pw = new PrintWriter(sw); |
||||||
|
pw.println("The tree for the current RepaintManager:"); |
||||||
|
pw.println(); |
||||||
|
RepaintManager manager = RepaintManager.currentManager(c); |
||||||
|
appendDelegates(pw, manager); |
||||||
|
pw.close(); |
||||||
|
String text = sw.toString(); |
||||||
|
JTextPane message = new JTextPane(); |
||||||
|
message.setFont(Font.decode(Font.MONOSPACED)); |
||||||
|
message.setContentType("text/plain"); |
||||||
|
message.setText(text); |
||||||
|
message.setEditable(false); |
||||||
|
JOptionPane.showMessageDialog( |
||||||
|
c, message, "The RepaintManager tree", JOptionPane.INFORMATION_MESSAGE); |
||||||
|
} |
||||||
|
|
||||||
|
private void appendClass(@NotNull final PrintWriter writer, @NotNull final Object obj) { |
||||||
|
Class<?> clazz = obj.getClass(); |
||||||
|
String prefix = "Class: "; |
||||||
|
while (clazz != null) { |
||||||
|
writer.println(prefix + clazz.getName()); |
||||||
|
clazz = clazz.getSuperclass(); |
||||||
|
prefix = "Extends: "; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void appendDelegates(@NotNull final PrintWriter writer, @NotNull final Object rp) { |
||||||
|
appendClass(writer, rp); |
||||||
|
@Nullable RepaintManager delegate; |
||||||
|
if (rp instanceof WrappedRepaintManager) { |
||||||
|
delegate = ((WrappedRepaintManager) rp).getDelegateManager(); |
||||||
|
} else if (swingX) { |
||||||
|
if (rp instanceof ForwardingRepaintManager) { |
||||||
|
delegate = ((ForwardingRepaintManager) rp).getDelegateManager(); |
||||||
|
} else { |
||||||
|
delegate = null; |
||||||
|
} |
||||||
|
} else { |
||||||
|
delegate = null; |
||||||
|
} |
||||||
|
if (delegate != null) { |
||||||
|
writer.println(); |
||||||
|
writer.println("Delegate:"); |
||||||
|
appendDelegates(writer, delegate); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,228 @@ |
|||||||
|
/* |
||||||
|
Copyright (c) 2009, Piet Blok |
||||||
|
All rights reserved. |
||||||
|
<p> |
||||||
|
Redistribution and use in source and binary forms, with or without |
||||||
|
modification, are permitted provided that the following conditions |
||||||
|
are met: |
||||||
|
<p> |
||||||
|
* Redistributions of source code must retain the above copyright |
||||||
|
notice, this list of conditions and the following disclaimer. |
||||||
|
* Redistributions in binary form must reproduce the above |
||||||
|
copyright notice, this list of conditions and the following |
||||||
|
disclaimer in the documentation and/or other materials provided |
||||||
|
with the distribution. |
||||||
|
* Neither the name of the copyright holder nor the names of the |
||||||
|
contributors may be used to endorse or promote products derived |
||||||
|
from this software without specific prior written permission. |
||||||
|
<p> |
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.pbjar.jxlayer.repaint; |
||||||
|
|
||||||
|
import org.jdesktop.swingx.ForwardingRepaintManager; |
||||||
|
import org.jetbrains.annotations.Contract; |
||||||
|
import org.jetbrains.annotations.NotNull; |
||||||
|
import org.jetbrains.annotations.Nullable; |
||||||
|
|
||||||
|
import javax.swing.*; |
||||||
|
import java.applet.Applet; |
||||||
|
import java.awt.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* A fall back class for when the SwingX class {@link ForwardingRepaintManager} is not available on |
||||||
|
* the class path. |
||||||
|
* |
||||||
|
* <p>A {@link RepaintManager} that preserves functionality of a wrapped {@code RepaintManager}. All |
||||||
|
* methods will delegate to the wrapped {@code RepaintManager}. |
||||||
|
* |
||||||
|
* <p>When sub classing this class, one must in all overridden methods call the {@code super} |
||||||
|
* method. |
||||||
|
* |
||||||
|
* @author Piet Blok |
||||||
|
* @see RepaintManagerUtils |
||||||
|
* @see RepaintManagerProvider |
||||||
|
* @see ForwardingRepaintManager |
||||||
|
*/ |
||||||
|
public class WrappedRepaintManager extends RepaintManager { |
||||||
|
|
||||||
|
/** |
||||||
|
* The wrapped manager. |
||||||
|
*/ |
||||||
|
@NotNull |
||||||
|
private final RepaintManager delegate; |
||||||
|
|
||||||
|
/** |
||||||
|
* Construct a {@code RepaintManager} wrapping an existing {@code RepaintManager}. |
||||||
|
* |
||||||
|
* @param delegate an existing RepaintManager |
||||||
|
*/ |
||||||
|
@Contract("null -> fail") |
||||||
|
public WrappedRepaintManager(@Nullable final RepaintManager delegate) { |
||||||
|
if (delegate == null) { |
||||||
|
throw new NullPointerException(); |
||||||
|
} |
||||||
|
this.delegate = delegate; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Just delegates. {@inheritDoc} |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public void addInvalidComponent(final JComponent invalidComponent) { |
||||||
|
delegate.addInvalidComponent(invalidComponent); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Just delegates. {@inheritDoc} |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public void removeInvalidComponent(final JComponent component) { |
||||||
|
delegate.removeInvalidComponent(component); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Just delegates. {@inheritDoc} |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public void addDirtyRegion(final JComponent c, final int x, final int y, final int w, final int h) { |
||||||
|
delegate.addDirtyRegion(c, x, y, w, h); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Just delegates. {@inheritDoc} |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public void addDirtyRegion(final Window window, final int x, final int y, final int w, final int h) { |
||||||
|
delegate.addDirtyRegion(window, x, y, w, h); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Just delegates. {@inheritDoc} |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
@Deprecated |
||||||
|
@SuppressWarnings("deprecation") |
||||||
|
public void addDirtyRegion(final Applet applet, final int x, final int y, final int w, final int h) { |
||||||
|
delegate.addDirtyRegion(applet, x, y, w, h); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Just delegates. {@inheritDoc} |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public Rectangle getDirtyRegion(final JComponent c) { |
||||||
|
return delegate.getDirtyRegion(c); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Just delegates. {@inheritDoc} |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public Dimension getDoubleBufferMaximumSize() { |
||||||
|
return delegate.getDoubleBufferMaximumSize(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Just delegates. {@inheritDoc} |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public void markCompletelyDirty(final JComponent c) { |
||||||
|
delegate.markCompletelyDirty(c); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Just delegates. {@inheritDoc} |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public void setDoubleBufferMaximumSize(final Dimension d) { |
||||||
|
delegate.setDoubleBufferMaximumSize(d); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Just delegates. {@inheritDoc} |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public void markCompletelyClean(final JComponent c) { |
||||||
|
delegate.markCompletelyClean(c); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Just delegates. {@inheritDoc} |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public boolean isCompletelyDirty(final JComponent c) { |
||||||
|
return delegate.isCompletelyDirty(c); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Just delegates. {@inheritDoc} |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public void validateInvalidComponents() { |
||||||
|
delegate.validateInvalidComponents(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Just delegates. {@inheritDoc} |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public void paintDirtyRegions() { |
||||||
|
delegate.paintDirtyRegions(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Just delegates. {@inheritDoc} |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public boolean isDoubleBufferingEnabled() { |
||||||
|
return delegate.isDoubleBufferingEnabled(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Just delegates. {@inheritDoc} |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public Image getOffscreenBuffer(final Component c, final int proposedWidth, final int proposedHeight) { |
||||||
|
return delegate.getOffscreenBuffer(c, proposedWidth, proposedHeight); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Just delegates. {@inheritDoc} |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public void setDoubleBufferingEnabled(final boolean flag) { |
||||||
|
delegate.setDoubleBufferingEnabled(flag); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Just delegates. {@inheritDoc} |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public Image getVolatileOffscreenBuffer(final Component c, final int proposedWidth, final int proposedHeight) { |
||||||
|
return delegate.getVolatileOffscreenBuffer(c, proposedWidth, proposedHeight); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the delegate. |
||||||
|
* |
||||||
|
* @return the delegate |
||||||
|
*/ |
||||||
|
@Nullable |
||||||
|
public RepaintManager getDelegateManager() { |
||||||
|
return delegate; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
@ -0,0 +1,63 @@ |
|||||||
|
# |
||||||
|
# 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. |
||||||
|
# |
||||||
|
# suppress inspection "UnusedProperty" for whole file |
||||||
|
TabFramePanelPopupUI = com.weis.darklaf.ui.tabframe.DarkPanelPopupUI |
||||||
|
TabFrameTabbedPopupUI = com.weis.darklaf.ui.tabframe.DarkTabbedPopupUI |
||||||
|
TabFramePopup.headerBorder = com.weis.darklaf.ui.tabframe.DarkTabFramePopupHeaderBorder |
||||||
|
|
||||||
|
TabFramePopup.headerBackground = %background |
||||||
|
TabFramePopup.headerFocusBackground = %backgroundColorful |
||||||
|
TabFramePopup.headerHoverBackground = %backgroundHover |
||||||
|
TabFramePopup.headerSelectedBackground = %background |
||||||
|
TabFramePopup.headerSelectedHoverBackground = %backgroundHover |
||||||
|
TabFramePopup.headerFocusHoverBackground = %backgroundHoverColorful |
||||||
|
TabFramePopup.headerFocusSelectedBackground = %backgroundColorful |
||||||
|
TabFramePopup.headerFocusSelectedHoverBackground = %backgroundHoverColorful |
||||||
|
TabFramePopup.headerButtonHoverBackground = %hoverHighlight |
||||||
|
TabFramePopup.headerButtonClickBackground = %clickHighlight |
||||||
|
TabFramePopup.headerButtonFocusHoverBackground = %hoverHighlightColorful |
||||||
|
TabFramePopup.headerButtonFocusClickBackground = %clickHighlightColorful |
||||||
|
|
||||||
|
TabFramePopup.closeAccelerator = shift pressed ESCAPE |
||||||
|
TabFramePopup.closeTooltipText = Close (shift ESC) |
||||||
|
|
||||||
|
TabFramePopup.minimumHeaderSize = null |
||||||
|
|
||||||
|
TabFramePopup.borderColor = %borderSecondary |
||||||
|
|
||||||
|
TabFrameTabLabelUI = com.weis.darklaf.ui.tabframe.DarkTabFrameTabLabelUI |
||||||
|
TabFrameTabContainerUI = com.weis.darklaf.ui.tabframe.DarkTabFrameTabContainerUI |
||||||
|
TabFrameTab.border = com.weis.darklaf.ui.tabframe.DarkTabFrameTabBorder |
||||||
|
TabFrameTab.font = Dialog-0-11 |
||||||
|
TabFrameTab.foreground = %textForeground |
||||||
|
TabFrameTab.selectedForeground = %textForegroundHighlight |
||||||
|
TabFrameTab.selectedBackground = %backgroundSelectedSecondary |
||||||
|
TabFrameTab.hoverBackground = %backgroundHoverSecondary |
||||||
|
|
||||||
|
TabFrameUI = com.weis.darklaf.ui.tabframe.DarkTabFrameUI |
||||||
|
TabFrame.line = %borderSecondary |
||||||
|
TabFrame.tabHeight = 24 |
||||||
|
TabFrame.acceleratorKeyCode = alt pressed |
||||||
|
#Icons |
||||||
|
TabFramePopup.close.icon = navigation/collapse.svg[aware] |
@ -0,0 +1,86 @@ |
|||||||
|
import com.weis.darklaf.LafManager; |
||||||
|
import com.weis.darklaf.components.alignment.Alignment; |
||||||
|
import com.weis.darklaf.components.tabframe.TabFrame; |
||||||
|
import com.weis.darklaf.components.tabframe.TabbedPopup; |
||||||
|
import com.weis.darklaf.icons.IconLoader; |
||||||
|
|
||||||
|
import javax.swing.*; |
||||||
|
import javax.swing.border.EmptyBorder; |
||||||
|
import java.awt.*; |
||||||
|
|
||||||
|
/* |
||||||
|
* 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. |
||||||
|
*/ |
||||||
|
public class TabFrameDemo { |
||||||
|
|
||||||
|
public static void main(final String[] args) { |
||||||
|
SwingUtilities.invokeLater(() -> { |
||||||
|
LafManager.install(); |
||||||
|
|
||||||
|
final JFrame frame = new JFrame(); |
||||||
|
Icon folderIcon = IconLoader.get().getUIAwareIcon("files/folder.svg", 19, 19); |
||||||
|
|
||||||
|
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); |
||||||
|
|
||||||
|
var tabFrame = new TabFrame(); |
||||||
|
for (var o : Alignment.values()) { |
||||||
|
if (o != Alignment.CENTER) { |
||||||
|
for (int i = 0; i < 2; i++) { |
||||||
|
var pcc = new JPanel(); |
||||||
|
pcc.setOpaque(true); |
||||||
|
pcc.setBackground(Color.YELLOW.darker().darker()); |
||||||
|
pcc.add(new JLabel(o.toString() + "_" + i + " Popup")); |
||||||
|
tabFrame.addTab(pcc, o.toString() + "_" + i, folderIcon, o); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
var tabbedPopup = new TabbedPopup("Tabbed Popup:"); |
||||||
|
tabFrame.setTabAt(tabbedPopup, "NORTH (Tabbed Pane Tab)", null, Alignment.NORTH, 0); |
||||||
|
for (int i = 0; i < 5; i++) { |
||||||
|
var panel = new JPanel(); |
||||||
|
var label = new JLabel("inside tab " + i); |
||||||
|
panel.add(label); |
||||||
|
panel.setBackground(Color.GREEN.darker().darker()); |
||||||
|
tabbedPopup.getTabbedPane().addTab("Tab " + i, panel); |
||||||
|
} |
||||||
|
|
||||||
|
tabFrame.setUserTabComponentAt(new JLabel("NORTH (custom tab)") {{ |
||||||
|
setBorder(new EmptyBorder(0, 5, 0, 5)); |
||||||
|
setOpaque(false); |
||||||
|
setForeground(Color.RED); |
||||||
|
setFont(new Font(Font.SERIF, Font.ITALIC, 12)); |
||||||
|
}}, Alignment.NORTH, 1); |
||||||
|
|
||||||
|
tabFrame.setAcceleratorAt(1, Alignment.NORTH_WEST, 0); |
||||||
|
|
||||||
|
var contentPane = new JPanel(new BorderLayout()); |
||||||
|
frame.setContentPane(tabFrame); |
||||||
|
tabFrame.setContent(contentPane); |
||||||
|
|
||||||
|
frame.pack(); |
||||||
|
frame.setSize(1000, 500); |
||||||
|
frame.setLocationRelativeTo(null); |
||||||
|
frame.setVisible(true); |
||||||
|
}); |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue