Help, Double-Press Script Executes Single Press Immediately Topic is solved

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
Mulsiphix
Posts: 148
Joined: 20 Nov 2015, 17:56

Help, Double-Press Script Executes Single Press Immediately

Post by Mulsiphix » 28 Jan 2022, 11:36

I grabbed a double-press script from the KeyWait documentation. I've added some code to it but noticed that the moment I press F3 once, it executes. No matter how quickly I double-tap F3, only the single-click portion of the script will execute. Is this how the script is designed to work or have I made a mistake somewhere?

Code: Select all

F3::
if (A_PriorHotkey != "F3" or A_TimeSincePriorHotkey > 350)
{
    ; Too much time between presses, so this isn't a double-press.
    Send, {Down 3}{Enter}
    Sleep, 300
    Send, {Down 2}{Enter}
    Sleep, 300
    Send, {Up}{Enter}
    Return
}

; A double-press was detected, script executes the following
Send, {Down 3}{Enter}
Sleep, 300
Send, {Down 2}{Enter}
Sleep, 300
Send, {Up 3}{Enter}
Return

User avatar
boiler
Posts: 16951
Joined: 21 Dec 2014, 02:44

Re: Help, Double-Press Script Executes Single Press Immediately

Post by boiler » 28 Jan 2022, 11:51

Notice the comment that you copied into your own code:
Too much time between presses, so this isn't a double-press.

Change it to this to act when it is a double-press:

Code: Select all

if (A_PriorHotkey = "F3" && A_TimeSincePriorHotkey < 350)

Or to correctly implement what the version you tried to use:

Code: Select all

F3::
if (A_PriorHotkey != "F3" or A_TimeSincePriorHotkey > 350)
{
	; Too much time between presses, so this isn't a double-press.
	KeyWait, F3
	return
}
Send, {Down 3}{Enter}
Sleep, 300
Send, {Down 2}{Enter}
Sleep, 300
Send, {Up}{Enter}
return

Mulsiphix
Posts: 148
Joined: 20 Nov 2015, 17:56

Re: Help, Double-Press Script Executes Single Press Immediately

Post by Mulsiphix » 28 Jan 2022, 14:04

I didn't even think about changing the or to &&. However, when I do that only the double-press section works. The single-press section no longer does. I thought I understood how the if (A_PriorHotkey <> "~LShift" or A_TimeSincePriorHotkey > 350) portion of the code functioned. But after seeing your example, it seems clear to me why or only allows the activation of the single-press section and && only works for the double-press section.

Your second example code does allow both sections to be activated, but not in the scenario that F6 was A_PriorHotkey. Is it possible for both the single and double-press sections to function, even if A_PriorHotkey is the trigger? For quick reference (really for myself as I know you already understand) here is what has been tried so far.

Always produces a Single-Press result, regardless of physical single/double-press activation

Code: Select all

F3::
if (A_PriorHotkey != "F3" or A_TimeSincePriorHotkey > 350)
{
	; Too much time between presses, so this isn't a double-press.
                  MsgBox, "!=" and "or" / Single-Press Detected
	return
}
MsgBox, "!=" and "or" / Double-Press Detected
return


Always produces a Single-Press result, regardless of physical single/double-press activation

Code: Select all

F4::
if (A_PriorHotkey != "F4" or A_TimeSincePriorHotkey > 350)
{
	; Too much time between presses, so this isn't a double-press.
                  MsgBox, "=" and "or" / Single-Press Detected
	return
}
MsgBox, "=" and "or" / Double-Press Detected
return


Always produces a Double-Press result, regardless of physical single/double-press activation

Code: Select all

F5::
if (A_PriorHotkey = "F5" && A_TimeSincePriorHotkey < 350)
{
	; Too much time between presses, so this isn't a double-press.
                  MsgBox, "=" and "&&" / Single-Press Detected
	return
}
MsgBox, "=" and "&&" / Double-Press Detected
return


If a different key is pressed before F6, it will always yield a Single-Press result. If F6 was pressed previously, it will always produce a Double-Press result. Results are true regardless of physical single/double-press activation

Code: Select all

F6::
if (A_PriorHotkey != "F6" && A_TimeSincePriorHotkey > 350)
{
	; Too much time between presses, so this isn't a double-press.
                  MsgBox, "!=" and "&&" / Single-Press Detected
	return
}
MsgBox, "!=" and "&&" / Double-Press Detected
return
Last edited by Mulsiphix on 28 Jan 2022, 14:43, edited 1 time in total.

