简体   繁体   English

如何使用 Javafx KeyCombination 覆盖系统默认键盘快捷键,如 Ctrl+C、Ctrl+V?

[英]how to overwrite system default keyboard shortcuts like Ctrl+C, Ctrl+V by using Javafx KeyCombination?

While implementing undo/redo mechanism, I have written below code to add paste operation to my own undo stack.在实现撤消/重做机制时,我编写了下面的代码来将粘贴操作添加到我自己的撤消堆栈中。 If I click on menu item it's executing event handling code, but if I use shortcut Ctrl+V my event handler is not executing.如果我单击菜单项,它正在执行事件处理代码,但如果我使用快捷键 Ctrl+V,我的事件处理程序不会执行。

For more information see the below code.有关更多信息,请参阅下面的代码。

在此处输入图像描述

        MenuItem paste = new MenuItem("Paste");

        KeyCombination pasteKeyCombination = new KeyCodeCombination(KeyCode.V,KeyCombination.CONTROL_DOWN);

        paste.setAccelerator(pasteKeyCombination);

        paste.setOnAction(event -> {
            System.out.println("Ctrl+V triggered.");
            if(clipboard.hasString()){
                String pastedText = clipboard.getString();
                InsertCommand insertCommand = new InsertCommand(textArea.getCaretPosition(),pastedText,textArea);
                insertCommand.execute();
                UndoRedo.insertIntoUndoStack(insertCommand);
            }
        });

If I use different KeyCode like Ctrl+J it's working fine but not for Ctrl+V.如果我使用不同的 KeyCode,如 Ctrl+J,它可以正常工作,但不适用于 Ctrl+V。

Note: When I use Ctrl+V it seems directly pasting data from system clipboard instead of executing my code.注意:当我使用 Ctrl+V 时,它似乎直接从系统剪贴板粘贴数据,而不是执行我的代码。

Can anyone please suggest the solution for this problem?谁能建议这个问题的解决方案? and please explain why it's working with Ctrl+J and why not Ctrl+V?请解释为什么它可以与 Ctrl+J 一起使用,为什么不使用 Ctrl+V?

The issue exists for all cut, copy, paste shortcuts.所有剪切、复制、粘贴快捷方式都存在此问题。

The problem is the behavior of TextArea (which is an internal class) adds a number of EventHandler s which handle various user-generated events (eg key presses).问题是TextArea (这是一个内部类)的行为添加了许多EventHandler来处理各种用户生成的事件(例如按键)。 This includes the common shortcuts used for cut, copy, paste, undo, redo, select all, and so on.这包括用于剪切、复制、粘贴、撤消、重做、select all 等的常用快捷键。 These handlers then consume the event which stops said event's propagation.这些处理程序然后使用停止所述事件传播的事件。 Since menu item accelerators only work when the event bubbles back up to the Scene , the event being consumed by the TextArea behavior means your menu items don't fire.由于菜单项加速器仅在事件回升到Scene时才起作用,因此TextArea行为所消耗的事件意味着您的菜单项不会触发。

One workaround is to use a custom EventDispatcher on the TextArea to filter out any key events which match any of a number of key combinations.一种解决方法是在TextArea上使用自定义EventDispatcher来过滤掉与多个键组合中的任何一个匹配的任何键事件。 All other events are allowed to proceed normally, leaving the rest of the behavior intact.允许所有其他事件正常进行,而行为完好无损的 rest。 This works by preventing the event from reaching the TextArea and letting the event enter the bubbling phase of event propagation, ultimately letting the event bubble back up the scene graph.这是通过阻止事件到达TextArea并让事件进入事件传播的冒泡阶段来实现的,最终让事件冒泡备份场景图。 That last bit is why using an EventDispatcher is necessary instead of an event filter;最后一点是为什么需要使用EventDispatcher而不是事件过滤器的原因; consuming an event in an event filter will stop the event from reaching the TextArea but won't let it bubble back up the scene graph.在事件过滤器中使用事件将阻止事件到达TextArea ,但不会让它在场景图中冒泡。

