How to detect fn key press in Swift? - swift

I want to detect when the user presses the fn key and do some tasks. I tried the below but it doesn't work:
if event.keyCode == kVK_Function {
print("fn key pressed")
}
I have similar code for other keys like left bracket, right bracket, slash, alphabets, and numbers. For these, similar code, as shown above, works fine but it doesn't work for fn key. I think this is handled differently.

You need to check the NSEvent's modifierFlags:
if event.modifierFlags.contains(.function) {
print("fn key pressed")
}

Related

MAUI Text Entry on Complete not Working - Possible Work around?

I'm trying to use text entry on maui to fire an event when completed. I have set the "Completed" event to a handler and it works correctly on windows. But on Android I have no joy, the event just isn't firing.
I realise there is a bug in Maui which is preventing this. But it looks like the problem was discovered in August? It's a fairly basic thing, well at least it appears to be on the face of things.
What is the best work around for this? The only thing I can think is by using the textchanged event instead of completed. This works correctly, but then i have to bodge it by doing this sort of thing:
if (entry1.Text.EndsWith("#"))
{
//Then string is complete, so need to fire correct event
System.Diagnostics.Debug.WriteLine("Complete String Detected");
}
This works and I can use it since I'm awaiting for input from a barcode scanner, so I can set the last terminating character to whatever I want. In this case I set it to a #. I can't figure out a way to detect the return key being pressed.
Thanks
Andrew
There is a similar issue on the github about the Entry.Completed and Entry.ReturnCommand not executed.
This comment shows the cause and the workaround about this problem.
detect the return key being pressed
In addition, I can't understand the problem. I have created a sample to test. The Entry.Completed event will call when I pressed the enter key. I don't kown the return key you mentioned is what. But you can use the IOnKeyListener for the android to detect any key being pressed.
Create a Listener class in the /Platform/Android:
public class MyKeyListener : Java.Lang.Object, IOnKeyListener
{
public bool OnKey(global::Android.Views.View v, [GeneratedEnum] Keycode keyCode, KeyEvent e)
{
var edittext = v as AppCompatEditText;
if (keyCode == Keycode.Enter && e.Action == KeyEventActions.Down )
//I used the Entry key as the example, you can use any other key you want to replace it.
{
edittext.ClearFocus();
edittext.SetBackgroundColor(Color.GreenYellow);
return true;
}
return false;
}
And in the page.xml:
<Entry Completed="Entry_Completed" x:Name="entry"/>
Set the listener to the entry by overriding the OnHandlerChanged() in the page.cs:
protected override void OnHandlerChanged()
      {
            base.OnHandlerChanged();
#if ANDROID
(entry.Handler.PlatformView as AndroidX.AppCompat.Widget.AppCompatEditText).SetOnKeyListener(new MauiAppTest.Platforms.Android.MyKeyListener());
#endif
}
You can also set the listener with the handler. Finally, you can try both the method above and the workaround in the issue on the github.

Is it possible to wait for either button A or button B?

This is code that waits for an event coming from button A.
control.waitForEvent(Button.A, EventBusValue.MICROBIT_EVT_ANY)
I want to wait for either button A OR button B to be pressed.
Is this possible?
EDIT
I know that in Scratch this is possible with something like button.any, also the above code is written in microjavascript, but it is written similarly in micropython, so maybe someone from this field can also help. Thank you.
Have a look at the python code generated using the 'on event from' block in https://makecode.microbit.org. This block can be found under the Advanced tab. The block can be set to wait for either button A or B to be pressed. The micropython code generated in this case is:
control.on_event(EventBusSource.MICROBIT_ID_BUTTON_AB,
EventBusValue.MICROBIT_EVT_ANY,
on_microbit_id_button_ab_evt)
The equivalent JavaScript code can also be viewed in the editor.
The best way I could do it was with common function for A nad B, to be called on events of button pressed, like:
input.onButtonPressed(Button.A, function () {
qAndA(true)
})
input.onButtonPressed(Button.B, function () {
qAndA(false)
})
The true and false are not the best implementations but to know which button was pressed, I needed to pass the true for A and false for B.
This was required in my case, because I was writing test and later in this function I would compare the button pressed and the actual correct answer. The function (without my full implementation) goes something like this:
function qAndA(aOrB: boolean) {
if (text_list.length == 0) {
basic.showNumber(count)
basic.pause(2000)
basic.showLeds(`
# # # # #
. . # . .
. . # . .
. . # . .
. . # . .
`)
}
if (text_list[0] == Q1 && aOrB == true) {
...
}
}
In scratch, placing a [Wait for <Key Pressed (a) ||or|| Key Pressed (b)>], will do the trick, you can just slot a key pressed (a) and a key pressed (b) into the same or statement, and place the new blocks into a "Wait until" block, place this where you want the code to stop and continue on the keypress update and you're golden.

How to double space as ctrl key?

I want the space key to work like Ctrl after 200 ms of being pressed. So, in theory it should work like this:
while space key is press down {
for first 200 ms {
if another key is pressed or space key released {
work like normal space
go no further
}
}
after first 200 ms {
if another key <Key> is pressed {
Send Ctrl+Key
}
}
}
Is it possible with AutoHotKey?
At least part of this is possible with AutoHotkey, the below fulfills everything except 'if another key is pressed'. It might be possible if you made every key a hotkey and then used A_PriorHotkey. Or perhaps there is another way I haven't thought of.
$Space::
KeyWait, Space, T2
if(ErrorLevel) {
Input, SingleKey, L1, {LControl}{RControl}{LAlt}{RAlt}{LShift}{RShift}{LWin}{RWin}{AppsKey}{F1}{F2}{F3}{F4}{F5}{F6}{F7}{F8}{F9}{F10}{F11}{F12}{Left}{Right}{Up}{Down}{Home}{End}{PgUp}{PgDn}{Del}{Ins}{BS}{CapsLock}{NumLock}{PrintScreen}{Pause}
Send {Ctrl down}%SingleKey%{Ctrl up}
} else {
Send {Space}
}
return
I took the input part from here.

Executing a function when key combination is pressed

I'm adding some functionality to a menubar app. I want to execute a few lines of code that copies some text to the clipboard when a combination of keys are pressed (e.g. cmd + alt + L). This should work globally, i.e any time these keys are pressed.
Not sure how to go about doing this, I tried overriding the keyDown method but it gives an error in AppDelegate.swift saying that there's no method to override.
First step you need to add a global monitor.
NSEvent.addGlobalMonitorForEvents(matching: .keyDown, handler: {
self.keyDown(with: $0)
})
But it can be also your func.
Second step is to handle these three keys
Read flags from NSApp.currentEvent?.modifierFlags and check if they contain .option and .command flags
Example
guard let flags = NSApp.currentEvent?.modifierFlags else {
return
}
let optionKeyIsPressed = flags.contains(.option)
At last key you can read from NSEvent property keyCode.
The keyCode of later "L" you can read from kVK_ANSI_L
Hope it's all that you need to solve your problem, good luck.

Clicking keyboard 'Next' key using UIAutomation

I have a search field in my app and I have set the return key type of the keyboard for this field to UIReturnKeyNext. I am attempting to write a UIAutomation test that clicks the Next button on the keyboard using the following line:
UIATarget.localTarget().frontMostApp().mainWindow().keyboard().keys().firstWithName("next");
This call is failing because the key with name 'next' is not being found. I have done a dump of all of the elements in my app using:
UIATarget.localTarget().frontMostApp().logElementTree();
This reveals that there is indeed a key in the keyboard with name 'next', but somehow my attempt to retrieve it as show above still fails. I can however retrieve other keys (like the key for the letter 'u') using this method. Is there a known issue here or am I doing something wrong?
I've tried other variations with no luck:
UIATarget.localTarget().frontMostApp().mainWindow().keyboard().elements()["next"];
Here is a screen capture of the elements in my UIAKeyboard:
If you just want to click it, and you know the keyboard has "next" as "Return key" (defined in your nib), then you can use this:
app.keyboard().typeString("\n");
Jelle's approach worked for me. But I also found an alternative way if anybody needed it.
XCUIApplication().keyboards.buttons["return"].tap()
Where you can create XCUIApplication() as a singleton on each UI Test session. The thing about this approach is you can now distinguish between return and done and other variants and even check for their existence.
You can go extra and do something like following:
extension UIReturnKeyType {
public var title: String {
switch self {
case .next:
return "Next"
case .default:
return "return"
case .continue:
return "Continue"
case .done:
return "Done"
case .emergencyCall:
return "Emergency call"
case .go:
return "Go"
case .join:
return "Join"
case .route:
return "Route"
case .yahoo, .google, .search:
return "Search"
case .send:
return "Send"
}
}
}
extension XCUIElement {
func tap(button: UIReturnKeyType) {
XCUIApplication().keyboards.buttons[button.title].tap()
}
}
And you can use it like:
let usernameTextField = XCUIApplication().textFields["username"]
usernameTextField.typeText("username")
usernameTextField.tap(button: .next)
I dont't have an example to test, but as the "Next" button is an UIAButton, and not an UIAKey you could try :
UIATarget.localTarget().frontMostApp().mainWindow().keyboard().buttons()["next"];
If it doesn't work, you can also try
UIATarget.localTarget().frontMostApp().mainWindow().keyboard().buttons()[4];
The following works for me:
UIATarget.localTarget().frontMostApp().mainWindow().keyboard().buttons().firstWi‌​thPredicate("name contains[c] 'next'");
For me, keyboard does not fall under mainWindow() in the View Hierarchy. It is at the same level as mainWindow() when you logElementTree() from top level. So, what you want to do is:
UIATarget.localTarget().frontMostApp().keyboard().buttons()["next"];
This worked for me when I was trying to press the "Search" button on keyboard.