User avatar
boiler
Posts: 16951
Joined: 21 Dec 2014, 02:44

Re: Help, Double-Press Script Executes Single Press Immediately

Post by boiler » 28 Jan 2022, 14:32

Look at it carefully. Either copy the whole line or notice all the changes in it.

Mulsiphix
Posts: 148
Joined: 20 Nov 2015, 17:56

Re: Help, Double-Press Script Executes Single Press Immediately

Post by Mulsiphix » 28 Jan 2022, 15:29

I am not sure what I am missing. On the off chance I have failed to communicate my desire, I would like to achieve the same result as this script from the SetTimer page. It is a bit large though. The KeyWait script is more compact and I prefer that. But maybe the KeyWait script just can't be modified to achieve the same result as the SetTimer script?

boiler wrote:
28 Jan 2022, 14:32
Look at it carefully. Either copy the whole line or notice all the changes in it.
I feel I have been. I apologize if I am being dense, but I just don't understand what I could be missing. I cannot comprehend why when an If statement fails to meet it's specified condition, it does not immediately go to the defined Else result.

if (A_PriorHotkey != "F3" or A_TimeSincePriorHotkey > 350)
Understanding: If last pressed key is not F3 OR time since last key press is greater than 350ms, execute single-press section. Otherwise, execute double-press section.

if (A_PriorHotkey = "F3" && A_TimeSincePriorHotkey < 350)
Understanding: If last pressed key is F3 AND time since last key press is greater than 350ms, execute single-press section. Otherwise, execute double-press section.



UPDATE
I tried rewriting the script and am having the same problem as the F6 example I provided above. It seems obvious to me now that I am missing a crucial piece of understanding as to why these scripts are not functioning as I expect them to.

Code: Select all

F7::
if (A_PriorHotkey = "F7" && A_TimeSincePriorHotkey < 350)
   Gosub, Double_Press
Else If (A_PriorHotkey = "F7" && A_TimeSincePriorHotkey > 350)
   Gosub, Single_Press
Return

Single_Press:
MsgBox, Single-Press Detected
Return

Double_Press:
MsgBox, Double-Press Detected
return

User avatar
boiler
Posts: 16951
Joined: 21 Dec 2014, 02:44

Re: Help, Double-Press Script Executes Single Press Immediately

Post by boiler » 28 Jan 2022, 15:48

One thing at a time because you're mixing things up. I said that for this code that you showed:

Code: Select all

F3::
if (A_PriorHotkey != "F3" or A_TimeSincePriorHotkey > 350)
{
    ; Too much time between presses, so this isn't a double-press.
    Send, {Down 3}{Enter}
    Sleep, 300
    Send, {Down 2}{Enter}
    Sleep, 300
    Send, {Up}{Enter}
    Return
}
...if you want to make that work for a double-press, you would change the first line to what I showed. When you added that line, you reversed things and added stuff so that you thought the first part would be a single-press, then you added stuff below that would then be executed when double-pressed. That's not what I said to do, and that's wrong:

Code: Select all

; THIS IS YOUR CODE THAT IS NOT CORRECT -- POSTED FOR REFERENCE
F5::
if (A_PriorHotkey = "F5" && A_TimeSincePriorHotkey < 350)
{
	; Too much time between presses, so this isn't a double-press.
                  MsgBox, "=" and "&&" / Single-Press Detected
	return
}
MsgBox, "=" and "&&" / Double-Press Detected
return

Again, I just said to change the first line to make that block of code execute on a double-press:

Code: Select all

F3::
if (A_PriorHotkey = "F3" && A_TimeSincePriorHotkey < 350)
{
    ; THIS NOW ACTS ON A DOUBLE-PRESS
    MsgBox, F3 was double-pressed.
}
Return

Now run that so you can understand it before we move on to having a single-press work as well.

User avatar
boiler
Posts: 16951
Joined: 21 Dec 2014, 02:44

Re: Help, Double-Press Script Executes Single Press Immediately

Post by boiler » 28 Jan 2022, 16:17

Now, to be able to distinguish between single and double-presses (or more), you can't just use that same code and execute code when the other condition is true because that condition will always be true whether it's on a single press or on the first press of a double-press. You need to have the code wait to see if there is another press and get a count before acting on either, and that requires a different approach. That's why the documentation points to a different approach based on SetTimer. Here is the code they've implemented there, but I changed it for F7 like you were looking to do:

Code: Select all

F7::
if (f7_presses > 0) ; SetTimer already started, so we log the keypress instead.
{
    f7_presses += 1
    return
}
; Otherwise, this is the first press of a new series. Set count to 1 and start
; the timer:
f7_presses := 1
SetTimer, KeyF7, -400 ; Wait for more presses within a 400 millisecond window.
return

