Cocoa Won't Capture Shift Modifier? - cocoa

I have an application in which I'm trying to capture the shift key modifier to perform an action, however when I run the program and press and normal key without the shift key modifier I get a beep and the modifier and key are not sent to my keyDown event. The relevant code is:
NSString* eventChars = [theEvent charactersIgnoringModifiers];
if ([eventChars isEqualTo:#"w"]) {
newPlayerRow++;
direction = eUp;
} else if ([eventChars isEqualTo:#"x"]) {
newPlayerRow--;
direction = eDown;
} else if ([eventChars isEqualTo:#"a"]) {
newPlayerCol--;
direction = eLeft;
} else if ([eventChars isEqualTo:#"d"]) {
newPlayerCol++;
direction = eRight;
} else {
[super keyDown:theEvent];
return;
}
// handle the player firing a bullet
if (([theEvent modifierFlags] & (NSShiftKeyMask | NSAlphaShiftKeyMask)) != 0) {
NSLog(#"Shift key");
[self fireBulletAtColumn:newPlayerCol row:newPlayerRow inDirection:direction];
[self setNeedsDisplay:YES];
} else {
...
}
I'm not sure what is causing this, but I'd like to be able to capture shift key presses. Thanks in advance for any help with this problem.
EDIT: Also I'm using a MacBook keyboard if that makes any difference.
EDIT: This is definitely a shift-centric problem as changing (NSShiftKeyMask | NSAlphaShiftKeyMask) to NSControlKeyMask does have the desired effect.

First, -charactersIgnoringModifiers doesn't ignore the shift key, so you will still get shifted characters (i.e UPPERCASE and !%#$%^&*) returned from it. What's probably happening in your function is: You press shift-w, your -isEqualTo: returns false because you're comparing a lowercase 'w' and an uppercase 'W', and so you return before getting to the shift-detection code at the bottom. The simplest solution is to just check for both.
However, if you want, for example, Arabic keyboardists to be able to easily use your app, you really shouldn't hardcode characters that may not even appear on the user's keyboard. The value returned by -keyCode refers to a key's position on the keyboard, not the represented character. For starters, the constants beginning in 'kVK_ANSI_' and 'kVK_' in Events.h (you may have to link to Carbon.framework and #include <Carbon/Carbon.h> to use those constants) can be compared to what -keyCode returns, and they refer to the key positions a QWERTY-using USian expects. So you can be (pretty) sure that, regardless of keyboard layout, the keycodes for 'wasd' (kVK_ANSI_W, kVK_ANSI_A, etc.) will refer to that triangle in the top left of your user's keyboard.

If you just want to be notified of modifier key presses without a character key you should be overriding the NSResponder method flagsChanged.
- (void)flagsChanged:(NSEvent *)theEvent

Have you tried logging the values of eventChars and modifierFlags using NSLog? Perhaps it's something other than what you expected it to be. If the log statement doesn't show up in your output, then this code is not running at all and you have a problem somewhere else.
NSLog(#"Chars: %#, modifier flags: 0x%x", eventChars, [theEvent modifierFlags]);
Some other things worth noting:
isEqualTo: is the AppleScript equality operator. It should work, but won't be as efficient because it goes through compare:. The proper method is either isEqualToString: or the more-generic isEqual:, either of which may do a straight equality comparison.
The traditional set of movement keys in the QWERTY letter region is wsad, not wxad. This uses the inverted-T arrangement. You may also want to support the arrow keys, as many of us do have keyboards with their arrows in the inverted T arrangement.
Furthermore, you should make these configurable by the user, and you should respond to key codes rather than letters because different layouts (French, Dvorak, etc.) will generate different letters. I have a table of key codes, adapted from Inside Macintosh, on my website. Use characters only for display purposes.
Shouldn't it be “fireBulletFromColumn:row:inDirection:”? I don't know about you, but saying that the method fires a bullet at a cell tells me (and will tell you, after you haven't looked at the code for a year) that it fires the bullet toward that cell; replacing “At” with “From” avoids this moment of confusion.

From your code it looks like the code that checks for the modifier is never reached.
Anything that is not "w,x,a,d" is sent to the superclass (see the else branch).
You probably want to move "[super keyDown:theEvent]; return;" at the end of your keyDown method.

Solving the immediate problem:
Building on what Boaz said, the -charactersIgnoringModifiers does not include the Shift key (read the documentation) and will return capitalized characters when the Shift key is down. That is why the Control key worked and the Shift key didn't. Switch the string to lowercase before you test it:
eventChars = [[theEvent charactersIgnoringModifiers] lowercaseString];
After that, (as Peter suggested) change all the isEqualTo:'s to isEqualToString: (besides being correct, it's also documented as being faster).
However:
If you are planning on releasing this app to other people, you should really be using key codes (as Peter and Boaz suggested) so it will work on as many different keyboards and setups as possible.
Create a view in your preferences with an input for each command you have and record the key code for the key the user presses. Store these in your NSUserDefaults and check against those instead of your hard coded strings. You can create a default set for your current "wasd" keys.
See projects like Shortcut Recorder for examples of how to do that (or search your friend).

Related

How to send keypress combinations in nightwatch

I can send a single key press in night-watch without a problem, but I need to press combination of them. For example UP_ARROW + SHIFT
Code from page objects.
this.sendKeys('#pmField', this.api.Keys.UP_ARROW+this.api.Keys.SHIFT)
This function just sends keys in a sequence. Firstly arrow up and then shift and I'm expecting that they would be pressed together as a combination.
browser
.keys(browser.Keys.CONTROL) // hold the control
.click('#element') // click something
.keys(browser.Keys.NULL) // release the control
This works properly in my tests when i need to click multiple elements while holding the control key. I think you can combine it with following key strokes instead of clicks. Hope this helps.
As Skuubi80 states you can use browser.keys().
Take a look at the api doc and note this line...
Rather than the setValue, the modifiers are not released at the end of the call. The state of the modifier keys is kept between calls, so mouse interactions can be performed while modifier keys are depressed.
Keep in mind this does mean that you will need to call browser.keys('null') to 'un-press' the keys once done.
Hope that helps :)

how to detect space key in NSTextField

i have used one NSTextField and i want to implement autocomplete in which i want to detect space key while there are multiple words in my NSTextField.
I have used this method doCommandBySelector but it does not have any method for space detecting while it has methods for tab , delete and back key.
For ex :
if (commandSelector == #selector(deleteBackward:))
{
backspaceKey = YES;
}
please help me on how to detect space key. Thank you.
Have you tried using UITextView's delegate to check for what was the input as it is suggested here?

How to tell NSTextView which side to extend selection on?

If I have an NSTextView which is in this state:
How can I tell the textview that if a user presses shift+right, that rather than extending right towards the 'o', it instead de-selects the 'e'? I though this had to do with the affinity, but I have tried setting it to both NSSelectionAffinityUpstream and NSSelectionAffinityDownstream via the following code:
[self setSelectionRange: NSMakeRange(9,6)
affinity: x
stillSelecting: NO];
But that made no different. Hitting shift+right still selected the 'o'.
NSTextView knows how to do this SOMEHOW, because if you cursor position between 'w' and 'o', then hit shift+left until it matches the screenshot, then hit shift+right, it matches the behaviour I mentioned.
I'm ok to override the shift+arrow code and roll my own, but I would rather allow NSTextView to do its own thing. Anyone know if I am missing anything?
I'm not sure what you're trying to do, since this is the default text movement/selection modification behavior. Are you trying to override this to always do this one thing or are you trying to add another keyboard shortcut that overrides this behavior? In either case, some background:
Selection affinity doesn't quite work the way it sounds (this is a surprise to me after researching it just now). In fact there seems to be a disconnect between the affinity and the inherited NSResponder actions corresponding to movement and selection modification. I'll get to that in a moment.
What you want to look at are the responder actions like -moveBackwardAndModifySelection:, -moveWordBackwardAndModifySelection:, and so on. Per the documentation, the first call to such selection-modifying movement actions determines the "end" of the selection that will be modified with subsequent calls to modify selection in either direction. This means that if your first action was to select forward (-moveForwardAndModifySelection:), the "forward end" (right end in left-to-right languages; left end in right-to-left, automagically) is what will be modified from that point forward, whether you call -moveForward... or -moveBackward....
I discovered the disconnect between affinity and -move...AndModifySelection: by looking at the open source Cocoatron version of NSTextView. It appears the internal _affinity property isn't consulted in any of the move-and-select methods. It determines whether the selection range change should be "upstream" or "downstream" based on _selectionOrigin, a private property that is set when the selection ranges are modified (via the -setSelectedRange(s)... method or a mouse down / drag). I think the affinity property is just there for you to consult. Overriding it to always return one value or another doesn't change any behavior, it just misreports to outsiders. The _selectionOrigin seems only to be modified via -setSelectedRanges... method if the selection is zero-length (ie, just cursor placement).
So, with all this in mind, you might have to add one step to your manual selection modification: First set an empty selection with a location where you'd like the selection origin to be (forward end if you want backward affinity; backward end if you want forward affinity), then set a non-zero-length selection with the desired affinity.
Roundabout and ridiculous, I know, but I think that's how it has to be, given the CocoaTron source code.

Recognize if user has pressed arrow key while editing NSTextField swift

I have many NSTextFields and I want to know, if the user has pressed one of the arrow keys while editing one of them. The function
override func keyDown(theEvent: NSEvent) {
switch theEvent.character {
case NSRightArrowFunctionKey:
println(1)
moveGor(NSRightArrowFunctionKey)
case NSLeftArrowFunctionKey:
moveGor(NSLeftArrowFunctionKey)
case NSUpArrowFunctionKey:
moveVert(NSUpArrowFunctionKey)
case NSDownArrowFunctionKey:
moveVert(NSDownArrowFunctionKey)
default:
super.keyDown(theEvent)
}
}
doesn't seem to work. Is there any other way to do that in swift?
EDIT:
I have the extension for NSEvent:
extension NSEvent {
var character: Int {
let str = charactersIgnoringModifiers!.utf16
return Int(str[str.startIndex])
}
}
that I used in previous function
When text fields have the focus, they actually don't. Instead, a text view is added to the window on top of the text field and that text view is the first responder and handles all of the input and editing behaviors. The text view is known as the "field editor". The text field does not receive key down events; the text view does.
You could substitute a custom text view as the first responder for the text field and have that text view handle the key down events specially. However, it's probably easier to take advantage of the fact that the text field is the delegate for the text view. Depending on exactly what you're trying to achieve, you might implement -textView:willChangeSelectionFromCharacterRange:toCharacterRange:, but that's not exclusively about arrow keys.
A more promising method might be -textView:doCommandBySelector:. That's also not really about the arrow keys, but in some ways it's better. The arrow keys, and all other standard editing keys, operate by being translated through the key bindings system into command selectors. The command selectors represent the semantic operation being performed, like -moveUp:. They are changed by modifier flags, so that Shift-up-arrow might generate -moveUpAndModifySelection:.
Anyway, in -textView:doCommandBySelector:, you can execute code based on the selector and either tell the text view not to do anything else (by returning YES) or let the text view do its normal thing in addition (by returning NO). (Obviously, return NO for anything that you don't care about.)

Repeating key events blocking

I wrote a simple program with SFML and OpenGL which draws a spinning square that can be moved around the screen with the arrow keys.
It works fine on all the Linux and Mac computers I've tested it on, but when I try to move the square on Windows (by holding down an arrow key) it moves a small distance and then stops moving and spinning. I'm pretty sure the program is getting stuck in the GetEvent method - my guess is that when I've held the key down long enough for it to start repeating, the event stack keeps getting new events added to it before I can pop everything off it (and if I turn the key repeat rate on Windows right down to the minimum then the problem goes away - I don't really like this as a solution though).
I found that pressing and holding Alt, Ctrl, Delete, Page up, Page down, Home, End etc all cause this behavior too (even though I don't specifically detect any of these keys in the program), but all the letter keys, as well as space, enter, backspace and the keypad arrow keys work fine (i.e. they don't cause the program to pause if I hold them down for too long).
I don't have the exact code (I just turned my laptop off), but it looks like:
while(running) {
while(app.GetEvent(event))
if(event.Type==sf::Event::Closed) running=false;
if(input.IsKeyDown(sf::Key::Right)); // move right
// etc etc
// update rotation
// draw everything
}
Any ideas as to what the exact problem might be, and how I could fix it?
I know this is an old question but I would like to answer it in the interest of helping others who may find themselves here experiencing similiar problems.
SFML 1.6 has two ways you can get input from the user. One is event-based where you process each event sent to you via sf::Window::GetEvent(). The other is query-based where you check the sf::Input class of your window directly.
You have used the query-based method here but put it inside an event loop which isn't really the way it was intended to be used. It was meant to be used like this. This is a nice feature because SFML essentially keeps a boolean table of keys automatically for you so you don't need to manage key states yourself. IMHO for using repeating input this is more elegant since you won't be spamming your event queue, just checking a boolean value.
while(app.GetEvent(event))
if(event.Type == sf::Event::Closed) running=false;
if(event.Type == sf::Event::KeyPressed && event.Key.Code == sf::Key::Right)
{
// move right
}
}
If you wanted to just query sf::Input directly then you use the same code as above, but you put it outside the event loop.
while(app.GetEvent(event)
{
}
if (myWindow.GetInput().IsKeyDown(sf::Key::Right))
{
}
By default automatic key repeat should be enabled for sf::Windows but you can make sure by using sf::Window::EnableKeyRepeat(true). This means it will send a KeyPressed event repeatedly while a key is held down.
Try using the query-based method outside the main event loop and see if that works for you.

Resources