Change display of JavaFX virtual keyboard - javafx

My development system contains of a Windows pc with three displays attached to it. The third display is my touch screen display. I've instructed Windows to use this screen as my touch screen display with the "Tablet PC Settings" from Control Panel.
My application is a simple JavaFX touch screen application containing a TextField. To show the virtual keyboard I've set the following settings to true:
-Dcom.sun.javafx.isEmbedded=true
-Dcom.sun.javafx.touch=true
-Dcom.sun.javafx.virtualKeyboard=javafx
My issue is that the keyboard is showing up, but on the wrong monitor. It shows on the primary monitor, instead of the third monitor that is set to be the touch monitor.
Is there a way to show the virtual keyboard on my touch monitor in the current system configuration? For example by telling the keyboard where it's owner application is so it displays on the correct monitor?

Found out how to change the monitor on which the keyboard is shown, to the monitor where the application is shown.
Attach a change listener to the focused property of your textField. When executing the change listener, retrieve the keyboard popup. Then find the active screen bounds of the monitor where the application is shown and move the keyboard x-coordinate to this location.
By setting autoFix to true, the keyboard will make sure its not (partially) outside your monitor, setting autoFix will adjust the y-coordinate automatically. If you don't set autoFix, you also have to set the y-coordinate manually.
#FXML
private void initialize() {
textField.focusedProperty().addListener(getKeyboardChangeListener());
}
private ChangeListener getKeyboardChangeListener() {
return new ChangeListener() {
#Override
public void changed(ObservableValue observable, Object oldValue, Object newValue) {
PopupWindow keyboard = getKeyboardPopup();
// Make sure the keyboard is shown at the screen where the application is already shown.
Rectangle2D screenBounds = getActiveScreenBounds();
keyboard.setX(screenBounds.getMinX());
keyboard.setAutoFix(true);
}
};
}
private PopupWindow getKeyboardPopup() {
#SuppressWarnings("deprecation")
final Iterator<Window> windows = Window.impl_getWindows();
while (windows.hasNext()) {
final Window window = windows.next();
if (window instanceof PopupWindow) {
if (window.getScene() != null && window.getScene().getRoot() != null) {
Parent root = window.getScene().getRoot();
if (root.getChildrenUnmodifiable().size() > 0) {
Node popup = root.getChildrenUnmodifiable().get(0);
if (popup.lookup(".fxvk") != null) {
return (PopupWindow)window;
}
}
}
return null;
}
}
return null;
}
private Rectangle2D getActiveScreenBounds() {
Scene scene = usernameField.getScene();
List<Screen> interScreens = Screen.getScreensForRectangle(scene.getWindow().getX(), scene.getWindow().getY(),
scene.getWindow().getWidth(), scene.getWindow().getHeight());
Screen activeScreen = interScreens.get(0);
return activeScreen.getBounds();
}

Related

JavaFX Events for Mouse Interactions are not triggered if Key is pressed

JavaFX does not execute events like the ActionEvent for Button or CheckBox, if a modifier key like CTRL or SHIFT is pressed. As far as I understand this behavior is implemented in ButtonBehavior (e.g. note the expression ! keyDown in the following method from that class):
#Override public void mouseReleased(MouseEvent e) {
// if armed by a mouse press instead of key press, then fire!
final ButtonBase button = getControl();
if (! keyDown && button.isArmed()) {
button.fire();
button.disarm();
}
}
First of all, I do not really understand the reason for this. What is the purpose of not firing a button if a key is pressed?
This is my use-case: I want to implement a checkbox that can be checked/unchecked as normal. It will toggle some state in a model. But it should have an additional feature: if the user presses some key like CTRL while checking/unchecking with the mouse, an additional flag called "locked" or "protected" should be set in the model, which will prevent that the state can be overwritten by some other logic of the application.
This should give an idea about the use-case, but if not it doesn't really matter for my actual question: How can I make it possible that a CheckBox can still be toggled (or a Button be pressed) even though the user presses a modifier key?
Thanks for your help!
That is odd you can implement it yourself like so
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
VBox vBox = new VBox();
vBox.setAlignment(Pos.CENTER);
CheckBox checkBox = new CheckBox();
checkBox.setOnMouseClicked(event -> {
if(event.isControlDown()) {
System.out.print("Control down click ");
checkBox.setSelected(!checkBox.isSelected());
}
else
System.out.print("Normal click ");
System.out.println("Checked Status:"+checkBox.isSelected());
});
Button button = new Button("Button");
button.setOnMouseClicked(event -> {
if(event.isControlDown())
System.out.println("Control down click");
else
System.out.println("Normal click");
});
vBox.getChildren().addAll(new Label("Click the box"),checkBox,button);
primaryStage.setScene(new Scene(vBox));
primaryStage.show();
}
public static void main(String[] args) { launch(args); }
}
The output for CheckBox:
Normal click Checked Status:true
Normal click Checked Status:false
Control down click Checked Status:true
Control down click Checked Status:false
The output for Button:
Normal click
Control down click

How do I stop TextArea from listening to Shortcut KeyCombinations as KeyEvents?

Just as the title says, how do I stop shortcut keys (accelerators) being picked up as key events in TextArea? I have tried the method suggested here with different modifications: TextArea ignore KeyEvent in JavaFX with no luck.
If you want to stop specific accelerators from working when the TextArea has focus simply add an event filter for KEY_PRESSED events.
public class AcceleratorFilter implements EventHandler<KeyEvent> {
// blacklist of KeyCombinations
private final Set<KeyCombination> combinations;
public AcceleratorFilter(KeyCombination... combinations) {
this.combinations = Set.of(combinations);
}
#Override
public void handle(Event event) {
if (combinations.stream().anyMatch(combo -> combo.match(event)) {
event.consume();
}
}
}
TextArea area = new TextArea();
area.addEventFilter(KeyEvent.KEY_PRESSED, new AcceleratorFilter(
KeyCombination.valueOf("shortcut+o"),
KeyCombination.valueOf("shortcut+s") // etc...
));
If you want to indiscriminately block all accelerators registered with the Scene then you can query the Scenes accelerators and consume the KeyEvent if appropriate.
TextArea area = new TextArea();
area.addEventFilter(KeyEvent.KEY_PRESSED, event -> {
var scene = ((Node) event.getSource()).getScene();
// #getAccelerators() = ObservableMap<KeyCombination, Runnable>
var combos = scene.getAccelerators().keySet();
if (combos.stream().anyMatch(combo -> combo.match(event)) {
event.consume();
}
});
This latter option may cause issues if you're not careful. For instance, if you have a default Button in the Scene then the above event filter may interfere with the ENTER key. Also, this option won't necessarily stop things like shortcut+c, shortcut+v, etc. because those shortcuts are registered with the TextInputControl, not the Scene.

How to capture the current screen image using Qt?

I have a button in my Custom QDialog, I am emitting a signal when pushbutton 1 is clicked
void MyCustomDialog::on_pushButton_1()
{
this->hide(); //i need to hide this window before 'OnButton_1_Clicked' stuffs starts
emit button_1_clicked();
}
In my main window I have connected the slot and created the instance as shown below
void MainWindow::MainWindow()
{
MyCustomDialog *dlg = MyCustomDialog::getInstance(this); //only single instance created
connect(dlg, &MyCustomDialog::button_1_clicked, this, &MainWindow::OnButton_1_Clicked);
}
I am displaying my custom dialog from a function in mainwindow as below
void MainWindow::dispayCustomDialog()
{
MyCustomDialog *dlg = MyCustomDialog::getInstance();
dlg->show();
}
Below shows how my 'OnButton_1_Clicked' slot. In which I am capturing the screenshot using below line
void MainWindow::OnButton_1_Clicked()
{
//capture the screen shot
QScreen *screen = QGuiApplication::primaryScreen();
QPixmap *map = new QPixmap(screen->grabWindow(0));
bool result = map->save("D:/test.jpg", "JPG");
}
Once I captured screen using above function, I can still see my 'MyCustomDialog' in test.jpg file. Qt doc says QGuiApplication::primaryScreen captures the initial state of application. So i think, this is expected in my case. Do we have any other solution to grab screen with current state ?
What I am trying to achieve is grab the screen in OnButton_1_Clicked() function after hiding my 'MyCustomDialog'.
I found a solution. Used a singleslot timer with delay of 500 msec before capturing the screen as below. This wait for my custom dialog to hide properly.
void MainWindow::OnButton_1_Clicked()
{
QTimer::singleShot(500, this, &MainWindow::shootscreen);
}
void MainWindow::shootscreen()
{
//capture the screen shot
QScreen *screen = QGuiApplication::primaryScreen();
QPixmap map = screen->grabWindow(0);
bool result = map.save("D:/test.jpg", "JPG");
}

How do I keep my Editor from scrolling below keyboard when moving the cursor?

I've got an Editor which is tall enough to accommodate multiple lines of input. The editor is at the bottom of the screen if it matters. Once multiple lines are entered, if you move the cursor up a line, the entire view shifts downward, so the line you just left is now obscured by the keyboard. I'd like it to not do this unless the line the cursor is on is either off screen or close to being off screen. Sort of like how the built in Android message app works. Here's what I mean in pictures.
Default state, everything looks good. We have 3 lines of input
I move the cursor up one line: notice that the entire view has shifted down one line and so "line 3" is now obscured. I don't want this behavior (it among other things hides some UI elements).
This is the Android messaging app. This is the behavior I want: when you move the cursor to that next line, the view doesn't just shift downward. If you have enough lines to scroll past the visible area, Android just shifts the text and not the entire view to accommodate it.
In essence, I just want the contents of the editor to shift (when appropriate) and not the entire window.
I managed to figure it out. Firstly, in MainActivity.cs, after the LoadApplication call, I added this:
Xamarin.Forms.Application.Current.On<Xamarin.Forms.PlatformConfiguration.Android>().UseWindowSoftInputModeAdjust(WindowSoftInputModeAdjust.Resize);
Using the default or .Pan didn't work at all. Note that just doing this didn't fix the issue. I also had to take the class that's described here and use it. Without that class, things didn't work correctly. Also the "alternative method" described on that page didn't work (which was basically just changing the soft input mode adjust).
In the event that the link I mentioned dies, here's the class. I did slightly modify it so that by default it is not enabled. You just set Disabled = false on it to enable it. Also you call the static method Init to initialize the class.
public class AndroidBug5497WorkaroundForXamarinAndroid
{
private readonly View _childOfContent;
private readonly FrameLayout.LayoutParams _frameLayoutParams;
private int _usableHeightPrevious;
public static AndroidBug5497WorkaroundForXamarinAndroid Instance { get; private set; }
private bool _disabled = true;
public bool Disabled
{
get => _disabled;
set
{
if (_disabled != value)
{
_disabled = value;
if (_disabled)
_childOfContent.ViewTreeObserver.GlobalLayout -= PossiblyResizeChildOfContent;
else
_childOfContent.ViewTreeObserver.GlobalLayout += PossiblyResizeChildOfContent;
}
}
}
public static void Init(Activity activity)
{
if (Instance != null)
return;
Instance = new AndroidBug5497WorkaroundForXamarinAndroid(activity);
}
private AndroidBug5497WorkaroundForXamarinAndroid(Activity activity)
{
FrameLayout content = (FrameLayout)activity.FindViewById(Android.Resource.Id.Content);
_childOfContent = content.GetChildAt(0);
_frameLayoutParams = (FrameLayout.LayoutParams)_childOfContent.LayoutParameters;
}
private void PossiblyResizeChildOfContent(object sender, EventArgs e)
{
int usableHeightNow = ComputeUsableHeight();
if (usableHeightNow != _usableHeightPrevious)
{
var usableHeightSansKeyboard = _childOfContent.RootView.Height;
var heightDifference = usableHeightSansKeyboard - usableHeightNow;
_frameLayoutParams.Height = usableHeightSansKeyboard - heightDifference;
_childOfContent.RequestLayout();
_usableHeightPrevious = usableHeightNow;
}
}
private int ComputeUsableHeight()
{
Rect r = new Rect();
_childOfContent.GetWindowVisibleDisplayFrame(r);
return (r.Bottom - r.Top);
}

How to make all buttons having ActionEvent handler handle Enter key in JavaFX?

I have about 50 buttons in my application. For all buttons I created handler this way:
#FXML
protected void handleFooButtonActionEvent(ActionEvent actionEvent) {
...
}
This way user can press buttons using mouse left button or Space key. However, it is normal practice (as I know) to allow user press button using Enter key. Is it possible to make all buttons having ActionEvent handler (see above) handle also Enter key in JavaFX, for example if we have reference to Stage stage, Scene scene or Parent root?
You can configure the Scene to do it:
scene.getAccelerators().put(
KeyCombination.valueOf("Enter"), () -> {
Node focusOwner = scene.getFocusOwner();
if (focusOwner instanceof Button) {
((Button) focusOwner).fire();
}
});
You can also do it with an event handler:
scene.addEventHandler(KeyEvent.KEY_PRESSED, e -> {
if (e.getCode() == KeyCode.ENTER) {
Node focusOwner = scene.getFocusOwner();
if (focusOwner instanceof Button) {
((Button) focusOwner).fire();
}
}
});
Normally I’d agree with James_D that changing the standard behavior of buttons is not a good idea, but I’m finding that all GTK applications allow Enter to trigger a button press, as does Firefox as you’ve mentioned.

Resources