Here's an example:这是一个例子:

import java.util.Set;
import javafx.application.Application;
import javafx.event.Event;
import javafx.event.EventDispatchChain;
import javafx.event.EventDispatcher;
import javafx.scene.Scene;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuBar;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TextArea;
import javafx.scene.input.KeyCombination;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class Main extends Application {

  @Override
  public void start(Stage primaryStage) {
    MenuItem cutItem = new MenuItem("Cut");
    cutItem.setAccelerator(KeyCombination.valueOf("shortcut+x"));
    cutItem.setOnAction(e -> System.out.println("CUT"));

    MenuItem copyItem = new MenuItem("Copy");
    copyItem.setAccelerator(KeyCombination.valueOf("shortcut+c"));
    copyItem.setOnAction(e -> System.out.println("COPY"));

    MenuItem pasteItem = new MenuItem("Paste");
    pasteItem.setAccelerator(KeyCombination.valueOf("shortcut+v"));
    pasteItem.setOnAction(e -> System.out.println("PASTE"));

    TextArea area = new TextArea();
    area.setEventDispatcher(
        new FilteringEventDispatcher(
            area.getEventDispatcher(),
            cutItem.getAccelerator(),
            copyItem.getAccelerator(),
            pasteItem.getAccelerator()));

    VBox root = new VBox(new MenuBar(new Menu("Edit", null, cutItem, copyItem, pasteItem)), area);
    VBox.setVgrow(area, Priority.ALWAYS);

    primaryStage.setScene(new Scene(root, 600, 400));
    primaryStage.show();
  }

  private static class FilteringEventDispatcher implements EventDispatcher {

    private final EventDispatcher delegate;
    private final Set<KeyCombination> blacklistedCombos;

    public FilteringEventDispatcher(EventDispatcher delegate, KeyCombination... blacklistedCombos) {
      this.delegate = delegate;
      // Set.of was added in Java 9
      this.blacklistedCombos = Set.of(blacklistedCombos);
    }

    @Override
    public Event dispatchEvent(Event event, EventDispatchChain tail) {
      if (!(event instanceof KeyEvent) || isPermitted((KeyEvent) event)) {
        return delegate.dispatchEvent(event, tail); // forward event to TextArea
      }
      return event; // skip TextArea and enter the bubbling phase
    }

    private boolean isPermitted(KeyEvent event) {
      return blacklistedCombos.stream().noneMatch(combo -> combo.match(event));
    }
  }
}

If necessary, you can filter more specifically by testing the EventType of the event (eg only filter KEY_PRESSED events).如有必要,您可以通过测试事件的EventType来更具体地过滤(例如仅过滤KEY_PRESSED事件)。 You could also whitelist combos instead of blacklisting them, depending on your needs.根据您的需要,您还可以将组合列入白名单而不是将它们列入黑名单。

As I couldn't find proper solution, I have tried some work around for now by adding onKeyPress event handler and it's executing the handler implementation even for Ctrl+C,Ctrl+V etc.由于我找不到合适的解决方案,我现在通过添加 onKeyPress 事件处理程序尝试了一些工作,并且它正在执行处理程序实现,即使是 Ctrl+C、Ctrl+V 等。

        MenuItem paste = new MenuItem("Paste");
        this.textArea.addEventHandler(KeyEvent.KEY_PRESSED,event -> {
            if(event.isControlDown()&& event.getCode()==KeyCode.V){
                System.out.println("Ctrl+V is triggered");
                if(clipboard.hasString()){
                    String pastedText = clipboard.getString();
                    InsertCommand insertCommand = new InsertCommand(textArea.getCaretPosition(),pastedText,textArea);
                    insertCommand.execute();
                    UndoRedo.insertIntoUndoStack(insertCommand);
                }
            }
        });

Note: still not clear about why it's not working with KeyCombination.注意:仍然不清楚为什么它不能与 KeyCombination 一起使用。 if anyone has answer please post it.如果有人有答案,请发布。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM