diff --git a/core/src/main/java/com/github/weisj/darklaf/task/InputDefaultsInitTask.java b/core/src/main/java/com/github/weisj/darklaf/task/InputDefaultsInitTask.java index 6fa69a62..db2095dd 100644 --- a/core/src/main/java/com/github/weisj/darklaf/task/InputDefaultsInitTask.java +++ b/core/src/main/java/com/github/weisj/darklaf/task/InputDefaultsInitTask.java @@ -100,7 +100,7 @@ public class InputDefaultsInitTask implements DefaultsInitTask { inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_X, mask), DefaultEditorKit.cutAction); } - private static void patchComboBox(final UIDefaults metalDefaults, final UIDefaults defaults) { + private void patchComboBox(final UIDefaults metalDefaults, final UIDefaults defaults) { defaults.remove("ComboBox.ancestorInputMap"); defaults.remove("ComboBox.actionMap"); defaults.put("ComboBox.ancestorInputMap", metalDefaults.get("ComboBox.ancestorInputMap")); diff --git a/core/src/main/java/com/github/weisj/darklaf/ui/text/DarkCaret.java b/core/src/main/java/com/github/weisj/darklaf/ui/text/DarkCaret.java index 95c7c58e..9cb38524 100644 --- a/core/src/main/java/com/github/weisj/darklaf/ui/text/DarkCaret.java +++ b/core/src/main/java/com/github/weisj/darklaf/ui/text/DarkCaret.java @@ -51,22 +51,66 @@ public class DarkCaret extends DefaultCaret implements UIResource { private final DarkHighlightPainter selectionPainter; private MouseEvent selectedWordEvent; private CaretStyle style; + private CaretStyle insertStyle; private boolean alwaysVisible; private boolean pasteOnMiddleMouseClick; + private boolean insertMode; + private boolean deleteCharMode; + public DarkCaret() { - this(CaretStyle.THICK_VERTICAL_LINE_STYLE); + this(null, null); } - public DarkCaret(final CaretStyle style) { + public DarkCaret(final CaretStyle style, final CaretStyle insertStyle) { seg = new Segment(); - setStyle(style); + setStyles(style, insertStyle); selectionPainter = new DarkHighlightPainter(); selectLine = new SelectLineAction(); selectWord = new SelectWordAction(); pasteOnMiddleMouseClick = true; } + public boolean isInsertMode() { + return insertMode; + } + + public void setInsertMode(final boolean insertMode) { + if (this.insertMode != insertMode) { + this.insertMode = insertMode; + repaint(); + } + } + + public void setDeleteCharMode(final boolean deleteCharMode) { + this.deleteCharMode = deleteCharMode; + } + + @Override + public int getMark() { + int mark = super.getMark(); + int dot = super.getDot(); + JTextComponent target = getComponent(); + if (isInsertMode() + && target != null + && mark == dot + && !deleteCharMode + && !isEndOfLine(target, dot)) { + mark += 1; + } + return mark; + } + + private boolean isEndOfLine(final JTextComponent target, final int dot) { + Document doc = target.getDocument(); + if (dot >= doc.getLength()) return true; + try { + return target.getText(dot, 1).equals("\n"); + } catch (BadLocationException e) { + return false; + } + } + public boolean getRoundedSelectionEdges() { return ((DarkHighlightPainter) getSelectionPainter()).getRoundedEdges(); } @@ -76,16 +120,21 @@ public class DarkCaret extends DefaultCaret implements UIResource { } public CaretStyle getStyle() { - return style; + return isInsertMode() ? insertStyle : style; } - public void setStyle(final CaretStyle style) { + public void setStyles(final CaretStyle style, final CaretStyle insertStyle) { CaretStyle s = style; + CaretStyle is = insertStyle; if (s == null) { s = CaretStyle.THICK_VERTICAL_LINE_STYLE; } - if (s != this.style) { + if (is == null) { + is = CaretStyle.BLOCK_BORDER_STYLE; + } + if (s != this.style || is != this.insertStyle) { this.style = s; + this.insertStyle = is; repaint(); } } @@ -305,18 +354,18 @@ public class DarkCaret extends DefaultCaret implements UIResource { if (textAreaBg == null) { textAreaBg = Color.white; } - switch (style) { + switch (getStyle()) { case BLOCK_STYLE : g.setXORMode(textAreaBg); g.fillRect(r.x, r.y, r.width, r.height); break; case BLOCK_BORDER_STYLE : - PaintUtil.drawRect(g, r.x, r.y, r.width, r.height, style.getSize()); + PaintUtil.drawRect(g, r.x, r.y, r.width, r.height, getStyle().getSize()); break; case UNDERLINE_STYLE : g.setXORMode(textAreaBg); int y = r.y + r.height; - g.fillRect(r.x, y - style.getSize(), r.width, style.getSize()); + g.fillRect(r.x, y - getStyle().getSize(), r.width, getStyle().getSize()); break; case THICK_VERTICAL_LINE_STYLE : case VERTICAL_LINE_STYLE : diff --git a/core/src/main/java/com/github/weisj/darklaf/ui/text/DarkEditorPaneUI.java b/core/src/main/java/com/github/weisj/darklaf/ui/text/DarkEditorPaneUI.java index b9de39be..3a9b7587 100644 --- a/core/src/main/java/com/github/weisj/darklaf/ui/text/DarkEditorPaneUI.java +++ b/core/src/main/java/com/github/weisj/darklaf/ui/text/DarkEditorPaneUI.java @@ -28,7 +28,6 @@ import java.awt.*; import java.beans.PropertyChangeEvent; import javax.swing.*; -import javax.swing.plaf.ActionMapUIResource; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.UIResource; import javax.swing.text.*; @@ -243,7 +242,7 @@ public class DarkEditorPaneUI extends DarkTextUI { * Fetch an action map to use. The map for a JEditorPane is not shared because it changes with the EditorKit. */ public ActionMap getActionMap() { - ActionMap am = new ActionMapUIResource(); + ActionMap am = super.getActionMap(); am.put("requestFocus", new FocusAction()); EditorKit editorKit = getEditorKit(getComponent()); if (editorKit != null) { diff --git a/core/src/main/java/com/github/weisj/darklaf/ui/text/DarkTextUI.java b/core/src/main/java/com/github/weisj/darklaf/ui/text/DarkTextUI.java index 7af8d860..4f43ecaf 100644 --- a/core/src/main/java/com/github/weisj/darklaf/ui/text/DarkTextUI.java +++ b/core/src/main/java/com/github/weisj/darklaf/ui/text/DarkTextUI.java @@ -48,6 +48,9 @@ import com.github.weisj.darklaf.graphics.PaintUtil; import com.github.weisj.darklaf.ui.list.DarkListUI; import com.github.weisj.darklaf.ui.table.DarkTableCellBorder; import com.github.weisj.darklaf.ui.table.DarkTableUI; +import com.github.weisj.darklaf.ui.text.action.DeleteNextCharAction; +import com.github.weisj.darklaf.ui.text.action.DeletePreviousCharAction; +import com.github.weisj.darklaf.ui.text.action.ToggleInsertAction; import com.github.weisj.darklaf.ui.tree.DarkTreeUI; import com.github.weisj.darklaf.util.DarkUIUtil; import com.github.weisj.darklaf.util.PropertyUtil; @@ -65,6 +68,8 @@ public abstract class DarkTextUI extends BasicTextUI implements PropertyChangeLi public static final String KEY_IS_LIST_RENDER = DarkListUI.KEY_IS_LIST_RENDERER; public static final String KEY_DEFAULT_TEXT = KEY_PREFIX + "defaultText"; + protected static final String TOGGLE_INSERT = "toggle_insert"; + protected JTextComponent editor; private FocusListener focusListener = new FocusListener() { @Override @@ -89,11 +94,15 @@ public abstract class DarkTextUI extends BasicTextUI implements PropertyChangeLi @Override protected Caret createCaret() { - return new DarkCaret(getDefaultCaretStyle()); + return new DarkCaret(getDefaultCaretStyle(), getDefaultInsertCaretStyle()); } protected abstract DarkCaret.CaretStyle getDefaultCaretStyle(); + protected DarkCaret.CaretStyle getDefaultInsertCaretStyle() { + return DarkCaret.CaretStyle.BLOCK_STYLE; + } + protected Color disabledColor; protected Color inactiveColor; @@ -342,6 +351,7 @@ public abstract class DarkTextUI extends BasicTextUI implements PropertyChangeLi if (shared != null) { map.setParent(shared); } + map.put(KeyStroke.getKeyStroke(KeyEvent.VK_INSERT, 0), TOGGLE_INSERT); return map; } @@ -366,6 +376,7 @@ public abstract class DarkTextUI extends BasicTextUI implements PropertyChangeLi * processed when the text component has focus and isn't editable. * */ + System.out.println(getEditorKit(editor)); if (getEditorKit(editor) instanceof DefaultEditorKit) { if (map != null) { Object obj = map.get(DefaultEditorKit.insertBreakAction); @@ -373,6 +384,9 @@ public abstract class DarkTextUI extends BasicTextUI implements PropertyChangeLi Action action = new TextActionWrapper((TextAction) obj); componentMap.put(action.getValue(Action.NAME), action); } + map.put(DefaultEditorKit.deletePrevCharAction, new DeletePreviousCharAction()); + map.put(DefaultEditorKit.deleteNextCharAction, new DeleteNextCharAction()); + map.put(TOGGLE_INSERT, new ToggleInsertAction()); } } if (map != null) { diff --git a/core/src/main/java/com/github/weisj/darklaf/ui/text/action/DarkTextAction.java b/core/src/main/java/com/github/weisj/darklaf/ui/text/action/DarkTextAction.java new file mode 100644 index 00000000..58380e4d --- /dev/null +++ b/core/src/main/java/com/github/weisj/darklaf/ui/text/action/DarkTextAction.java @@ -0,0 +1,51 @@ +/* + * MIT License + * + * Copyright (c) 2020 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.action; + +import javax.swing.text.Caret; +import javax.swing.text.JTextComponent; +import javax.swing.text.TextAction; + +import com.github.weisj.darklaf.ui.text.DarkCaret; + +public abstract class DarkTextAction extends TextAction { + + /** + * Creates a new JTextAction object. + * + * @param name the name of the action + */ + public DarkTextAction(final String name) { + super(name); + } + + protected void setupDeleteMode(final JTextComponent textComponent, final boolean isDeleteMode) { + if (textComponent == null) return; + Caret c = textComponent.getCaret(); + if (c instanceof DarkCaret) { + ((DarkCaret) c).setDeleteCharMode(isDeleteMode); + } + } +} diff --git a/core/src/main/java/com/github/weisj/darklaf/ui/text/action/DeleteNextCharAction.java b/core/src/main/java/com/github/weisj/darklaf/ui/text/action/DeleteNextCharAction.java new file mode 100644 index 00000000..535eff42 --- /dev/null +++ b/core/src/main/java/com/github/weisj/darklaf/ui/text/action/DeleteNextCharAction.java @@ -0,0 +1,76 @@ +/* + * MIT License + * + * Copyright (c) 2020 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.action; + +import java.awt.event.ActionEvent; + +import javax.swing.*; +import javax.swing.text.*; + +public class DeleteNextCharAction extends DarkTextAction { + + public DeleteNextCharAction() { + super(DefaultEditorKit.deleteNextCharAction); + } + + @Override + public void actionPerformed(final ActionEvent e) { + JTextComponent target = getTextComponent(e); + setupDeleteMode(target, true); + boolean beep = true; + if ((target != null) && (target.isEditable())) { + try { + Document doc = target.getDocument(); + Caret caret = target.getCaret(); + int dot = caret.getDot(); + int mark = caret.getMark(); + if (dot != mark) { + doc.remove(Math.min(dot, mark), Math.abs(dot - mark)); + beep = false; + } else if (dot < doc.getLength()) { + int delChars = 1; + + if (dot < doc.getLength() - 1) { + String dotChars = doc.getText(dot, 2); + char c0 = dotChars.charAt(0); + char c1 = dotChars.charAt(1); + + if (c0 >= '\uD800' && c0 <= '\uDBFF' && + c1 >= '\uDC00' && c1 <= '\uDFFF') { + delChars = 2; + } + } + + doc.remove(dot, delChars); + beep = false; + } + } catch (BadLocationException ignored) {} + } + if (beep) { + UIManager.getLookAndFeel().provideErrorFeedback(target); + } + setupDeleteMode(target, false); + } +} diff --git a/core/src/main/java/com/github/weisj/darklaf/ui/text/action/DeletePreviousCharAction.java b/core/src/main/java/com/github/weisj/darklaf/ui/text/action/DeletePreviousCharAction.java new file mode 100644 index 00000000..3e362b8d --- /dev/null +++ b/core/src/main/java/com/github/weisj/darklaf/ui/text/action/DeletePreviousCharAction.java @@ -0,0 +1,76 @@ +/* + * MIT License + * + * Copyright (c) 2020 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.action; + +import java.awt.event.ActionEvent; + +import javax.swing.*; +import javax.swing.text.*; + +public class DeletePreviousCharAction extends DarkTextAction { + + public DeletePreviousCharAction() { + super(DefaultEditorKit.deletePrevCharAction); + } + + @Override + public void actionPerformed(final ActionEvent e) { + JTextComponent target = getTextComponent(e); + setupDeleteMode(target, true); + boolean beep = true; + if ((target != null) && (target.isEditable())) { + try { + Document doc = target.getDocument(); + Caret caret = target.getCaret(); + int dot = caret.getDot(); + int mark = caret.getMark(); + if (dot != mark) { + doc.remove(Math.min(dot, mark), Math.abs(dot - mark)); + beep = false; + } else if (dot > 0) { + int delChars = 1; + + if (dot > 1) { + String dotChars = doc.getText(dot - 2, 2); + char c0 = dotChars.charAt(0); + char c1 = dotChars.charAt(1); + + if (c0 >= '\uD800' && c0 <= '\uDBFF' && + c1 >= '\uDC00' && c1 <= '\uDFFF') { + delChars = 2; + } + } + + doc.remove(dot - delChars, delChars); + beep = false; + } + } catch (BadLocationException ignored) {} + } + if (beep) { + UIManager.getLookAndFeel().provideErrorFeedback(target); + } + setupDeleteMode(target, false); + } +} diff --git a/core/src/main/java/com/github/weisj/darklaf/ui/text/action/ToggleInsertAction.java b/core/src/main/java/com/github/weisj/darklaf/ui/text/action/ToggleInsertAction.java new file mode 100644 index 00000000..e32498fb --- /dev/null +++ b/core/src/main/java/com/github/weisj/darklaf/ui/text/action/ToggleInsertAction.java @@ -0,0 +1,51 @@ +/* + * MIT License + * + * Copyright (c) 2020 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.action; + +import java.awt.event.ActionEvent; + +import javax.swing.text.Caret; +import javax.swing.text.JTextComponent; +import javax.swing.text.TextAction; + +import com.github.weisj.darklaf.ui.text.DarkCaret; + +public class ToggleInsertAction extends TextAction { + + public ToggleInsertAction() { + super("toggle insert mode"); + } + + @Override + public void actionPerformed(final ActionEvent e) { + JTextComponent target = getTextComponent(e); + if (target != null) { + Caret c = target.getCaret(); + if (c instanceof DarkCaret) { + ((DarkCaret) c).setInsertMode(!((DarkCaret) c).isInsertMode()); + } + } + } +}