KeyF7:
if (f7_presses = 1) ; The key was pressed once.
{
    MsgBox, F7 was pressed once.
}
else if (f7_presses = 2) ; The key was pressed twice.
{
    MsgBox, F7 was pressed twice.
}
else if (f7_presses > 2)
{
    MsgBox, F7 was pressed 3 or more times.
}
; Regardless of which action above was triggered, reset the count to
; prepare for the next series of presses:
f7_presses := 0
return

Mulsiphix
Posts: 148
Joined: 20 Nov 2015, 17:56

Re: Help, Double-Press Script Executes Single Press Immediately

Post by Mulsiphix » 29 Jan 2022, 13:27

boiler wrote:
28 Jan 2022, 15:48
One thing at a time because you're mixing things up. I said that for this code... if you want to make that work for a double-press, you would change the first line to what I showed.
I understand now. What you gave me made my original code activate on a double-press. I was confused initially because I had the expectation of how the code you gave me was supposed to theoretically function. I expected both the single-press and double-press sections to work. I now understand that it can be either the single or double section functioning, but not both. At least, I understand you are telling me that is not possible using this approach. I am still trying to understand why though, which I will discuss about further below.

boiler wrote:
28 Jan 2022, 15:48
When you added that line, you reversed things and added stuff so that you thought the first part would be a single-press, then you added stuff below that would then be executed when double-pressed. That's not what I said to do, and that's wrong:
I see that now. Sorry about that :oops:

boiler wrote:
28 Jan 2022, 15:48
I just said to change the first line to make that block of code execute on a double-press. Now run that so you can understand it before we move on to having a single-press work as well.
I think we are on the same page now ✌️

boiler wrote:
28 Jan 2022, 16:17
Now, to be able to distinguish between single and double-presses (or more), you can't just use that same code and execute code when the other condition is true because that condition will always be true whether it's on a single press or on the first press of a double-press.
I am really confused on what you are saying in the bolded and italicized part above. Are you discussing how this If statement functions? If so, are you communicating this message?

Code: Select all

If (evaluates to true)
{
     ; This code section executes
}
; You cannot place code here. If you do, regardless of what the If statement evaluates to, this code will always be executed.
Return
If you are saying this, I think I definitely have an issue with understanding how the If statement works. Because I thought it meant this:

Code: Select all

If (evaluates to true)
{
     ; This code section executes
}
Else ; If did not evaluate to true
{
     ; This code section executes [b]instead[/b]
}
Return
It is obvious my understanding doesn't hold up though. Because when I use the full If/Else template, the script produces an identical result to the version without the Else statement and corresponding brackets added. I thought If/Else meant Either/Or. Yet the moment I place anything in the Else brackets the Else section executes always. How can that be?

boiler wrote:
28 Jan 2022, 16:17
You need to have the code wait to see if there is another press and get a count before acting on either, and that requires a different approach.
Isn't A_TimeSincePriorHotkey < 400 similar to the SetTimer, KeyF7, -400? That is, both track time inside a variable. I feel I understand the SetTimer script well. Seems very straight forward. I guess I am just hung up on the fact If statements don't function like I expect them too. I want very badly to understand why the if (A_PriorHotkey = "F3" && A_TimeSincePriorHotkey < 350) code doesn't operate as I expect.

boiler wrote:
28 Jan 2022, 16:17
Here is the code they've implemented there, but I changed it for F7 like you were looking to do:
That is extremely helpful and kind of you. Thank you very much :rainbow: :D
Last edited by Mulsiphix on 30 Jan 2022, 11:51, edited 1 time in total.

User avatar
boiler
Posts: 16951
Joined: 21 Dec 2014, 02:44

Re: Help, Double-Press Script Executes Single Press Immediately  Topic is solved

Post by boiler » 29 Jan 2022, 13:54

Mulsiphix wrote:
boiler wrote:
28 Jan 2022, 16:17
Now, to be able to distinguish between single and double-presses (or more), you can't just use that same code and execute code when the other condition is true because that condition will always be true whether it's on a single press or on the first press of a double-press.
I am really confused on what you are saying in the bolded and italicized part above. Are you discussing how this If statement functions? If so, are you communicating this message?
You are right about how the if statement functions. That's actually not the issue. You have to think about it this way: Just because the condition for a double-press is not true, it doesn't mean that the only other result is a single-press. There are actually three possible results that it could be each time the hotkey is registered (if we ignore the possibility of triple-presses and beyond for now):
  1. It is the first press of what will be a double-press
  2. It is the second press of a double-press
  3. It is a single-press
