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