From 9b6c244fdad732aa7ee7948bc661350f6f2559f8 Mon Sep 17 00:00:00 2001 From: weisj Date: Sat, 26 Oct 2019 20:45:57 +0200 Subject: [PATCH] Added widget border creation. Added option to change selection foreground in StyledDocument. Signed-off-by: weisj --- .../components/border/DarkBorders.java | 23 +- .../darklaf/ui/list/DarkListUIBridge.java | 640 +++++++++++++++++- .../darklaf/ui/text/StyleConstantsEx.java | 28 + .../DarkHighlightPainter.java | 20 +- 4 files changed, 701 insertions(+), 10 deletions(-) create mode 100644 src/main/java/com/github/weisj/darklaf/ui/text/StyleConstantsEx.java diff --git a/src/main/java/com/github/weisj/darklaf/components/border/DarkBorders.java b/src/main/java/com/github/weisj/darklaf/components/border/DarkBorders.java index 37ce88ce..02cfa7b4 100644 --- a/src/main/java/com/github/weisj/darklaf/components/border/DarkBorders.java +++ b/src/main/java/com/github/weisj/darklaf/components/border/DarkBorders.java @@ -34,25 +34,40 @@ public final class DarkBorders { private static final WeakLineBorder KEY = new WeakLineBorder(0, 0, 0, 0); private static Map lineBorderMap = new WeakHashMap<>(); + private static Map lineWidgetBorderMap = new WeakHashMap<>(); @NotNull public static Border createLineBorder(final int top, final int left, final int bottom, final int right) { + return createBorder(top, left, bottom, right, lineBorderMap, "border"); + } + + @NotNull + private static Border createBorder(final int top, final int left, final int bottom, final int right, + @NotNull final Map map, final String key) { WeakLineBorder border = null; KEY.setInsets(top, left, bottom, right); - if (lineBorderMap.containsKey(KEY)) { - border = lineBorderMap.get(KEY); + if (map.containsKey(KEY)) { + border = map.get(KEY); } if (border == null) { border = new WeakLineBorder(top, left, bottom, right); - lineBorderMap.put(KEY, border); + map.put(KEY, border); } - border.setColor(UIManager.getColor("border")); + border.setColor(UIManager.getColor(key)); return border; } + @NotNull + public static Border createWidgetLineBorder(final int top, final int left, final int bottom, final int right) { + return createBorder(top, left, bottom, right, lineWidgetBorderMap, "borderSecondary"); + } + public static void update() { for (var border : lineBorderMap.values()) { border.setColor(UIManager.getColor("border")); } + for (var border : lineWidgetBorderMap.values()) { + border.setColor(UIManager.getColor("border")); + } } } diff --git a/src/main/java/com/github/weisj/darklaf/ui/list/DarkListUIBridge.java b/src/main/java/com/github/weisj/darklaf/ui/list/DarkListUIBridge.java index b0d676d9..b4d362fd 100644 --- a/src/main/java/com/github/weisj/darklaf/ui/list/DarkListUIBridge.java +++ b/src/main/java/com/github/weisj/darklaf/ui/list/DarkListUIBridge.java @@ -27,8 +27,10 @@ import com.github.weisj.darklaf.util.DarkUIUtil; import com.github.weisj.darklaf.util.LazyActionMap; import org.jdesktop.swingx.plaf.basic.core.BasicTransferable; import org.jdesktop.swingx.plaf.basic.core.DragRecognitionSupport; +import org.jetbrains.annotations.NotNull; import sun.swing.DefaultLookup; import sun.swing.SwingUtilities2; +import sun.swing.UIAction; import javax.swing.*; import javax.swing.event.ListDataEvent; @@ -42,6 +44,7 @@ import javax.swing.plaf.basic.BasicListUI; import javax.swing.text.Position; import java.awt.*; import java.awt.datatransfer.Transferable; +import java.awt.event.ActionEvent; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.awt.event.KeyEvent; @@ -457,6 +460,46 @@ public class DarkListUIBridge extends BasicListUI { } } + public static void loadActionMap(@NotNull final LazyActionMap map) { + map.put(new Actions(Actions.SELECT_PREVIOUS_COLUMN)); + map.put(new Actions(Actions.SELECT_PREVIOUS_COLUMN_EXTEND)); + map.put(new Actions(Actions.SELECT_PREVIOUS_COLUMN_CHANGE_LEAD)); + map.put(new Actions(Actions.SELECT_NEXT_COLUMN)); + map.put(new Actions(Actions.SELECT_NEXT_COLUMN_EXTEND)); + map.put(new Actions(Actions.SELECT_NEXT_COLUMN_CHANGE_LEAD)); + map.put(new Actions(Actions.SELECT_PREVIOUS_ROW)); + map.put(new Actions(Actions.SELECT_PREVIOUS_ROW_EXTEND)); + map.put(new Actions(Actions.SELECT_PREVIOUS_ROW_CHANGE_LEAD)); + map.put(new Actions(Actions.SELECT_NEXT_ROW)); + map.put(new Actions(Actions.SELECT_NEXT_ROW_EXTEND)); + map.put(new Actions(Actions.SELECT_NEXT_ROW_CHANGE_LEAD)); + map.put(new Actions(Actions.SELECT_FIRST_ROW)); + map.put(new Actions(Actions.SELECT_FIRST_ROW_EXTEND)); + map.put(new Actions(Actions.SELECT_FIRST_ROW_CHANGE_LEAD)); + map.put(new Actions(Actions.SELECT_LAST_ROW)); + map.put(new Actions(Actions.SELECT_LAST_ROW_EXTEND)); + map.put(new Actions(Actions.SELECT_LAST_ROW_CHANGE_LEAD)); + map.put(new Actions(Actions.SCROLL_UP)); + map.put(new Actions(Actions.SCROLL_UP_EXTEND)); + map.put(new Actions(Actions.SCROLL_UP_CHANGE_LEAD)); + map.put(new Actions(Actions.SCROLL_DOWN)); + map.put(new Actions(Actions.SCROLL_DOWN_EXTEND)); + map.put(new Actions(Actions.SCROLL_DOWN_CHANGE_LEAD)); + map.put(new Actions(Actions.SELECT_ALL)); + map.put(new Actions(Actions.CLEAR_SELECTION)); + map.put(new Actions(Actions.ADD_TO_SELECTION)); + map.put(new Actions(Actions.TOGGLE_AND_ANCHOR)); + map.put(new Actions(Actions.EXTEND_TO)); + map.put(new Actions(Actions.MOVE_SELECTION_TO)); + + map.put(TransferHandler.getCutAction().getValue(Action.NAME), + TransferHandler.getCutAction()); + map.put(TransferHandler.getCopyAction().getValue(Action.NAME), + TransferHandler.getCopyAction()); + map.put(TransferHandler.getPasteAction().getValue(Action.NAME), + TransferHandler.getPasteAction()); + } + /** * Registers the keyboard bindings on the JList that the * BasicListUI is associated with. This method is called at @@ -467,7 +510,7 @@ public class DarkListUIBridge extends BasicListUI { protected void installKeyboardActions() { InputMap inputMap = getInputMap(JComponent.WHEN_FOCUSED); SwingUtilities.replaceUIInputMap(list, JComponent.WHEN_FOCUSED, inputMap); - LazyActionMap.installLazyActionMap(list, BasicListUI.class, "List.actionMap"); + LazyActionMap.installLazyActionMap(list, DarkListUIBridge.class, "List.actionMap"); } /** @@ -2227,4 +2270,599 @@ public class DarkListUIBridge extends BasicListUI { repaintCellFocus(); } } + + private static class Actions extends UIAction { + private static final String SELECT_PREVIOUS_COLUMN = + "selectPreviousColumn"; + private static final String SELECT_PREVIOUS_COLUMN_EXTEND = + "selectPreviousColumnExtendSelection"; + private static final String SELECT_PREVIOUS_COLUMN_CHANGE_LEAD = + "selectPreviousColumnChangeLead"; + private static final String SELECT_NEXT_COLUMN = "selectNextColumn"; + private static final String SELECT_NEXT_COLUMN_EXTEND = + "selectNextColumnExtendSelection"; + private static final String SELECT_NEXT_COLUMN_CHANGE_LEAD = + "selectNextColumnChangeLead"; + private static final String SELECT_PREVIOUS_ROW = "selectPreviousRow"; + private static final String SELECT_PREVIOUS_ROW_EXTEND = + "selectPreviousRowExtendSelection"; + private static final String SELECT_PREVIOUS_ROW_CHANGE_LEAD = + "selectPreviousRowChangeLead"; + private static final String SELECT_NEXT_ROW = "selectNextRow"; + private static final String SELECT_NEXT_ROW_EXTEND = + "selectNextRowExtendSelection"; + private static final String SELECT_NEXT_ROW_CHANGE_LEAD = + "selectNextRowChangeLead"; + private static final String SELECT_FIRST_ROW = "selectFirstRow"; + private static final String SELECT_FIRST_ROW_EXTEND = + "selectFirstRowExtendSelection"; + private static final String SELECT_FIRST_ROW_CHANGE_LEAD = + "selectFirstRowChangeLead"; + private static final String SELECT_LAST_ROW = "selectLastRow"; + private static final String SELECT_LAST_ROW_EXTEND = + "selectLastRowExtendSelection"; + private static final String SELECT_LAST_ROW_CHANGE_LEAD = + "selectLastRowChangeLead"; + private static final String SCROLL_UP = "scrollUp"; + private static final String SCROLL_UP_EXTEND = + "scrollUpExtendSelection"; + private static final String SCROLL_UP_CHANGE_LEAD = + "scrollUpChangeLead"; + private static final String SCROLL_DOWN = "scrollDown"; + private static final String SCROLL_DOWN_EXTEND = + "scrollDownExtendSelection"; + private static final String SCROLL_DOWN_CHANGE_LEAD = + "scrollDownChangeLead"; + private static final String SELECT_ALL = "selectAll"; + private static final String CLEAR_SELECTION = "clearSelection"; + + // add the lead item to the selection without changing lead or anchor + private static final String ADD_TO_SELECTION = "addToSelection"; + + // toggle the selected state of the lead item and move the anchor to it + private static final String TOGGLE_AND_ANCHOR = "toggleAndAnchor"; + + // extend the selection to the lead item + private static final String EXTEND_TO = "extendTo"; + + // move the anchor to the lead and ensure only that item is selected + private static final String MOVE_SELECTION_TO = "moveSelectionTo"; + + Actions(final String name) { + super(name); + } + + public void actionPerformed(final ActionEvent e) { + String name = getName(); + @SuppressWarnings("unchecked") + JList list = (JList) e.getSource(); + DarkListUIBridge ui = (DarkListUIBridge) DarkUIUtil.getUIOfType(list.getUI(), DarkListUIBridge.class); + + if (Objects.equals(name, SELECT_PREVIOUS_COLUMN)) { + changeSelection(list, CHANGE_SELECTION, + getNextColumnIndex(list, ui, -1), -1); + } else if (Objects.equals(name, SELECT_PREVIOUS_COLUMN_EXTEND)) { + changeSelection(list, EXTEND_SELECTION, + getNextColumnIndex(list, ui, -1), -1); + } else if (Objects.equals(name, SELECT_PREVIOUS_COLUMN_CHANGE_LEAD)) { + changeSelection(list, CHANGE_LEAD, + getNextColumnIndex(list, ui, -1), -1); + } else if (Objects.equals(name, SELECT_NEXT_COLUMN)) { + changeSelection(list, CHANGE_SELECTION, + getNextColumnIndex(list, ui, 1), 1); + } else if (Objects.equals(name, SELECT_NEXT_COLUMN_EXTEND)) { + changeSelection(list, EXTEND_SELECTION, + getNextColumnIndex(list, ui, 1), 1); + } else if (Objects.equals(name, SELECT_NEXT_COLUMN_CHANGE_LEAD)) { + changeSelection(list, CHANGE_LEAD, + getNextColumnIndex(list, ui, 1), 1); + } else if (Objects.equals(name, SELECT_PREVIOUS_ROW)) { + changeSelection(list, CHANGE_SELECTION, + getNextIndex(list, ui, -1), -1); + } else if (Objects.equals(name, SELECT_PREVIOUS_ROW_EXTEND)) { + changeSelection(list, EXTEND_SELECTION, + getNextIndex(list, ui, -1), -1); + } else if (Objects.equals(name, SELECT_PREVIOUS_ROW_CHANGE_LEAD)) { + changeSelection(list, CHANGE_LEAD, + getNextIndex(list, ui, -1), -1); + } else if (Objects.equals(name, SELECT_NEXT_ROW)) { + changeSelection(list, CHANGE_SELECTION, + getNextIndex(list, ui, 1), 1); + } else if (Objects.equals(name, SELECT_NEXT_ROW_EXTEND)) { + changeSelection(list, EXTEND_SELECTION, + getNextIndex(list, ui, 1), 1); + } else if (Objects.equals(name, SELECT_NEXT_ROW_CHANGE_LEAD)) { + changeSelection(list, CHANGE_LEAD, + getNextIndex(list, ui, 1), 1); + } else if (Objects.equals(name, SELECT_FIRST_ROW)) { + changeSelection(list, CHANGE_SELECTION, 0, -1); + } else if (Objects.equals(name, SELECT_FIRST_ROW_EXTEND)) { + changeSelection(list, EXTEND_SELECTION, 0, -1); + } else if (Objects.equals(name, SELECT_FIRST_ROW_CHANGE_LEAD)) { + changeSelection(list, CHANGE_LEAD, 0, -1); + } else if (Objects.equals(name, SELECT_LAST_ROW)) { + changeSelection(list, CHANGE_SELECTION, + list.getModel().getSize() - 1, 1); + } else if (Objects.equals(name, SELECT_LAST_ROW_EXTEND)) { + changeSelection(list, EXTEND_SELECTION, + list.getModel().getSize() - 1, 1); + } else if (Objects.equals(name, SELECT_LAST_ROW_CHANGE_LEAD)) { + changeSelection(list, CHANGE_LEAD, + list.getModel().getSize() - 1, 1); + } else if (Objects.equals(name, SCROLL_UP)) { + changeSelection(list, CHANGE_SELECTION, + getNextPageIndex(list, -1), -1); + } else if (Objects.equals(name, SCROLL_UP_EXTEND)) { + changeSelection(list, EXTEND_SELECTION, + getNextPageIndex(list, -1), -1); + } else if (Objects.equals(name, SCROLL_UP_CHANGE_LEAD)) { + changeSelection(list, CHANGE_LEAD, + getNextPageIndex(list, -1), -1); + } else if (Objects.equals(name, SCROLL_DOWN)) { + changeSelection(list, CHANGE_SELECTION, + getNextPageIndex(list, 1), 1); + } else if (Objects.equals(name, SCROLL_DOWN_EXTEND)) { + changeSelection(list, EXTEND_SELECTION, + getNextPageIndex(list, 1), 1); + } else if (Objects.equals(name, SCROLL_DOWN_CHANGE_LEAD)) { + changeSelection(list, CHANGE_LEAD, + getNextPageIndex(list, 1), 1); + } else if (Objects.equals(name, SELECT_ALL)) { + selectAll(list); + } else if (Objects.equals(name, CLEAR_SELECTION)) { + clearSelection(list); + } else if (Objects.equals(name, ADD_TO_SELECTION)) { + int index = adjustIndex( + list.getSelectionModel().getLeadSelectionIndex(), list); + + if (!list.isSelectedIndex(index)) { + int oldAnchor = list.getSelectionModel().getAnchorSelectionIndex(); + list.setValueIsAdjusting(true); + list.addSelectionInterval(index, index); + list.getSelectionModel().setAnchorSelectionIndex(oldAnchor); + list.setValueIsAdjusting(false); + } + } else if (Objects.equals(name, TOGGLE_AND_ANCHOR)) { + int index = adjustIndex( + list.getSelectionModel().getLeadSelectionIndex(), list); + + if (list.isSelectedIndex(index)) { + list.removeSelectionInterval(index, index); + } else { + list.addSelectionInterval(index, index); + } + } else if (Objects.equals(name, EXTEND_TO)) { + changeSelection( + list, EXTEND_SELECTION, + adjustIndex(list.getSelectionModel().getLeadSelectionIndex(), list), + 0); + } else if (Objects.equals(name, MOVE_SELECTION_TO)) { + changeSelection( + list, CHANGE_SELECTION, + adjustIndex(list.getSelectionModel().getLeadSelectionIndex(), list), + 0); + } + } + + private void changeSelection(final JList list, int type, + final int index, final int direction) { + if (index >= 0 && index < list.getModel().getSize()) { + ListSelectionModel lsm = list.getSelectionModel(); + + // CHANGE_LEAD is only valid with multiple interval selection + if (type == CHANGE_LEAD && + list.getSelectionMode() + != ListSelectionModel.MULTIPLE_INTERVAL_SELECTION) { + + type = CHANGE_SELECTION; + } + + // IMPORTANT - This needs to happen before the index is changed. + // This is because JFileChooser, which uses JList, also scrolls + // the selected item into view. If that happens first, then + // this method becomes a no-op. + adjustScrollPositionIfNecessary(list, index, direction); + + if (type == EXTEND_SELECTION) { + int anchor = adjustIndex(lsm.getAnchorSelectionIndex(), list); + if (anchor == -1) { + anchor = 0; + } + + list.setSelectionInterval(anchor, index); + } else if (type == CHANGE_SELECTION) { + list.setSelectedIndex(index); + } else { + // casting should be safe since the action is only enabled + // for DefaultListSelectionModel + ((DefaultListSelectionModel) lsm).moveLeadSelectionIndex(index); + } + } + } + + private int getNextColumnIndex(final JList list, final DarkListUIBridge ui, + final int amount) { + if (list.getLayoutOrientation() != JList.VERTICAL) { + int index = adjustIndex(list.getLeadSelectionIndex(), list); + int size = list.getModel().getSize(); + + if (index == -1) { + return 0; + } else if (size == 1) { + // there's only one item so we should select it + return 0; + } else if (ui == null || ui.columnCount <= 1) { + return -1; + } + + int column = ui.convertModelToColumn(index); + int row = ui.convertModelToRow(index); + + column += amount; + if (column >= ui.columnCount || column < 0) { + // No wrapping. + return -1; + } + int maxRowCount = ui.getRowCount(column); + if (row >= maxRowCount) { + return -1; + } + return ui.getModelIndex(column, row); + } + // Won't change the selection. + return -1; + } + + private int getNextIndex(final JList list, final DarkListUIBridge ui, final int amount) { + int index = adjustIndex(list.getLeadSelectionIndex(), list); + int size = list.getModel().getSize(); + + if (index == -1) { + if (size > 0) { + if (amount > 0) { + index = 0; + } else { + index = size - 1; + } + } + } else if (size == 1) { + // there's only one item so we should select it + index = 0; + } else if (list.getLayoutOrientation() == JList.HORIZONTAL_WRAP) { + if (ui != null) { + index += ui.columnCount * amount; + } + } else { + index += amount; + } + + return index; + } + + private int getNextPageIndex(final JList list, int direction) { + if (list.getModel().getSize() == 0) { + return -1; + } + + int index = -1; + Rectangle visRect = list.getVisibleRect(); + ListSelectionModel lsm = list.getSelectionModel(); + int lead = adjustIndex(lsm.getLeadSelectionIndex(), list); + Rectangle leadRect = + (lead == -1) ? new Rectangle() : list.getCellBounds(lead, lead); + + if (leadRect == null) { + return index; + } + if (list.getLayoutOrientation() == JList.VERTICAL_WRAP && + list.getVisibleRowCount() <= 0) { + if (!list.getComponentOrientation().isLeftToRight()) { + direction = -direction; + } + // apply for horizontal scrolling: the step for next + // page index is number of visible columns + if (direction < 0) { + // left + visRect.x = leadRect.x + leadRect.width - visRect.width; + Point p = new Point(visRect.x - 1, leadRect.y); + index = list.locationToIndex(p); + if (index == -1) { + return index; + } + Rectangle cellBounds = list.getCellBounds(index, index); + if (cellBounds != null && visRect.intersects(cellBounds)) { + p.x = cellBounds.x - 1; + index = list.locationToIndex(p); + if (index == -1) { + return index; + } + cellBounds = list.getCellBounds(index, index); + } + // this is necessary for right-to-left orientation only + if (cellBounds != null && cellBounds.y != leadRect.y) { + p.x = cellBounds.x + cellBounds.width; + index = list.locationToIndex(p); + } + } else { + // right + visRect.x = leadRect.x; + Point p = new Point(visRect.x + visRect.width, leadRect.y); + index = list.locationToIndex(p); + if (index == -1) { + return index; + } + Rectangle cellBounds = list.getCellBounds(index, index); + if (cellBounds != null && visRect.intersects(cellBounds)) { + p.x = cellBounds.x + cellBounds.width; + index = list.locationToIndex(p); + if (index == -1) { + return index; + } + cellBounds = list.getCellBounds(index, index); + } + if (cellBounds != null && cellBounds.y != leadRect.y) { + p.x = cellBounds.x - 1; + index = list.locationToIndex(p); + } + } + } else { + if (direction < 0) { + // up + // go to the first visible cell + Point p = new Point(leadRect.x, visRect.y); + index = list.locationToIndex(p); + if (lead <= index) { + // if lead is the first visible cell (or above it) + // adjust the visible rect up + visRect.y = leadRect.y + leadRect.height - visRect.height; + p.y = visRect.y; + index = list.locationToIndex(p); + if (index == -1) { + return index; + } + Rectangle cellBounds = list.getCellBounds(index, index); + // go one cell down if first visible cell doesn't fit + // into adjasted visible rectangle + if (cellBounds != null && cellBounds.y < visRect.y) { + p.y = cellBounds.y + cellBounds.height; + index = list.locationToIndex(p); + if (index == -1) { + return index; + } + cellBounds = list.getCellBounds(index, index); + } + // if index isn't less then lead + // try to go to cell previous to lead + if (cellBounds != null && cellBounds.y >= leadRect.y) { + p.y = leadRect.y - 1; + index = list.locationToIndex(p); + } + } + } else { + // down + // go to the last completely visible cell + Point p = new Point(leadRect.x, + visRect.y + visRect.height - 1); + index = list.locationToIndex(p); + if (index == -1) { + return index; + } + Rectangle cellBounds = list.getCellBounds(index, index); + // go up one cell if last visible cell doesn't fit + // into visible rectangle + if (cellBounds != null && + cellBounds.y + cellBounds.height > + visRect.y + visRect.height) { + p.y = cellBounds.y - 1; + index = list.locationToIndex(p); + if (index == -1) { + return index; + } + cellBounds = list.getCellBounds(index, index); + index = Math.max(index, lead); + } + + if (lead >= index) { + // if lead is the last completely visible index + // (or below it) adjust the visible rect down + visRect.y = leadRect.y; + p.y = visRect.y + visRect.height - 1; + index = list.locationToIndex(p); + if (index == -1) { + return index; + } + cellBounds = list.getCellBounds(index, index); + // go one cell up if last visible cell doesn't fit + // into adjasted visible rectangle + if (cellBounds != null && + cellBounds.y + cellBounds.height > + visRect.y + visRect.height) { + p.y = cellBounds.y - 1; + index = list.locationToIndex(p); + if (index == -1) { + return index; + } + cellBounds = list.getCellBounds(index, index); + } + // if index isn't greater then lead + // try to go to cell next after lead + if (cellBounds != null && cellBounds.y <= leadRect.y) { + p.y = leadRect.y + leadRect.height; + index = list.locationToIndex(p); + } + } + } + } + + return index; + } + + private void selectAll(final JList list) { + int size = list.getModel().getSize(); + if (size > 0) { + ListSelectionModel lsm = list.getSelectionModel(); + int lead = adjustIndex(lsm.getLeadSelectionIndex(), list); + + if (lsm.getSelectionMode() == ListSelectionModel.SINGLE_SELECTION) { + if (lead == -1) { + int min = adjustIndex(list.getMinSelectionIndex(), list); + lead = (min == -1 ? 0 : min); + } + + list.setSelectionInterval(lead, lead); + list.ensureIndexIsVisible(lead); + } else { + list.setValueIsAdjusting(true); + + int anchor = adjustIndex(lsm.getAnchorSelectionIndex(), list); + + list.setSelectionInterval(0, size - 1); + + // this is done to restore the anchor and lead + SwingUtilities2.setLeadAnchorWithoutSelection(lsm, anchor, lead); + + list.setValueIsAdjusting(false); + } + } + } + + private void clearSelection(final JList list) { + list.clearSelection(); + } + + /** + * When scroll down makes selected index the last completely visible index. When scroll up makes selected index + * the first visible index. Adjust visible rectangle respect to list's component orientation. + */ + private void adjustScrollPositionIfNecessary(final JList list, final int index, + final int direction) { + if (direction == 0) { + return; + } + Rectangle cellBounds = list.getCellBounds(index, index); + Rectangle visRect = list.getVisibleRect(); + if (cellBounds != null && !visRect.contains(cellBounds)) { + if (list.getLayoutOrientation() == JList.VERTICAL_WRAP && + list.getVisibleRowCount() <= 0) { + // horizontal + if (list.getComponentOrientation().isLeftToRight()) { + if (direction > 0) { + // right for left-to-right + int x = Math.max(0, + cellBounds.x + cellBounds.width - visRect.width); + int startIndex = + list.locationToIndex(new Point(x, cellBounds.y)); + if (startIndex == -1) { + return; + } + Rectangle startRect = list.getCellBounds(startIndex, + startIndex); + if (startRect != null && + startRect.x < x && startRect.x < cellBounds.x) { + startRect.x += startRect.width; + startIndex = + list.locationToIndex(startRect.getLocation()); + if (startIndex == -1) { + return; + } + startRect = list.getCellBounds(startIndex, + startIndex); + } + cellBounds = startRect; + } + if (cellBounds != null) { + cellBounds.width = visRect.width; + } + } else { + if (direction > 0) { + // left for right-to-left + int x = cellBounds.x + visRect.width; + int rightIndex = + list.locationToIndex(new Point(x, cellBounds.y)); + if (rightIndex == -1) { + return; + } + Rectangle rightRect = list.getCellBounds(rightIndex, + rightIndex); + if (rightRect != null) { + if (rightRect.x + rightRect.width > x && + rightRect.x > cellBounds.x) { + rightRect.width = 0; + } + cellBounds.x = Math.max(0, + rightRect.x + rightRect.width - visRect.width); + cellBounds.width = visRect.width; + } + } else { + cellBounds.x += Math.max(0, + cellBounds.width - visRect.width); + // adjust width to fit into visible rectangle + cellBounds.width = Math.min(cellBounds.width, + visRect.width); + } + } + } else { + // vertical + if (direction > 0 && + (cellBounds.y < visRect.y || + cellBounds.y + cellBounds.height + > visRect.y + visRect.height)) { + //down + int y = Math.max(0, + cellBounds.y + cellBounds.height - visRect.height); + int startIndex = + list.locationToIndex(new Point(cellBounds.x, y)); + if (startIndex == -1) { + return; + } + Rectangle startRect = list.getCellBounds(startIndex, + startIndex); + if (startRect != null && + startRect.y < y && startRect.y < cellBounds.y) { + startRect.y += startRect.height; + startIndex = + list.locationToIndex(startRect.getLocation()); + if (startIndex == -1) { + return; + } + startRect = + list.getCellBounds(startIndex, startIndex); + } + cellBounds = startRect; + if (cellBounds != null) { + cellBounds.height = visRect.height; + } + } else { + // adjust height to fit into visible rectangle + cellBounds.height = Math.min(cellBounds.height, visRect.height); + } + } + if (cellBounds != null) { + list.scrollRectToVisible(cellBounds); + } + } + } + + @Override + public boolean accept(final Object c) { + Object name = getName(); + if (name == SELECT_PREVIOUS_COLUMN_CHANGE_LEAD || + name == SELECT_NEXT_COLUMN_CHANGE_LEAD || + name == SELECT_PREVIOUS_ROW_CHANGE_LEAD || + name == SELECT_NEXT_ROW_CHANGE_LEAD || + name == SELECT_FIRST_ROW_CHANGE_LEAD || + name == SELECT_LAST_ROW_CHANGE_LEAD || + name == SCROLL_UP_CHANGE_LEAD || + name == SCROLL_DOWN_CHANGE_LEAD) { + + // discontinuous selection actions are only enabled for + // DefaultListSelectionModel + return c != null && ((JList) c).getSelectionModel() + instanceof DefaultListSelectionModel; + } + + return true; + } + } + } diff --git a/src/main/java/com/github/weisj/darklaf/ui/text/StyleConstantsEx.java b/src/main/java/com/github/weisj/darklaf/ui/text/StyleConstantsEx.java new file mode 100644 index 00000000..d83e4862 --- /dev/null +++ b/src/main/java/com/github/weisj/darklaf/ui/text/StyleConstantsEx.java @@ -0,0 +1,28 @@ +/* + * 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.text; + +public class StyleConstantsEx { + public static final Object SelectedForeground = "selectedForeground"; +} diff --git a/src/main/java/javax/swing/text/DefaultHighlighterDark/DarkHighlightPainter.java b/src/main/java/javax/swing/text/DefaultHighlighterDark/DarkHighlightPainter.java index 4927e895..e9d8d921 100644 --- a/src/main/java/javax/swing/text/DefaultHighlighterDark/DarkHighlightPainter.java +++ b/src/main/java/javax/swing/text/DefaultHighlighterDark/DarkHighlightPainter.java @@ -23,6 +23,7 @@ */ package javax.swing.text.DefaultHighlighterDark; +import com.github.weisj.darklaf.ui.text.StyleConstantsEx; import com.github.weisj.darklaf.util.GraphicsContext; import com.github.weisj.darklaf.util.GraphicsUtil; import org.jetbrains.annotations.Contract; @@ -51,8 +52,9 @@ import java.awt.geom.Rectangle2D; */ public class DarkHighlightPainter extends DefaultHighlighter.DefaultHighlightPainter { - private static final boolean DEBUG_COLOR = true; + private static final boolean DEBUG_COLOR = false; private Paint paint; + private Color color; private boolean roundedEdges; private AlphaComposite alphaComposite; private float alpha; @@ -94,6 +96,11 @@ public class DarkHighlightPainter extends DefaultHighlighter.DefaultHighlightPai } + @Override + public Color getColor() { + return color; + } + /** * Paints a highlight. * @@ -109,6 +116,7 @@ public class DarkHighlightPainter extends DefaultHighlighter.DefaultHighlightPai Rectangle alloc = bounds.getBounds(); Graphics2D g2d = (Graphics2D) g; GraphicsContext context = new GraphicsContext(g2d); + color = c.getSelectedTextColor(); if (getAlpha() < 1.0f) { g2d.setComposite(getAlphaComposite()); @@ -175,7 +183,11 @@ public class DarkHighlightPainter extends DefaultHighlighter.DefaultHighlightPai @Override public Shape paintLayer(final Graphics g, final int offs0, final int offs1, - final Shape bounds, final JTextComponent c, final View view) { + final Shape bounds, final JTextComponent c, @NotNull final View view) { + color = (Color) view.getAttributes().getAttribute(StyleConstantsEx.SelectedForeground); + if (color == null) { + color = c.getSelectedTextColor(); + } Shape dirtyShape = null; Graphics2D g2d = (Graphics2D) g; GraphicsContext context = GraphicsUtil.setupAAPainting(g2d); @@ -440,7 +452,7 @@ public class DarkHighlightPainter extends DefaultHighlighter.DefaultHighlightPai private void paintRoundedLeftRight(final Graphics g, final boolean left, final boolean right, final Rectangle r) { if (right || left) { g.fillRoundRect(r.x, r.y, r.width, r.height, arcSize, arcSize); - g.setColor(Color.PINK); + if (DEBUG_COLOR) g.setColor(Color.PINK); if (!left) { g.fillRect(r.x, r.y, arcSize, r.height); } @@ -454,7 +466,6 @@ public class DarkHighlightPainter extends DefaultHighlighter.DefaultHighlightPai private void paintStartArc(@NotNull final Graphics2D g2d, @NotNull final Rectangle r) { if (DEBUG_COLOR) g2d.setColor(Color.PINK); - Area arc = new Area(new Rectangle2D.Double( r.x - arcSize + 0.25, r.y + r.height - arcSize + 0.25, arcSize, arcSize)); arc.subtract(new Area(new Arc2D.Double( @@ -468,7 +479,6 @@ public class DarkHighlightPainter extends DefaultHighlighter.DefaultHighlightPai private void paintEndArc(@NotNull final Graphics2D g2d, @NotNull final Rectangle r) { if (DEBUG_COLOR) g2d.setColor(Color.PINK); - Area arc = new Area(new Rectangle2D.Double( r.x + r.width - 0.25, r.y - 0.25, arcSize, arcSize)); arc.subtract(new Area(new Arc2D.Double(