And as I mentioned, this approach can't distinguish between anything other than #2 above (the second press of a double-press) and either of the other two. So it doesn't try to distinguish. It just acts on a double-press. So you might ask what good is that? The reason it is still useful is because in the example, they use the ~ modifier with the hotkey, which means the single-press of the hotkey still retains its native function. You now get the added benefit of performing some other action in addition if it was a double-press.

So then you are wondering why it can't distinguish between the possible results 1 and 3 above. It's because the hotkey fires right away, so as soon as it receives the first press of what may be a double-press, it's going to react anyway because it meets the criteria for what is intended to be a single-press (the same key hasn't been pressed immediately before it). Logically, the only way it can know not to act on the very first press (yet) is to wait and see if another press is going to immediately follow, and that's what the SetTimer approach does. You can probably see when you use that version of the script that the result of a single-press doesn't occur immediately. That is because it has to wait a fraction of a second first to make sure it's not followed by a second press to know this is indeed a single-press and not just the first press of a double-press.

Mulsiphix wrote:

Code: Select all

If (evaluates to true)
{
     ; This code section executes
}
; You cannot place code here. If you do, regardless of what the If statement evaluates to, this code will always be executed.
Return
If you are saying this, I think I definitely have an issue with understanding how the If statement works. Because I thought it meant this:

Code: Select all

If (evaluates to true)
{
     ; This code section executes
}
Else ; If did not evaluate to true
{
     ; This code section executes [b]instead[/b]
}
Return
It is obvious my understanding doesn't hold up though. Because when I use the full If/Else template, the script produces an identical result to the version without the Else statement and corresponding brackets added. I thought If/Else meant Either/Or. Yet the moment I place anything in the Else brackets the Else section executes always. How can that be?
That is how if/else works, but again, you are operating under the false impression that the only result of that key being pressed other than the second press of a double-press is a single-press. But it could be the first press of what was intended to be a double-press. So if you act upon that key press assuming it's a single-press, you are cutting off the possibility of a double-press because you didn't wait to see if another press is coming.

Mulsiphix wrote:
boiler wrote:
28 Jan 2022, 16:17
You need to have the code wait to see if there is another press and get a count before acting on either, and that requires a different approach.
Isn't A_TimeSincePriorHotkey < 400 similar to the SetTimer, KeyF7, -400? That is, both track time since inside a variable. I feel I understand the SetTimer script well. Seems very straight forward. I guess I am just hung up on the fact If statements don't function like I expect them too. I want very badly to understand why the if (A_PriorHotkey = "F3" && A_TimeSincePriorHotkey < 350) code doesn't operate as I expect.
They're not the same nor really even similar. The condition A_TimeSincePriorHotkey < 400 just evaluates whether a certain amount of time has passed or not since the hotkey was last pressed. It does not wait for that full time period to pass before acting. When you use SetTimer, KeyF7, -400, you are having the script actually wait 400 ms before acting. That's what allows it to see if there is another press that occurs within that time or not. As mentioned earlier, the first approach does not incorporate any waiting to see if the first press of the hotkey is immediately followed by another one before determining whether it is a single or double-press. The SetTimer approach does wait that long, and it can determine if it was pressed twice or just once within that timeframe.

Mulsiphix
Posts: 148
Joined: 20 Nov 2015, 17:56

Re: Help, Double-Press Script Executes Single Press Immediately

Post by Mulsiphix » 31 Jan 2022, 10:29

I actually understand now. Thank you so much for breaking all of this down for me. It was very frustrating feeling like I had all the pieces in front of me, yet being unable to produce the desired result. Time is hard for me to come by. It truly is my most precious resource. I know your time and effort was sizeable, helping me get from where I was to where I now am. I feel like I understand AHK a little bit better, which gives me greater confidence. Sincerely, thank you for your help boiler. It means a lot to me :dance:

User avatar
boiler
Posts: 16951
Joined: 21 Dec 2014, 02:44

Re: Help, Double-Press Script Executes Single Press Immediately

Post by boiler » 31 Jan 2022, 10:54

Glad to help. Event-driven coding such as scripts that involved hotkeys or GUI interactions can require a bit of a different mindset because you can't just look at the code and follow its flow as laid out. You have to think about what happens when a particular event occurs, whether the code is currently executing and is interrupted or is just in a waiting state.

Post Reply

Return to “Ask for Help (v1)”