diff --git a/src/main/java/com/github/weisj/darklaf/components/OverlayScrollPane.java b/src/main/java/com/github/weisj/darklaf/components/OverlayScrollPane.java index 92ac0542..dded9056 100644 --- a/src/main/java/com/github/weisj/darklaf/components/OverlayScrollPane.java +++ b/src/main/java/com/github/weisj/darklaf/components/OverlayScrollPane.java @@ -301,6 +301,7 @@ public class OverlayScrollPane extends JLayeredPane { && scrollPane.verticalScrollBar.getBounds().contains(x, y); } + @Contract(pure = true) @Override public boolean isOpaque() { return false; diff --git a/src/main/java/com/github/weisj/darklaf/components/tabframe/ToggleSplitPane.java b/src/main/java/com/github/weisj/darklaf/components/tabframe/ToggleSplitPane.java index 0e5653e4..cade8460 100644 --- a/src/main/java/com/github/weisj/darklaf/components/tabframe/ToggleSplitPane.java +++ b/src/main/java/com/github/weisj/darklaf/components/tabframe/ToggleSplitPane.java @@ -24,6 +24,7 @@ package com.github.weisj.darklaf.components.tabframe; import javax.swing.*; +import javax.swing.plaf.basic.BasicSplitPaneDivider; import javax.swing.plaf.basic.BasicSplitPaneUI; public class ToggleSplitPane extends JSplitPane { @@ -61,11 +62,19 @@ public class ToggleSplitPane extends JSplitPane { setEnabled(false); disabledPos = super.getDividerLocation(); disabledMax = getMaximumDividerLocation(); + getDivider().setEnabled(false); + setComponentZOrder(getDivider(), getComponentCount() - 1); } else { setEnabled(lastEnabled); + getDivider().setEnabled(lastEnabled); + setComponentZOrder(getDivider(), 0); } } + protected BasicSplitPaneDivider getDivider() { + return ((BasicSplitPaneUI) getUI()).getDivider(); + } + @Override public void setEnabled(final boolean enabled) { ((BasicSplitPaneUI) getUI()).getDivider().setEnabled(enabled); diff --git a/src/main/java/com/github/weisj/darklaf/components/text/LineHighlighter.java b/src/main/java/com/github/weisj/darklaf/components/text/LineHighlighter.java new file mode 100644 index 00000000..6d416037 --- /dev/null +++ b/src/main/java/com/github/weisj/darklaf/components/text/LineHighlighter.java @@ -0,0 +1,103 @@ +/* + * 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.github.weisj.darklaf.components.text; + +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.text.BadLocationException; +import javax.swing.text.Highlighter; +import javax.swing.text.JTextComponent; +import java.awt.*; + +public class LineHighlighter implements Highlighter.HighlightPainter, ChangeListener { + private JTextComponent component; + private Color color; + private Rectangle lastView; + + /** + * Manually control the line color + * + * @param component text component that requires background line painting + * @param color the color of the background line + */ + public LineHighlighter(final JTextComponent component, final Color color) { + this.component = component; + setColor(color); + } + + /* + * You can reset the line color at any time + * + * @param color the color of the background line + */ + public void setColor(final Color color) { + this.color = color; + } + + public void paint(@NotNull final Graphics g, final int p0, final int p1, final Shape bounds, + @NotNull final JTextComponent c) { + try { + Rectangle r = c.modelToView2D(c.getCaretPosition()).getBounds(); + g.setColor(color); + g.fillRect(0, r.y, c.getWidth(), r.height); + + if (lastView == null) { lastView = r; } + } catch (BadLocationException ble) { + ble.printStackTrace(); + } + } + + public void setComponent(final JTextComponent component) { + this.component = component; + } + + @Override + public void stateChanged(final ChangeEvent e) { + resetHighlight(); + } + + /* + * Caret position has changed, remove the highlight + */ + private void resetHighlight() { + // Use invokeLater to make sure updates to the Document are completed, + // otherwise Undo processing causes the modelToView method to loop. + if (component == null) return; + SwingUtilities.invokeLater(() -> { + try { + int offset = component.getCaretPosition(); + Rectangle currentView = component.modelToView2D(offset).getBounds(); + + // Remove the highlighting from the previously highlighted line + if (lastView.y != currentView.y) { + component.repaint(0, lastView.y, component.getWidth(), lastView.height); + lastView = currentView; + } + } catch (BadLocationException ignored) {} + }); + } +} diff --git a/src/main/java/com/github/weisj/darklaf/components/text/NonWrappingEditorPane.java b/src/main/java/com/github/weisj/darklaf/components/text/NonWrappingEditorPane.java new file mode 100644 index 00000000..ab9f1afd --- /dev/null +++ b/src/main/java/com/github/weisj/darklaf/components/text/NonWrappingEditorPane.java @@ -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.github.weisj.darklaf.components.text; + +import javax.swing.*; +import javax.swing.plaf.ComponentUI; +import java.awt.*; + +/** + * JTextPane that keeps the original width of text. + * + * @author Jannis Weis + * @since 2018 + */ +public class NonWrappingEditorPane extends JEditorPane { + + @Override + public Dimension getPreferredSize() { + // Avoid substituting the minimum width for the preferred width + // when the viewport is too narrow. + return getUI().getPreferredSize(this); + } + + @Override + public boolean getScrollableTracksViewportWidth() { + final Component parent = getParent(); + final ComponentUI ui = getUI(); + return parent == null || (ui.getPreferredSize(this).width <= parent.getSize().width); + } + + @Override + public int getScrollableUnitIncrement(final Rectangle visibleRect, final int orientation, final int direction) { + return getFont().getSize(); + } +} diff --git a/src/main/java/com/github/weisj/darklaf/components/text/NonWrappingTextArea.java b/src/main/java/com/github/weisj/darklaf/components/text/NonWrappingTextArea.java new file mode 100644 index 00000000..8e034eef --- /dev/null +++ b/src/main/java/com/github/weisj/darklaf/components/text/NonWrappingTextArea.java @@ -0,0 +1,51 @@ +/* + * 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.github.weisj.darklaf.components.text; + +import javax.swing.*; +import javax.swing.plaf.ComponentUI; +import java.awt.*; + +/** + * JTextPane that keeps the original width of text. + * + * @author Jannis Weis + * @since 2018 + */ +public class NonWrappingTextArea extends JTextArea { + + @Override + public Dimension getPreferredSize() { + // Avoid substituting the minimum width for the preferred width + // when the viewport is too narrow. + return getUI().getPreferredSize(this); + } + + @Override + public boolean getScrollableTracksViewportWidth() { + final Component parent = getParent(); + final ComponentUI ui = getUI(); + return parent == null || (ui.getPreferredSize(this).width <= parent.getSize().width); + } +} diff --git a/src/main/java/com/github/weisj/darklaf/components/text/NonWrappingTextPane.java b/src/main/java/com/github/weisj/darklaf/components/text/NonWrappingTextPane.java new file mode 100644 index 00000000..5c7b6f02 --- /dev/null +++ b/src/main/java/com/github/weisj/darklaf/components/text/NonWrappingTextPane.java @@ -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.github.weisj.darklaf.components.text; + +import javax.swing.*; +import javax.swing.plaf.ComponentUI; +import java.awt.*; + +/** + * JTextPane that keeps the original width of text. + * + * @author Jannis Weis + * @since 2018 + */ +public class NonWrappingTextPane extends JTextPane { + + @Override + public Dimension getPreferredSize() { + // Avoid substituting the minimum width for the preferred width + // when the viewport is too narrow. + return getUI().getPreferredSize(this); + } + + @Override + public boolean getScrollableTracksViewportWidth() { + final Component parent = getParent(); + final ComponentUI ui = getUI(); + return parent == null || (ui.getPreferredSize(this).width <= parent.getSize().width); + } + + @Override + public int getScrollableUnitIncrement(final Rectangle visibleRect, final int orientation, final int direction) { + return getFont().getSize(); + } +} diff --git a/src/main/java/com/github/weisj/darklaf/components/text/NumberedTextComponent.java b/src/main/java/com/github/weisj/darklaf/components/text/NumberedTextComponent.java new file mode 100644 index 00000000..328b70b0 --- /dev/null +++ b/src/main/java/com/github/weisj/darklaf/components/text/NumberedTextComponent.java @@ -0,0 +1,49 @@ +/* + * 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.github.weisj.darklaf.components.text; + +import com.github.weisj.darklaf.components.OverlayScrollPane; + +import javax.swing.*; +import javax.swing.text.JTextComponent; +import java.awt.*; + +public class NumberedTextComponent extends JPanel { + + private NumberingPane numberingPane; + + public NumberedTextComponent(final JTextComponent textComponent) { + super(new BorderLayout()); + OverlayScrollPane overlayScrollPane = new OverlayScrollPane(textComponent); + numberingPane = new NumberingPane(); + numberingPane.setTextComponent(textComponent); + overlayScrollPane.getVerticalScrollBar().setBlockIncrement(textComponent.getFont().getSize()); + overlayScrollPane.getScrollPane().setRowHeaderView(numberingPane); + add(overlayScrollPane, BorderLayout.CENTER); + } + + public NumberingPane getNumberingPane() { + return numberingPane; + } +} diff --git a/src/main/java/com/github/weisj/darklaf/components/text/NumberingPane.java b/src/main/java/com/github/weisj/darklaf/components/text/NumberingPane.java new file mode 100644 index 00000000..2a76a070 --- /dev/null +++ b/src/main/java/com/github/weisj/darklaf/components/text/NumberingPane.java @@ -0,0 +1,117 @@ +/* + * 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.github.weisj.darklaf.components.text; + +import javax.swing.*; +import javax.swing.text.BadLocationException; +import javax.swing.text.JTextComponent; +import javax.swing.text.Position; +import javax.swing.text.Segment; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public class NumberingPane extends JComponent { + + private JTextComponent textComponent; + private Map iconMap; + + public NumberingPane() { + iconMap = new HashMap<>(); + updateUI(); + } + + @Override + public void updateUI() { + setUI(UIManager.getUI(this)); + } + + @Override + public String getUIClassID() { + return "NumberingPaneUI"; + } + + public JTextComponent getTextComponent() { + return textComponent; + } + + public void setTextComponent(final JTextComponent textComponent) { + var old = this.textComponent; + this.textComponent = textComponent; + firePropertyChange("editorPane", old, textComponent); + } + + public int getIconCount() { + return iconMap.size(); + } + + public List> getIconsInRange(final int startOff, final int endOff) { + return iconMap.entrySet().stream() + .filter(e -> { + var pos = e.getKey(); + return pos.getOffset() >= startOff && pos.getOffset() <= endOff; + }) + .collect(Collectors.toList()); + } + + public Position addIconAtLine(final int lineIndex, final Icon icon) throws BadLocationException { + return addIconAtLine(lineIndex, icon, true); + } + + public Position addIconAtLine(final int lineIndex, final Icon icon, final boolean atTextStart) throws BadLocationException { + int offset = textComponent.getDocument().getDefaultRootElement().getElement(lineIndex).getStartOffset(); + if (atTextStart) { + var doc = textComponent.getDocument(); + var txt = new Segment(); + txt.setPartialReturn(true); + String str = doc.getText(offset, 1); + while (str.isBlank()) { + offset++; + str = doc.getText(offset, 1); + } + } + return addIconAtOffset(offset, icon); + } + + public Position addIconAtOffset(final int offset, final Icon icon) throws BadLocationException { + var doc = textComponent.getDocument(); + var pos = doc.createPosition(offset); + if (icon != null) { + iconMap.put(pos, icon); + } + firePropertyChange("icons", null, icon); + return pos; + } + + public Collection getIcons() { + return iconMap.values(); + } + + public void removeIconAt(final Position position) { + var icon = iconMap.remove(position); + firePropertyChange("icons", icon, icon); + } +} diff --git a/src/main/java/com/github/weisj/darklaf/icons/ScaledIcon.java b/src/main/java/com/github/weisj/darklaf/icons/ScaledIcon.java new file mode 100644 index 00000000..55c544ac --- /dev/null +++ b/src/main/java/com/github/weisj/darklaf/icons/ScaledIcon.java @@ -0,0 +1,58 @@ +/* + * 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.github.weisj.darklaf.icons; + +import com.github.weisj.darklaf.util.GraphicsUtil; +import org.jetbrains.annotations.Contract; + +import javax.swing.*; +import java.awt.*; + +public class ScaledIcon implements Icon { + + private Image img; + + @Contract(pure = true) + public ScaledIcon(final Image img) { + this.img = img; + } + + @Override + public void paintIcon(final Component c, final Graphics g2, final int x, final int y) { + var g = (Graphics2D) g2; + g.translate(x, y); + g.scale(1.0 / GraphicsUtil.SCALE_X, 1.0 / GraphicsUtil.SCALE_Y); + g.drawImage(img, 0, 0, img.getWidth(null), img.getHeight(null), null); + } + + @Override + public int getIconWidth() { + return (int) (img.getWidth(null) / GraphicsUtil.SCALE_X); + } + + @Override + public int getIconHeight() { + return (int) (img.getHeight(null) / GraphicsUtil.SCALE_Y); + } +} diff --git a/src/main/java/com/github/weisj/darklaf/theme/Theme.java b/src/main/java/com/github/weisj/darklaf/theme/Theme.java index fcd1fa79..3418dbb4 100644 --- a/src/main/java/com/github/weisj/darklaf/theme/Theme.java +++ b/src/main/java/com/github/weisj/darklaf/theme/Theme.java @@ -48,7 +48,7 @@ public abstract class Theme { private static final Logger LOGGER = Logger.getLogger(Theme.class.getName()); private static final String[] UI_PROPERTIES = new String[]{ "borders", "button", "checkBox", "colorChooser", "comboBox", "fileChooser", "tristate", - "internalFrame", "label", "list", "menu", "menuBar", "menuItem", "optionPane", "panel", + "internalFrame", "label", "list", "menu", "menuBar", "menuItem", "numberingPane", "optionPane", "panel", "popupMenu", "progressBar", "radioButton", "rootPane", "scrollBar", "scrollPane", "separator", "slider", "spinner", "splitPane", "statusBar", "tabbedPane", "tabFrame", "table", "taskPane", "text", "toggleButton", "toolBar", "toolTip", "tree", diff --git a/src/main/java/com/github/weisj/darklaf/ui/numberingpane/DarkNumberingPaneBorder.java b/src/main/java/com/github/weisj/darklaf/ui/numberingpane/DarkNumberingPaneBorder.java new file mode 100644 index 00000000..8e096d71 --- /dev/null +++ b/src/main/java/com/github/weisj/darklaf/ui/numberingpane/DarkNumberingPaneBorder.java @@ -0,0 +1,37 @@ +/* + * 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.github.weisj.darklaf.ui.numberingpane; + +import com.github.weisj.darklaf.components.border.MutableLineBorder; + +import javax.swing.*; +import javax.swing.plaf.UIResource; + +public class DarkNumberingPaneBorder extends MutableLineBorder implements UIResource { + + public DarkNumberingPaneBorder() { + super(0, 0, 0, 1, null); + setColor(UIManager.getColor("NumberingPane.borderColor")); + } +} diff --git a/src/main/java/com/github/weisj/darklaf/ui/numberingpane/DarkNumberingPaneUI.java b/src/main/java/com/github/weisj/darklaf/ui/numberingpane/DarkNumberingPaneUI.java new file mode 100644 index 00000000..c87a3cfe --- /dev/null +++ b/src/main/java/com/github/weisj/darklaf/ui/numberingpane/DarkNumberingPaneUI.java @@ -0,0 +1,342 @@ +/* + * 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.github.weisj.darklaf.ui.numberingpane; + +import com.github.weisj.darklaf.components.text.LineHighlighter; +import com.github.weisj.darklaf.components.text.NumberingPane; +import com.github.weisj.darklaf.util.DarkUIUtil; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.plaf.ComponentUI; +import javax.swing.text.BadLocationException; +import javax.swing.text.Caret; +import javax.swing.text.Element; +import javax.swing.text.JTextComponent; +import java.awt.*; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; + +public class DarkNumberingPaneUI extends ComponentUI { + + protected static final int OUTER_PAD = 7; + protected static final int PAD = 5; + protected Handler handler; + protected NumberingPane numberingPane; + protected Color backgroundHighlight; + protected Color foregroundHighlight; + protected LineHighlighter currentLinePainter; + protected int textWidth = 0; + protected JTextComponent textComponent; + protected JViewport viewport; + protected int maxIconWidth = 0; + + @NotNull + @Contract("_ -> new") + public static ComponentUI createUI(final JComponent c) { + return new DarkNumberingPaneUI(); + } + + @Override + public void installUI(final JComponent c) { + numberingPane = (NumberingPane) c; + super.installUI(c); + installDefaults(c); + installListeners(c); + } + + protected void installDefaults(final JComponent c) { + LookAndFeel.installColorsAndFont(c, "NumberingPane.background", + "NumberingPane.foreground", + "NumberingPane.font"); + foregroundHighlight = UIManager.getColor("NumberingPane.currentLineForeground"); + backgroundHighlight = UIManager.getColor("NumberingPane.currentLineBackground"); + LookAndFeel.installBorder(c, "NumberingPane.border"); + } + + protected void installListeners(final JComponent c) { + currentLinePainter = new LineHighlighter(null, backgroundHighlight); + numberingPane.addMouseListener(getMouseListener()); + numberingPane.addMouseMotionListener(getMouseMotionListener()); + numberingPane.addPropertyChangeListener(getPropertyChangeListener()); + } + + protected MouseListener getMouseListener() { + if (handler == null) { + handler = new Handler(); + } + return handler; + } + + protected MouseMotionListener getMouseMotionListener() { + if (handler == null) { + handler = new Handler(); + } + return handler; + } + + protected PropertyChangeListener getPropertyChangeListener() { + if (handler == null) { + handler = new Handler(); + } + return handler; + } + + @Override + public void uninstallUI(final JComponent c) { + super.uninstallUI(c); + uninstallListeners(c); + currentLinePainter.setComponent(null); + currentLinePainter = null; + numberingPane = null; + } + + protected void uninstallListeners(final JComponent c) { + if (textComponent != null) { + textComponent.getCaret().removeChangeListener(getChangeListener()); + textComponent.getCaret().removeChangeListener(currentLinePainter); + textComponent.removePropertyChangeListener(getPropertyChangeListener()); + } + numberingPane.removePropertyChangeListener(getPropertyChangeListener()); + numberingPane.removeMouseListener(getMouseListener()); + numberingPane.removeMouseMotionListener(getMouseMotionListener()); + } + + protected ChangeListener getChangeListener() { + if (handler == null) { + handler = new Handler(); + } + return handler; + } + + @Override + public void paint(final Graphics g, final JComponent c) { + super.paint(g, c); + if (textComponent == null || viewport == null) return; + var metrics = textComponent.getFontMetrics(textComponent.getFont()); + int descent = metrics.getDescent(); + + var doc = textComponent.getDocument(); + var viewRect = viewport.getViewRect(); + var p = viewRect.getLocation(); + int startIndex = textComponent.viewToModel2D(p); + p.y += viewRect.height; + int endIndex = textComponent.viewToModel2D(p); + + int currOffset = textComponent.getCaretPosition(); + Element root = doc.getDefaultRootElement(); + int startLine = root.getElementIndex(startIndex); + int endLine = root.getElementIndex(endIndex); + + int yCurr = drawHighlightBackground(g, currOffset); + drawNumbering(g, startLine, endLine, yCurr, root, descent); + paintIcons(g, startLine, endLine, root); + } + + @Override + public Dimension getPreferredSize(final JComponent c) { + if (textComponent == null || viewport == null) return super.getPreferredSize(c); + int lines = textComponent.getDocument().getDefaultRootElement().getElementCount(); + int pad = 2 * OUTER_PAD; + if (maxIconWidth > 0) pad += PAD; + textWidth = numberingPane.getFontMetrics(numberingPane.getFont()).stringWidth(String.valueOf(lines)); + return new Dimension(maxIconWidth + pad + textWidth, viewport.getView().getHeight()); + } + + protected int drawHighlightBackground(@NotNull final Graphics g, final int currOffset) { + g.setColor(backgroundHighlight); + Rectangle rect; + try { + rect = textComponent.modelToView2D(currOffset).getBounds(); + } catch (BadLocationException e) { + rect = new Rectangle(0, 0, 0, 0); + } + g.fillRect(0, rect.y, numberingPane.getWidth(), rect.height); + return rect.y; + } + + protected void drawNumbering(@NotNull final Graphics g, final int startLine, final int endLine, final int yCur, + @NotNull final Element root, final int descent) { + g.setColor(numberingPane.getForeground()); + int digits = String.valueOf(root.getElementCount()).length(); + for (int i = startLine; i <= endLine; i++) { + int off = root.getElement(i).getStartOffset(); + try { + Rectangle lineRect = textComponent.modelToView2D(off).getBounds(); + g.setColor(lineRect.y == yCur ? foregroundHighlight : numberingPane.getForeground()); + g.drawString(String.format("%1$" + digits + "d", i), OUTER_PAD, + lineRect.y + lineRect.height - descent); + } catch (BadLocationException ignored) { } + } + } + + protected void paintIcons(final Graphics g, final int startLine, final int endLine, @NotNull final Element root) { + var icons = numberingPane.getIconsInRange(root.getElement(startLine).getStartOffset(), + root.getElement(endLine).getEndOffset()); + for (var icon : icons) { + Rectangle lineRect; + try { + lineRect = textComponent.modelToView2D(icon.getKey().getOffset()).getBounds(); + int h = icon.getValue().getIconHeight(); + int x = OUTER_PAD + PAD + textWidth; + int y = lineRect.y + lineRect.height / 2 - h / 2; + icon.getValue().paintIcon(numberingPane, g, x, y); + } catch (BadLocationException ignored) { + } + } + } + + protected int calculateMaxIconWidth() { + var icons = numberingPane.getIcons(); + int max = 0; + for (var icon : icons) { + max = Math.max(icon.getIconWidth(), max); + } + return max; + } + + protected class Handler extends MouseAdapter implements PropertyChangeListener, ChangeListener { + + protected int selectionLineStart; + protected int selectionLineEnd; + protected Object currentHighlight; + + @Override + public void mouseClicked(final MouseEvent e) { + if (textComponent == null) return; + var p = e.getPoint(); + int iconCount = numberingPane.getIconCount(); + int width = numberingPane.getWidth(); + if (iconCount > 0 && p.x > PAD + OUTER_PAD + textWidth && p.x <= width - PAD) { + int offset = textComponent.viewToModel2D(new Point(0, p.y)); + var doc = textComponent.getDocument(); + int start = doc.getDefaultRootElement().getElementIndex(offset); + int startOffset = doc.getDefaultRootElement().getElement(start).getStartOffset(); + int endOffset = doc.getDefaultRootElement().getElement(start).getEndOffset(); + var icons = numberingPane.getIconsInRange(startOffset, endOffset); + if (!icons.isEmpty()) { + var icon = icons.get(0).getValue(); + Rectangle lineRect; + try { + lineRect = textComponent.modelToView2D(start).getBounds(); + int h = icon.getIconHeight(); + int x = OUTER_PAD + PAD + textWidth; + int y = lineRect.y + lineRect.height / 2 - h / 2; + if (p.x >= x && p.y >= y && p.y <= y + h) { + System.out.println("clicked icon" + start); + return; + } + } catch (BadLocationException ignored) { } + } + System.out.println("clicked line: " + start); + } + } + + @Override + public void mousePressed(final MouseEvent e) { + if (textComponent == null) return; + var p = e.getPoint(); + if (p.x <= OUTER_PAD + textWidth) { + selectionLineStart = textComponent.viewToModel2D(new Point(0, p.y)); + selectionLineEnd = textComponent.viewToModel2D(new Point(textComponent.getWidth(), p.y)); + textComponent.getCaret().setDot(selectionLineEnd + 1); + textComponent.getCaret().moveDot(Math.min(selectionLineStart, + textComponent.getDocument().getLength())); + } + } + + @Override + public void stateChanged(final ChangeEvent e) { + numberingPane.repaint(); + } + + @Override + public void propertyChange(@NotNull final PropertyChangeEvent evt) { + var key = evt.getPropertyName(); + if ("caret".equals(key)) { + if (evt.getNewValue() instanceof Caret) { + var oldCaret = evt.getOldValue(); + if (oldCaret instanceof Caret) { + ((Caret) oldCaret).removeChangeListener(getChangeListener()); + ((Caret) oldCaret).removeChangeListener(currentLinePainter); + } + var newCaret = evt.getNewValue(); + if (newCaret instanceof Caret) { + ((Caret) newCaret).addChangeListener(getChangeListener()); + ((Caret) newCaret).addChangeListener(currentLinePainter); + } + } + } else if ("font".equals(key)) { + var font = textComponent.getFont(); + numberingPane.setFont(font.deriveFont(Math.max(font.getSize() - 1, 1.0f))); + } else if ("editorPane".equals(key)) { + var newPane = evt.getNewValue(); + if (textComponent != null) { + currentLinePainter.setComponent(null); + textComponent.getHighlighter().removeHighlight(currentHighlight); + textComponent.getCaret().removeChangeListener(getChangeListener()); + textComponent.getCaret().removeChangeListener(currentLinePainter); + textComponent.removePropertyChangeListener(getPropertyChangeListener()); + } + if (newPane instanceof JTextComponent) { + textComponent = (JTextComponent) newPane; + try { + currentHighlight = textComponent.getHighlighter().addHighlight(0, 0, currentLinePainter); + textComponent.getCaret().addChangeListener(currentLinePainter); + currentLinePainter.setComponent(textComponent); + } catch (BadLocationException ignored) {} + textComponent.addPropertyChangeListener(getPropertyChangeListener()); + textComponent.getCaret().addChangeListener(getChangeListener()); + var font = textComponent.getFont(); + numberingPane.setFont(font.deriveFont(Math.max(font.getSize() - 1, 1.0f))); + } + } else if ("icons".equals(key)) { + var oldVal = evt.getOldValue(); + var newVal = evt.getNewValue(); + if (oldVal instanceof Icon) { + maxIconWidth = calculateMaxIconWidth(); + } + if (newVal instanceof Icon) { + maxIconWidth = Math.max(maxIconWidth, ((Icon) newVal).getIconWidth()); + } + } else if ("ancestor".equals(key)) { + if (evt.getSource() == numberingPane) { + var parent = DarkUIUtil.getParentOfType(JScrollPane.class, (Component) evt.getNewValue()); + if (parent != null) { + viewport = parent.getViewport(); + } else { + viewport = null; + } + } + } + } + } +} diff --git a/src/main/java/com/github/weisj/darklaf/ui/rootpane/DarkTitlePane.java b/src/main/java/com/github/weisj/darklaf/ui/rootpane/DarkTitlePane.java index fa3a1dd7..479d050c 100644 --- a/src/main/java/com/github/weisj/darklaf/ui/rootpane/DarkTitlePane.java +++ b/src/main/java/com/github/weisj/darklaf/ui/rootpane/DarkTitlePane.java @@ -25,6 +25,7 @@ package com.github.weisj.darklaf.ui.rootpane; import com.github.weisj.darklaf.decorators.AncestorAdapter; +import com.github.weisj.darklaf.icons.ScaledIcon; import com.github.weisj.darklaf.platform.windows.JNIDecorations; import com.github.weisj.darklaf.util.GraphicsUtil; import org.jetbrains.annotations.Contract; @@ -767,31 +768,4 @@ public class DarkTitlePane extends JComponent { } } - protected static class ScaledIcon implements Icon { - - private Image img; - - @Contract(pure = true) - protected ScaledIcon(final Image img) { - this.img = img; - } - - @Override - public void paintIcon(final Component c, final Graphics g2, final int x, final int y) { - var g = (Graphics2D) g2; - g.translate(x, y); - g.scale(1.0 / GraphicsUtil.SCALE_X, 1.0 / GraphicsUtil.SCALE_Y); - g.drawImage(img, 0, 0, img.getWidth(null), img.getHeight(null), null); - } - - @Override - public int getIconWidth() { - return (int) (img.getWidth(null) / GraphicsUtil.SCALE_X); - } - - @Override - public int getIconHeight() { - return (int) (img.getHeight(null) / GraphicsUtil.SCALE_Y); - } - } } diff --git a/src/main/java/com/github/weisj/darklaf/ui/splitpane/DarkSplitPaneUI.java b/src/main/java/com/github/weisj/darklaf/ui/splitpane/DarkSplitPaneUI.java index 5958a3aa..2e0147a1 100644 --- a/src/main/java/com/github/weisj/darklaf/ui/splitpane/DarkSplitPaneUI.java +++ b/src/main/java/com/github/weisj/darklaf/ui/splitpane/DarkSplitPaneUI.java @@ -182,6 +182,12 @@ public class DarkSplitPaneUI extends BasicSplitPaneUI implements PropertyChangeL } } + @Override + public boolean contains(final Point p) { + if (!isEnabled()) return false; + return super.contains(p); + } + @Override protected void dragDividerTo(final int location) { super.dragDividerTo(location + DIVIDER_DRAG_OFFSET); diff --git a/src/main/java/com/github/weisj/darklaf/ui/text/DarkCaret.java b/src/main/java/com/github/weisj/darklaf/ui/text/DarkCaret.java index 1074f957..03a7b920 100644 --- a/src/main/java/com/github/weisj/darklaf/ui/text/DarkCaret.java +++ b/src/main/java/com/github/weisj/darklaf/ui/text/DarkCaret.java @@ -250,61 +250,61 @@ public class DarkCaret extends DefaultCaret implements UIResource { @Override public void paint(final Graphics g) { if (isVisible() || alwaysVisible) { + JTextComponent textArea = getComponent(); + g.setColor(textArea.getCaretColor()); + TextUI mapper = textArea.getUI(); + Rectangle r; try { - JTextComponent textArea = getComponent(); - g.setColor(textArea.getCaretColor()); - TextUI mapper = textArea.getUI(); - Rectangle r = mapper.modelToView2D(textArea, getDot(), Position.Bias.Forward).getBounds(); - - validateWidth(r); - - if (width > 0 && height > 0 && - !contains(r.x, r.y, r.width, r.height)) { - Rectangle clip = g.getClipBounds(); - if (clip != null && !clip.contains(this)) { - // Clip doesn't contain the old location, force it - // to be repainted lest we leave a caret around. - repaint(); - } - damage(r); + r = mapper.modelToView2D(textArea, getDot(), Position.Bias.Forward).getBounds(); + } catch (BadLocationException ex) { + r = new Rectangle(0, 0, 0, 0); + } + validateWidth(r); + + if (width > 0 && height > 0 && + !contains(r.x, r.y, r.width, r.height)) { + Rectangle clip = g.getClipBounds(); + if (clip != null && !clip.contains(this)) { + // Clip doesn't contain the old location, force it + // to be repainted lest we leave a caret around. + repaint(); } + damage(r); + } - // Need to subtract 2 from height, otherwise - // the caret will expand too far vertically. - r.height -= 2; - r.y += 1; + // Need to subtract 2 from height, otherwise + // the caret will expand too far vertically. + r.height -= 2; + r.y += 1; - Color textAreaBg = textArea.getBackground(); - switch (style) { - case BLOCK_STYLE: - if (textAreaBg == null) { - textAreaBg = Color.white; - } - g.setXORMode(textAreaBg); - g.fillRect(r.x, r.y, r.width, r.height); - break; - case BLOCK_BORDER_STYLE: - DarkUIUtil.drawRect(g, r.x, r.y, r.width - 1, r.height, 1); - break; - case UNDERLINE_STYLE: - if (textAreaBg == null) { - textAreaBg = Color.white; - } - g.setXORMode(textAreaBg); - int y = r.y + r.height; - g.drawLine(r.x, y, r.x + r.width - 1, y); - break; - case THICK_VERTICAL_LINE_STYLE: - g.drawLine(r.x, r.y, r.x, r.y + r.height); - r.x++; - g.drawLine(r.x, r.y, r.x, r.y + r.height); - break; - case VERTICAL_LINE_STYLE: - g.drawLine(r.x, r.y, r.x, r.y + r.height); - break; - } - } catch (BadLocationException ble) { - ble.printStackTrace(); + Color textAreaBg = textArea.getBackground(); + switch (style) { + case BLOCK_STYLE: + if (textAreaBg == null) { + textAreaBg = Color.white; + } + g.setXORMode(textAreaBg); + g.fillRect(r.x, r.y, r.width, r.height); + break; + case BLOCK_BORDER_STYLE: + DarkUIUtil.drawRect(g, r.x, r.y, r.width - 1, r.height, 1); + break; + case UNDERLINE_STYLE: + if (textAreaBg == null) { + textAreaBg = Color.white; + } + g.setXORMode(textAreaBg); + int y = r.y + r.height; + g.drawLine(r.x, y, r.x + r.width - 1, y); + break; + case THICK_VERTICAL_LINE_STYLE: + g.drawLine(r.x, r.y, r.x, r.y + r.height); + r.x++; + g.drawLine(r.x, r.y, r.x, r.y + r.height); + break; + case VERTICAL_LINE_STYLE: + g.drawLine(r.x, r.y, r.x, r.y + r.height); + break; } } } @@ -340,24 +340,21 @@ public class DarkCaret extends DefaultCaret implements UIResource { private void validateWidth(final Rectangle rect) { if (rect != null && rect.width <= 1) { + JTextComponent textArea = getComponent(); try { - JTextComponent textArea = getComponent(); textArea.getDocument().getText(getDot(), 1, seg); - Font font = textArea.getFont(); - FontMetrics fm = textArea.getFontMetrics(font); - rect.width = fm.charWidth(seg.array[seg.offset]); - - if (rect.width == 0) { - rect.width = fm.charWidth(' '); - } - } catch (BadLocationException ble) { // This shouldn't ever happen. ble.printStackTrace(); rect.width = 8; } + Font font = textArea.getFont(); + FontMetrics fm = textArea.getFontMetrics(font); + rect.width = fm.charWidth(seg.array[seg.offset]); + + if (rect.width == 0) { + rect.width = fm.charWidth(' '); + } } } - - } diff --git a/src/main/java/com/github/weisj/darklaf/util/Pair.java b/src/main/java/com/github/weisj/darklaf/util/Pair.java index 457d8f64..2c4dd300 100644 --- a/src/main/java/com/github/weisj/darklaf/util/Pair.java +++ b/src/main/java/com/github/weisj/darklaf/util/Pair.java @@ -43,4 +43,12 @@ public class Pair { public T getFirst() { return first; } + + public void setFirst(final T first) { + this.first = first; + } + + public void setSecond(final H second) { + this.second = second; + } } diff --git a/src/main/resources/com/github/weisj/darklaf/properties/ui/numberingPane.properties b/src/main/resources/com/github/weisj/darklaf/properties/ui/numberingPane.properties new file mode 100644 index 00000000..4b3c1b7c --- /dev/null +++ b/src/main/resources/com/github/weisj/darklaf/properties/ui/numberingPane.properties @@ -0,0 +1,31 @@ +# suppress inspection "UnusedProperty" for whole file +# +# 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. +# +NumberingPaneUI = com.github.weisj.darklaf.ui.numberingpane.DarkNumberingPaneUI +NumberingPane.border = com.github.weisj.darklaf.ui.numberingpane.DarkNumberingPaneBorder +NumberingPane.borderColor = %border +NumberingPane.foreground = %textForegroundSecondary +NumberingPane.background = %textBackgroundSecondaryInactive +NumberingPane.currentLineForeground = %textForeground +NumberingPane.currentLineBackground = %textSelectionBackgroundSecondary \ No newline at end of file diff --git a/src/test/java/ScrollPaneDemo.java b/src/test/java/ScrollPaneDemo.java index 78255321..722b2115 100644 --- a/src/test/java/ScrollPaneDemo.java +++ b/src/test/java/ScrollPaneDemo.java @@ -17,7 +17,7 @@ public final class ScrollPaneDemo extends MultiSplitLayout { frame.setSize(500, 500); var overlayScroll = new JScrollPane(new JEditorPane() {{ // setEditorKit(new HTMLEditorKit()); - setText(TestResources.LOREM_IPSUM); + setText(TestResources.LOREM_IPSUM.repeat(10)); setFont(Font.getFont(Font.MONOSPACED)); // setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT); }}); diff --git a/src/test/java/TabFrameDemo.java b/src/test/java/TabFrameDemo.java index 96a4eba0..93cae98d 100644 --- a/src/test/java/TabFrameDemo.java +++ b/src/test/java/TabFrameDemo.java @@ -1,10 +1,12 @@ import com.github.weisj.darklaf.LafManager; -import com.github.weisj.darklaf.components.OverlayScrollPane; import com.github.weisj.darklaf.components.SelectableTreeNode; import com.github.weisj.darklaf.components.alignment.Alignment; import com.github.weisj.darklaf.components.tabframe.JTabFrame; import com.github.weisj.darklaf.components.tabframe.TabbedPopup; +import com.github.weisj.darklaf.components.text.NonWrappingEditorPane; +import com.github.weisj.darklaf.components.text.NumberedTextComponent; import com.github.weisj.darklaf.icons.IconLoader; +import org.jetbrains.annotations.NotNull; import javax.swing.*; import javax.swing.tree.DefaultMutableTreeNode; @@ -74,14 +76,8 @@ public class TabFrameDemo { */ tabFrame.setAcceleratorAt(1, Alignment.NORTH_WEST, 0); - var contentPane = new JPanel(new BorderLayout()); - var overlayScroll = new OverlayScrollPane(new JTextPane() {{ - setText(TestResources.LOREM_IPSUM); - setFont(Font.getFont(Font.MONOSPACED)); - }}); - contentPane.add(overlayScroll, BorderLayout.CENTER); frame.setContentPane(tabFrame); - tabFrame.setContent(contentPane); + tabFrame.setContent(createTextArea()); frame.pack(); frame.setSize(1000, 500); @@ -90,6 +86,7 @@ public class TabFrameDemo { }); } + @NotNull protected static JTree createTree() { DefaultMutableTreeNode root = new DefaultMutableTreeNode("States"); DefaultMutableTreeNode parent1 = new DefaultMutableTreeNode("Andhra Pradesh"); @@ -113,4 +110,22 @@ public class TabFrameDemo { // tree.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT); return tree; } + + @NotNull + private static Component createTextArea() { + var numberPane = new NumberedTextComponent(new NonWrappingEditorPane() {{ + setText((TestResources.LOREM_IPSUM).repeat(10)); + setFont(new Font(Font.MONOSPACED, Font.PLAIN, 14)); + }}); + var numbering = numberPane.getNumberingPane(); + Icon icon = IconLoader.get().getIcon("navigation/arrowRight.svg"); +// try { +// numbering.addIconAtLine(5, icon); +// numbering.addIconAtLine(10, icon); +// numbering.addIconAtLine(15, icon); +// } catch (BadLocationException e) { +// e.printStackTrace(); +// } + return numberPane; + } } diff --git a/src/test/java/TestResources.java b/src/test/java/TestResources.java index 2f73075e..02a4d6f2 100644 --- a/src/test/java/TestResources.java +++ b/src/test/java/TestResources.java @@ -23,28 +23,28 @@ */ public class TestResources { public static final String LOREM_IPSUM = - "Lorem ipsum dolor sit amet, consectetur adipiscing elit. In tempor quis nibh a semper. Nullam" - + " auctor, erat non viverra commodo, libero orci aliquam quam, ac interdum nunc est sed " - + "ligula. Aliquam vel velit non dolor accumsan blandit id eu metus. Aenean iaculis urna in " - + "placerat aliquam. Aliquam dui quam, bibendum sed magna in, cursus ornare est. Quisque " - + "tempor nunc quis nunc tempor convallis. Vestibulum tristique luctus ante, ac hendrerit dui" + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. In tempor quis nibh a semper. Nullam\n" + + " auctor, erat non viverra commodo, libero orci aliquam quam, ac interdum nunc est sed\n " + + "ligula. Aliquam vel velit non dolor accumsan blandit id eu metus. Aenean iaculis urna in\n " + + "placerat aliquam. Aliquam dui quam, bibendum sed magna in, cursus ornare est. Quisque\n " + + "tempor nunc quis nunc tempor convallis. Vestibulum tristique luctus ante, ac hendrerit dui\n" + ".\n\n" - + "Donec ut maximus augue. Nam eleifend maximus scelerisque. Duis varius accumsan est, non " - + "aliquam dolor. Aenean iaculis nibh in aliquam viverra. Sed laoreet, urna ut facilisis " - + "convallis, arcu turpis vestibulum augue, id convallis tellus metus nec orci. Lorem ipsum " - + "dolor sit amet, consectetur adipiscing elit. Donec hendrerit purus velit, at blandit elit " - + "luctus ut. Proin diam nisl, sodales vitae dignissim nec, eleifend eu libero. Maecenas odio" - + " ligula, fermentum eget nisl vel, cursus tristique est. In nec nibh nec dui tempor " - + "ullamcorper. Praesent tincidunt luctus sem, ut luctus dolor commodo non. Nulla consectetur" - + " facilisis dolor, in facilisis ligula fringilla et. Cras id placerat libero. Donec " - + "vehicula orci a quam rutrum, eu efficitur lorem iaculis. Aenean varius nisi in dictum " + + "Donec ut maximus augue. Nam eleifend maximus scelerisque. Duis varius accumsan est, non\n " + + "aliquam dolor. Aenean iaculis nibh in aliquam viverra. Sed laoreet, urna ut facilisis\n " + + "convallis, arcu turpis vestibulum augue, id convallis tellus metus nec orci. Lorem ipsum\n " + + "dolor sit amet, consectetur adipiscing elit. Donec hendrerit purus velit, at blandit elit\n " + + "luctus ut. Proin diam nisl, sodales vitae dignissim nec, eleifend eu libero. Maecenas odio\n" + + " ligula, fermentum eget nisl vel, cursus tristique est. In nec nibh nec dui tempor\n " + + "ullamcorper. Praesent tincidunt luctus sem, ut luctus dolor commodo non. Nulla consectetur\n" + + " facilisis dolor, in facilisis ligula fringilla et. Cras id placerat libero. Donec\n " + + "vehicula orci a quam rutrum, eu efficitur lorem iaculis. Aenean varius nisi in dictum\n " + "accumsan.\n\n" - + "Nulla massa ipsum, consectetur non gravida ut, blandit quis velit. Ut pretium quam aliquam" - + " diam porttitor mattis. Nam ullamcorper, felis ut iaculis iaculis, nunc odio pulvinar " - + "enim, vitae iaculis turpis sapien iaculis metus. Donec rutrum varius augue in dictum. Cras" - + " vestibulum vitae mauris ut finibus. Ut dictum imperdiet lorem et imperdiet. Vivamus " - + "semper tempor dolor eu porta. Sed at vehicula nisl. Pellentesque ut lorem tincidunt, " - + "elementum ligula at, porta turpis. Praesent feugiat dolor diam, at facilisis metus gravida" - + " non. Aliquam quis pellentesque nibh. Sed vestibulum porttitor nisi. In vitae malesuada " + + "Nulla massa ipsum, consectetur non gravida ut, blandit quis velit. Ut pretium quam aliquam\n" + + " diam porttitor mattis. Nam ullamcorper, felis ut iaculis iaculis, nunc odio pulvinar \n" + + "enim, vitae iaculis turpis sapien iaculis metus. Donec rutrum varius augue in dictum. Cras\n" + + " vestibulum vitae mauris ut finibus. Ut dictum imperdiet lorem et imperdiet. Vivamus \n" + + "semper tempor dolor eu porta. Sed at vehicula nisl. Pellentesque ut lorem tincidunt, \n" + + "elementum ligula at, porta turpis. Praesent feugiat dolor diam, at facilisis metus gravida\n" + + " non. Aliquam quis pellentesque nibh. Sed vestibulum porttitor nisi. In vitae malesuada\n " + "sapien."; }