QB64.org Forum

Active Forums => QB64 Discussion => Topic started by: krovit on February 16, 2020, 05:47:01 pm

Title: double click
Post by: krovit on February 16, 2020, 05:47:01 pm
Hi all,

I have to give up... can you help me understand, please, where is the error in this example?
Can you enter the missing code?

Code: QB64: [Select]
  1.  DO
  2.         j$ = INKEY$          'other controls
  3.         xxw& = _KEYHIT   'other controls
  4.  
  5.         num_click=0    
  6.         the_end=0            'other controls
  7.  
  8.  
  9.  
  10. ' here one o double click control missing code
  11. '--------------------------------------------------
  12.  
  13.  
  14.  
  15.  
  16.  
  17. LOOP UNTIL j$ <> "" OR the_end > 0
  18.  
  19.  
  20. ? num_click
  21.  
  22.  

I can't insert the wiki example.
I had solved the single click but now I need to monotorate the second click.




Title: Re: double click
Post by: krovit on February 16, 2020, 06:03:41 pm
Code: QB64: [Select]
  1.  DO
  2.         j$ = INKEY$
  3.         xxw& = _KEYHIT
  4.  
  5.         num_click=0    
  6.         the_end=0  
  7.  
  8.    
  9.         DO WHILE _MOUSEINPUT                'check mouse status
  10.                 mb% = _MOUSEBUTTON(1)
  11.         LOOP
  12.         DO WHILE mb%                 'check for button release
  13.                 i = _MOUSEINPUT
  14.                 mb% = _MOUSEBUTTON(1)
  15.                 num_click=1
  16.         LOOP
  17.  
  18.         IF num_click = 1 THEN                   'if button was pressed and released
  19.                 t = TIMER + .3
  20.                 DO WHILE TIMER < t      'check for a second press within .3 seconds
  21.                         i = _MOUSEINPUT
  22.                         IF _MOUSEBUTTON(1) THEN num_click = 2': EXIT DO
  23.                 LOOP
  24.                 'IF num_click>0 then exit do
  25.         END IF
  26.  
  27.  
  28. LOOP UNTIL j$ <> "" OR the_end > 0 or num_click>0
  29.  
  30.  
  31. ? num_click
  32.  
  33.  


So it works but then, inserting it into the real program, no. Maybe it's some variable that disturbs...

In any case is this the cleanest code to monitor the double click or do you think and be another more efficient?

Thank you
Title: Re: double click
Post by: bplus on February 16, 2020, 06:44:23 pm
OK same menu demo I gave you last time now with modification for Double Click detection:
Code: QB64: [Select]
  1. _TITLE "press menu number key or Double Click Menu Item" 'b+ 2020-02-16
  2.     CLS
  3.     PRINT "Press menu # key or double click menu item:"
  4.     FOR m = 1 TO 8
  5.         ms$ = _TRIM$(STR$(m))
  6.         PRINT ms$; ") Menu item "; ms$
  7.     NEXT
  8.     PRINT "9) Quit this demo"
  9.  
  10.  
  11.     k$ = INKEY$ '<<<<<<<<<<<<<<<<<<< update k$
  12.     WHILE _MOUSEINPUT: WEND ' <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< update mouse status
  13.  
  14.     ' ++++++++++++++++++++++++++++++++++++ Report Stuff +++++++++++++++++++++++++++++++++++++++++++++++
  15.     mb = _MOUSEBUTTON(1) OR _MOUSEBUTTON(2) OR _MOUSEBUTTON(3) ' >>>>>>>>>>>>> report mouse status
  16.     mx = _MOUSEX: my = _MOUSEY
  17.     PRINT
  18.     PRINT "INKEY$:"; k$; "   _MOUSEBUTTON(X) pressed "; mb; "  Current _MOUSEX"; mx; "  Current _MOUSEY"; my
  19.     PRINT
  20.     '-------------------------------------------------------------------------------------------------------
  21.  
  22.     IF mb AND oldMouse = 0 OR LEN(k$) THEN '>>>>> wait for oldMouse to clear to 0  so you know mb is new one!
  23.         IF TIMER(.001) - lastTime < 0 THEN curTime = TIMER(.001) + 24 * 60 * 60 ELSE curTime = TIMER(.001)
  24.         IF curTime - lastTime < .3 THEN 'double = two clicks in less than .3 sec
  25.             mb2 = 1
  26.         ELSE
  27.             lastTime = curTime 'save last click time
  28.             mb2 = 0 'clear last double click report
  29.         END IF
  30.         FOR m = 1 TO 9
  31.             IF mb2 AND my = m + 1 OR k$ = _TRIM$(STR$(m)) THEN 'for screen 0 or default my = line number
  32.                 PRINT "You selected menu"; m: _DELAY 2
  33.                 IF m = 9 THEN SYSTEM
  34.             END IF
  35.         NEXT
  36.     END IF
  37.  
  38.     'save last mouse status
  39.     oldMouse = mb ' <<<  comment on and off to see difference when hold mouse button down and go up and down menu
  40.     '          oldMouse will wait until you release mousebutton before accepting another menu selection
  41.  
  42.  
  43.     _LIMIT 60
  44.  
  45.  
  46.  
  47.  

EDIT add comments

EDIT 2: before Steve reminds us of Midnight problem here is the fix.
Title: Re: double click
Post by: krovit on February 17, 2020, 03:36:40 am
Thank you, it actually seems like the best, clean and efficient routine.

I can't import it into my program but maybe it depends on the complexity of the function in which I want to insert it.
And the flow of execution is probably not as efficient.
It's going to take me some time...
Title: Re: double click
Post by: bplus on February 17, 2020, 10:51:06 am
Does not port?

Aha! I see a problem trying to get INKEY$ when mouse polling is done outside main loop where INKEY$ polling is.

So do INKEY$ (and/or _KEYHIT) with mouse polling in a portable FUNCTION:
Code: QB64: [Select]
  1. _TITLE "press menu number key or Double Click Menu Item" 'b+ 2020-02-18
  2.     CLS
  3.     PRINT "Press menu # key or double click menu item:"
  4.     FOR m = 1 TO 8
  5.         ms$ = _TRIM$(STR$(m))
  6.         PRINT ms$; ") Menu item "; ms$
  7.     NEXT
  8.     PRINT "9) Quit this demo"
  9.  
  10.     '====================================  evaluate for double click ======================================
  11.     IF singleClick%(mx, my, k$) THEN
  12.         IF TIMER(.001) - lastTime < 0 THEN curTime = TIMER(.001) + 24 * 60 * 60 ELSE curTime = TIMER(.001)
  13.         IF curTime - lastTime < .3 THEN 'double = two clicks in less than .3 sec
  14.             mb2 = 1
  15.         ELSE
  16.             lastTime = curTime 'save last click time
  17.             mb2 = 0 'clear last double click report
  18.         END IF
  19.     END IF
  20.     ' ================================== use mb2, mx, my for double click existence and location ==========
  21.  
  22.     ' mF = 0 ' Demo using keypress or double click info
  23.     FOR m = 1 TO 9
  24.         IF mb2 AND my = m + 1 OR k$ = _TRIM$(STR$(m)) THEN 'for screen 0 or default my = line number
  25.             PRINT "You selected menu"; m: _DELAY 2
  26.             IF m = 9 THEN SYSTEM
  27.             'mF = 1
  28.         END IF
  29.     NEXT
  30.  
  31.     ' Demo use single click info when doesn't interfere with dbl click evaluation, ie doesn't delay 2nd click detection
  32.     IF my > 10 THEN PRINT " You clicked outside then menu area: col"; mx; ", row"; my: _DELAY 2
  33.     IF LEN(k$) THEN PRINT "You pressed "; k$: _DELAY 2
  34.  
  35. FUNCTION singleClick% (atX, atY, K$) 'this function waits until mousebutton is pressed and released or a key is pressed
  36.     _KEYCLEAR: atX = -1: atY = -1
  37.     DO
  38.         K$ = INKEY$ '<<<<<<<<<<<<<<<<<<< update k$
  39.         WHILE _MOUSEINPUT: WEND ' <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< update mouse status
  40.         mb = _MOUSEBUTTON(1) OR _MOUSEBUTTON(2) OR _MOUSEBUTTON(3) ' >>>>>>>>>>>>> report mouse status
  41.         mx = _MOUSEX: my = _MOUSEY
  42.  
  43.         '___________________________________ Comment this block out in app _______________________________________________
  44.         LOCATE 15
  45.         PRINT
  46.         PRINT "INKEY$:"; K$; "   _MOUSEBUTTON(X) pressed "; mb; "  Current _MOUSEX"; mx; "  Current _MOUSEY"; my
  47.         PRINT
  48.  
  49.  
  50.         IF mb AND oldMouse = 0 THEN '>>>>> wait for oldMouse to clear to 0  so you know mb is new one!
  51.             singleClick% = -1: atX = mx: atY = my
  52.         END IF
  53.         oldMouse = mb
  54.         _LIMIT 200 '<<<<<<<<<<<<<<<<<<<<<<<<<< reduce if you want to see a keypress flash
  55.     LOOP UNTIL singleClick% AND oldMouse = 0 OR LEN(K$) 'hang until mouse button is released
  56.  
  57.  
  58.  

Edit again: I keep finding improvements
Title: Re: double click
Post by: krovit on February 17, 2020, 01:50:30 pm
Maybe it's like you say.

I'm going crazy trying to understand why when it enters the routine at the first click it already thinks it has double clicked and then exits...

The version you posted is improved and even more effective than the previous one. But I'm going crazy trying...




My programs has tens of thousands of lines ... posting it is impossible. If I say it again to explain the problem, it simplifies too much.

The application are all parametric and modular.
A series of fields can be selected with the keyboard and with the mouse.
With a first click jump to the position of the field.
With the second click ... nothing happens. And that's okay!
In fact the second click, only in certain fields, activates a drop-down menu. Alternatively, on that field, I press CTRL+Enter and I get the same result.

Everything worked fine until I realized how useful it is to activate the drop-down menu with a double click.
I can't insert double click! when I'm on the field (because I pressed a click or because I got there by scrolling through the fields with the keyboard) I can't manage the double click: it doesn't notice and during the attempts I get to the maximum that the first click is already mistaken for the second and activate - or try to activate even where it doesn't exist - the drop-down menu.

I understand that it is difficult to understand in words... but in fact I seem to move your routines to the right position of mine but it no longer works! It's really frustrating.

I am one who does not give up but I know that I could spend months looking for the solution.
And I'm not that young anymore.


I will try your second version again.
In the meantime if something else comes to mind... write it! Thank you!










Title: Re: double click
Post by: bplus on February 17, 2020, 03:03:19 pm
Oh man, drop down menus!

I don't know if we could detect both a single click AND a double click for the same spot (in the same screen state), the single click would always win I would think, though I think Windows does it.

We could mouse down on a spot and release mouse at another spot far enough away to call that a double click or  otherwise mouse release at very near the same spot and call that a single click as would be expected. That way we don't have to count clicks we just distinguish how far away the mouse is when released.

Another consideration, use a right click instead of a double left click?

Ah trying to distinguish double click AND single from same spot might be possible, I have an experiment in mind... ;-)
Title: Re: double click
Post by: SMcNeill on February 17, 2020, 04:51:53 pm
Oh man, drop down menus!

I don't know if we could detect both a single click AND a double click for the same spot (in the same screen state), the single click would always win I would think, though I think Windows does it.

We could mouse down on a spot and release mouse at another spot far enough away to call that a double click or  otherwise mouse release at very near the same spot and call that a single click as would be expected. That way we don't have to count clicks we just distinguish how far away the mouse is when released.

Another consideration, use a right click instead of a double left click?

Ah trying to distinguish double click AND single from same spot might be possible, I have an experiment in mind... ;-)

I’ve got a few mouse demos which cover single/double clicks, hover events, drag events, and all, on one of these various forums.  If I can find some free time here later, I’ll try and drag one up for you guys.

The trick is in using a timer and counter with the mouse.  Basically:

FUNCTION mouseclick
    STATIC lastclicktime AS _FLOAT, oldmouse AS INTEGER
    WHILE _MOUSEINPUT: WEND
     IF lastclicktime = 0 THEN ‘there was no previous click event to deal with
         IF oldmouse = UP AND _MOUSEINPUT(1) = DOWN THEN lastclicktime = TIMER ‘start the timer
     ELSE ‘we had a previous click event
          IF lastclicktime + 0.2 > TIMER THEN
               lastclicktime = 0 ‘reset timer
               mouseclick = 1 ‘single click if there’s no double click in 0.2 seconds
         ELSE
              IF oldmouse = UP AND _MOUSEINPUT(1) = DOWN THEN
                  mouseclick = 2 ‘doubleclick
                  lastclicktimer = 0
         END IF
     END IF

    oldmouse = _MOUSEINPUT(1)
EXIT FUNCTION
Title: Re: double click
Post by: SMcNeill on February 17, 2020, 05:02:09 pm
If you’re worried about the mouse moving, just store the old x/y positions and compare them before you decide what the result is.

STATIC mx, my

If ABS(_MOUSEX - mx) > 5 OR ABS(_MOUSEY - my) THEN mousemoved = TRUE


^ With something like the above, you store the mouse x/y position when the first click event starts and then compare to see if the mouse has moved before the time is up to determine your double click vs click resolution.  I like the idea of ABS(firstposition - currentposition) < 5 instead of firstposition = currentposition type comparison as I’ve had some old mice over the years which “jiggle” +/- a few pixels almost constantly.  Give it an acceptable threshold for minor variance, before you just declare it “moved” from a single pixel’s change.   ;)
Title: Re: double click
Post by: TerryRitchie on February 17, 2020, 05:17:36 pm
Here is how I usually detect single and double mouse clicks within a looping structure. Perhaps this can help you.

Code: QB64: [Select]
  1. TYPE MOUSE
  2.     x AS INTEGER '           actual Y location of mouse cursor
  3.     y AS INTEGER '           actual X location of mouse cursor
  4.     Click AS INTEGER '       TRUE if single click occurred
  5.     DoubleClick AS INTEGER ' TRUE if double click occurred
  6.     DCTimer AS INTEGER '     double click timer
  7.     LeftDown AS INTEGER '    TRUE if left button is down
  8.     ClickX AS INTEGER
  9.     ClickY AS INTEGER
  10.  
  11. CONST FALSE = 0, TRUE = NOT FALSE
  12. CONST FPS = 60
  13.  
  14. DIM Mouse AS MOUSE
  15.  
  16. SCREEN _NEWIMAGE(640, 480, 32)
  17.  
  18.     _LIMIT FPS
  19.     CLS
  20.     UpdateMouse
  21.     IF Mouse.DoubleClick THEN SOUND 880, 1
  22.     IF Mouse.Click THEN SOUND 440, 1
  23.     _DISPLAY
  24.  
  25.  
  26. SUB UpdateMouse ()
  27.  
  28.     SHARED Mouse AS MOUSE
  29.  
  30.     Mouse.x = _MOUSEX '                     actual X coordinate
  31.     Mouse.y = _MOUSEY '                     actual Y coordinate
  32.     Mouse.DoubleClick = FALSE
  33.     Mouse.Click = FALSE
  34.     SELECT CASE _MOUSEBUTTON(1) '                   current state of left button?
  35.         CASE TRUE '                                 button is down
  36.             IF NOT Mouse.LeftDown THEN '            already held down?
  37.                 Mouse.LeftDown = TRUE '             no, remember that it is now
  38.                 IF Mouse.DCTimer THEN '             was last click less than double click timer value?
  39.                     IF ABS(Mouse.ClickX - Mouse.x) < 3 AND ABS(Mouse.ClickY - Mouse.y) < 3 THEN
  40.                         Mouse.DoubleClick = TRUE '  yes, report a double click happened
  41.                     END IF
  42.                     Mouse.DCTimer = 0 '             reset the double click timer
  43.                 ELSE '                              no, double click timer has run out
  44.                     Mouse.DCTimer = FPS \ 3 '       reset double click timer to 1/3 second
  45.                     Mouse.ClickX = Mouse.x '        remember X location of click
  46.                     Mouse.ClickY = Mouse.y '        remember Y location of click
  47.                     Mouse.Click = TRUE '            report that a single click occurred
  48.                 END IF
  49.             END IF
  50.         CASE FALSE '                                button is up
  51.             IF Mouse.LeftDown THEN '                was the left button previously down?
  52.                 Mouse.LeftDown = FALSE '            yes, report that it's now up
  53.             END IF
  54.             IF Mouse.DCTimer THEN '                 time left in double click timer?
  55.                 Mouse.DCTimer = Mouse.DCTimer - 1 ' yes, decrement value
  56.             END IF
  57.     END SELECT
  58.  
Title: Re: double click
Post by: bplus on February 17, 2020, 05:29:24 pm
You all are missing a point, we need to get keypress also and  krovit is looking for both INKEY$ and _KEYHIT.

Test and prove in working app / demo.
Title: Re: double click
Post by: SMcNeill on February 17, 2020, 05:36:51 pm
You all are missing a point, we need to get keypress also and  krovit is looking for both INKEY$ and _KEYHIT.

Test and prove in working app / demo.

Mouse in one function, keyhit in a second one, and check desired resolution results in your main loop.
Title: Re: double click
Post by: TerryRitchie on February 17, 2020, 05:49:36 pm
You all are missing a point, we need to get keypress also and  krovit is looking for both INKEY$ and _KEYHIT.

Test and prove in working app / demo.

I'm not seeing the issue here. Here is the previous code I wrote modified a bit to detect _KEYHIT and INKEY$.

Code: QB64: [Select]
  1. TYPE MOUSE
  2.     x AS INTEGER '           actual Y location of mouse cursor
  3.     y AS INTEGER '           actual X location of mouse cursor
  4.     Click AS INTEGER '       TRUE if single click occurred
  5.     DoubleClick AS INTEGER ' TRUE if double click occurred
  6.     DCTimer AS INTEGER '     double click timer
  7.     LeftDown AS INTEGER '    TRUE if left button is down
  8.     ClickX AS INTEGER
  9.     ClickY AS INTEGER
  10.  
  11. CONST FALSE = 0, TRUE = NOT FALSE
  12. CONST FPS = 60
  13.  
  14. DIM Mouse AS MOUSE
  15.  
  16. SCREEN _NEWIMAGE(640, 480, 32)
  17.  
  18.     _LIMIT FPS
  19.     CLS
  20.     UpdateMouse
  21.     IF Mouse.DoubleClick THEN SOUND 880, 1
  22.     IF Mouse.Click THEN SOUND 440, 1
  23.     PRINT "Mouse X     : "; Mouse.x
  24.     PRINT "Mouse Y     : "; Mouse.y
  25.     PRINT "Last click X: "; Mouse.ClickX
  26.     PRINT "Last click Y: "; Mouse.ClickY
  27.     PRINT "Left button is ";
  28.     IF Mouse.LeftDown THEN PRINT "DOWN" ELSE PRINT "UP"
  29.     keyhit = _KEYHIT
  30.     IF keyhit > 0 THEN oldkeyhit = keyhit
  31.     ink$ = INKEY$
  32.     IF ink$ <> "" THEN oldink$ = ink$
  33.     PRINT "Last KeyHit = "; oldkeyhit
  34.     PRINT "Last Inkey$ = "; oldink$
  35.     _DISPLAY
  36.  
  37.  
  38. SUB UpdateMouse ()
  39.  
  40.     SHARED Mouse AS MOUSE
  41.  
  42.     Mouse.x = _MOUSEX '                     actual X coordinate
  43.     Mouse.y = _MOUSEY '                     actual Y coordinate
  44.     Mouse.DoubleClick = FALSE
  45.     Mouse.Click = FALSE
  46.     SELECT CASE _MOUSEBUTTON(1) '                   current state of left button?
  47.         CASE TRUE '                                 button is down
  48.             IF NOT Mouse.LeftDown THEN '            already held down?
  49.                 Mouse.LeftDown = TRUE '             no, remember that it is now
  50.                 IF Mouse.DCTimer THEN '             was last click less than double click timer value?
  51.                     IF ABS(Mouse.ClickX - Mouse.x) < 3 AND ABS(Mouse.ClickY - Mouse.y) < 3 THEN
  52.                         Mouse.DoubleClick = TRUE '  yes, report a double click happened
  53.                     END IF
  54.                     Mouse.DCTimer = 0 '             reset the double click timer
  55.                 ELSE '                              no, double click timer has run out
  56.                     Mouse.DCTimer = FPS \ 3 '       reset double click timer to 1/3 second
  57.                     Mouse.ClickX = Mouse.x '        remember X location of click
  58.                     Mouse.ClickY = Mouse.y '        remember Y location of click
  59.                     Mouse.Click = TRUE '            report that a single click occurred
  60.                 END IF
  61.             END IF
  62.         CASE FALSE '                                button is up
  63.             IF Mouse.LeftDown THEN '                was the left button previously down?
  64.                 Mouse.LeftDown = FALSE '            yes, report that it's now up
  65.             END IF
  66.             IF Mouse.DCTimer THEN '                 time left in double click timer?
  67.                 Mouse.DCTimer = Mouse.DCTimer - 1 ' yes, decrement value
  68.             END IF
  69.     END SELECT
  70.  
  71.  
Title: Re: double click
Post by: TerryRitchie on February 17, 2020, 06:00:29 pm
Here it is modified a bit more. Now you can drag a selection box around while holding down the left mouse button. It still detects keystrokes as well while dragging the box.

Code: QB64: [Select]
  1. TYPE MOUSE
  2.     x AS INTEGER '           actual Y location of mouse cursor
  3.     y AS INTEGER '           actual X location of mouse cursor
  4.     Click AS INTEGER '       TRUE if single click occurred
  5.     DoubleClick AS INTEGER ' TRUE if double click occurred
  6.     DCTimer AS INTEGER '     double click timer
  7.     LeftDown AS INTEGER '    TRUE if left button is down
  8.     ClickX AS INTEGER
  9.     ClickY AS INTEGER
  10.  
  11. CONST FALSE = 0, TRUE = NOT FALSE
  12. CONST FPS = 60
  13.  
  14. DIM Mouse AS MOUSE
  15.  
  16. SCREEN _NEWIMAGE(640, 480, 32)
  17.  
  18.     _LIMIT FPS
  19.     CLS
  20.     UpdateMouse
  21.     IF Mouse.DoubleClick THEN SOUND 880, 1
  22.     IF Mouse.Click THEN SOUND 440, 1
  23.     PRINT "Mouse X     : "; Mouse.x
  24.     PRINT "Mouse Y     : "; Mouse.y
  25.     PRINT "Last click X: "; Mouse.ClickX
  26.     PRINT "Last click Y: "; Mouse.ClickY
  27.     PRINT "Left button is ";
  28.     IF Mouse.LeftDown THEN PRINT "DOWN" ELSE PRINT "UP"
  29.     keyhit = _KEYHIT
  30.     IF keyhit > 0 THEN oldkeyhit = keyhit
  31.     ink$ = INKEY$
  32.     IF ink$ <> "" THEN oldink$ = ink$
  33.     PRINT "Last KeyHit = "; oldkeyhit
  34.     PRINT "Last Inkey$ = "; oldink$
  35.     IF Mouse.LeftDown THEN
  36.         LINE (Mouse.ClickX, Mouse.ClickY)-(Mouse.x, Mouse.y), _RGB32(0, 0, 255), BF
  37.     END IF
  38.     _DISPLAY
  39.  
  40.  
  41. SUB UpdateMouse ()
  42.  
  43.     SHARED Mouse AS MOUSE
  44.  
  45.     Mouse.x = _MOUSEX '                     actual X coordinate
  46.     Mouse.y = _MOUSEY '                     actual Y coordinate
  47.     Mouse.DoubleClick = FALSE
  48.     Mouse.Click = FALSE
  49.     SELECT CASE _MOUSEBUTTON(1) '                   current state of left button?
  50.         CASE TRUE '                                 button is down
  51.             IF NOT Mouse.LeftDown THEN '            already held down?
  52.                 Mouse.LeftDown = TRUE '             no, remember that it is now
  53.                 IF Mouse.DCTimer THEN '             was last click less than double click timer value?
  54.                     IF ABS(Mouse.ClickX - Mouse.x) < 3 AND ABS(Mouse.ClickY - Mouse.y) < 3 THEN
  55.                         Mouse.DoubleClick = TRUE '  yes, report a double click happened
  56.                     END IF
  57.                     Mouse.DCTimer = 0 '             reset the double click timer
  58.                 ELSE '                              no, double click timer has run out
  59.                     Mouse.DCTimer = FPS \ 3 '       reset double click timer to 1/3 second
  60.                     Mouse.ClickX = Mouse.x '        remember X location of click
  61.                     Mouse.ClickY = Mouse.y '        remember Y location of click
  62.                     Mouse.Click = TRUE '            report that a single click occurred
  63.                 END IF
  64.             END IF
  65.         CASE FALSE '                                button is up
  66.             IF Mouse.LeftDown THEN '                was the left button previously down?
  67.                 Mouse.LeftDown = FALSE '            yes, report that it's now up
  68.             END IF
  69.             IF Mouse.DCTimer THEN '                 time left in double click timer?
  70.                 Mouse.DCTimer = Mouse.DCTimer - 1 ' yes, decrement value
  71.             END IF
  72.     END SELECT
  73.  
  74.  
Title: Re: double click
Post by: krovit on February 17, 2020, 06:21:54 pm
Wow, thank you for your interest!

In fact, the issue is crucial. it's also crucial because an application can't ignore the intensive use of the mouse.


Checking the timer behavior in the various examples entered in my code I see some oddities.
This is the differential within which to count the number of clicks.

Well, it seems that whatever the frequency and speed of clicks the time between one and the other is always the same (and it happens always out of the imposed range - in the examples usually 0.3 seconds). Change, when there is, you see after the ninth, tenth and over digit after the comma.

Another question:
the results are very different when treated in double precision or in a single.

In single precision the results seem completely unreliable (even have the appearance of a random value and the differential between one measurement and the other is absolutely far from the 0.3-second limit).
It seems to be for this reason - or at least that this is one of the reasons why the second click is not intercepted.

That there is a problem in the TIMER command that in some particular case may arise?
Or maybe the command is used incorrectly or improperly?

Anyway I'll try your contributions in my code to continue to check their behavior.

I need to monitor that second click! And I can't give up after so much effort and to the point where I've come.


Title: Re: double click
Post by: bplus on February 17, 2020, 07:01:04 pm
I have deployed Terry's code in my simple menu app and...
Code: QB64: [Select]
  1. _TITLE "press menu number key or Double Click Menu Item" 'b+ 2020-02-17 with TerryRitchie mouse code
  2. TYPE MOUSE
  3.     x AS INTEGER '           actual Y location of mouse cursor
  4.     y AS INTEGER '           actual X location of mouse cursor
  5.     Click AS INTEGER '       TRUE if single click occurred
  6.     DoubleClick AS INTEGER ' TRUE if double click occurred
  7.     DCTimer AS INTEGER '     double click timer
  8.     LeftDown AS INTEGER '    TRUE if left button is down
  9.     ClickX AS INTEGER
  10.     ClickY AS INTEGER
  11.  
  12. CONST FALSE = 0, TRUE = NOT FALSE
  13. CONST FPS = 60
  14. DIM Mouse AS MOUSE, m, ms$, ink$, oldink$, keyhit AS LONG, oldkeyhit AS LONG
  15. 'SCREEN _NEWIMAGE(640, 480, 32) '<<<<<<<<<<<<<< i'm thinking mouse sub should work in any screen including default
  16.     _LIMIT FPS
  17.     CLS
  18.     PRINT "Press menu # key or double click menu item:"
  19.     FOR m = 1 TO 8
  20.         ms$ = _TRIM$(STR$(m))
  21.         PRINT ms$; ") Menu item "; ms$
  22.     NEXT
  23.     PRINT "9) Quit this demo"
  24.  
  25.     UpdateMouse
  26.     'IF Mouse.DoubleClick THEN SOUND 880, 1
  27.     'IF Mouse.Click THEN SOUND 440, 1
  28.     'PRINT "Mouse X     : "; Mouse.x
  29.     'PRINT "Mouse Y     : "; Mouse.y
  30.     'PRINT "Last click X: "; Mouse.ClickX
  31.     'PRINT "Last click Y: "; Mouse.ClickY
  32.     'PRINT "Left button is ";
  33.     'IF Mouse.LeftDown THEN PRINT "DOWN" ELSE PRINT "UP"
  34.     keyhit = _KEYHIT
  35.     IF keyhit > 0 THEN oldkeyhit = keyhit
  36.     ink$ = INKEY$
  37.     IF ink$ <> "" THEN oldink$ = ink$
  38.     'PRINT "Last KeyHit = "; oldkeyhit
  39.     'PRINT "Last Inkey$ = "; oldink$
  40.     'IF Mouse.LeftDown THEN
  41.     'LINE (Mouse.ClickX, Mouse.ClickY)-(Mouse.x, Mouse.y), _RGB32(0, 0, 255), BF
  42.     'END IF
  43.     LOCATE 15, 1: PRINT "Mouse x, y:"; Mouse.x; ","; Mouse.y
  44.     FOR m = 1 TO 9
  45.         IF NOT Mouse.Click THEN
  46.             IF Mouse.DoubleClick AND Mouse.ClickY = m + 1 OR ink$ = _TRIM$(STR$(m)) THEN 'for screen 0 or default my = line number
  47.                 PRINT "You selected menu"; m: _DELAY 2
  48.                 IF m = 9 THEN SYSTEM
  49.             END IF
  50.         END IF
  51.     NEXT
  52.  
  53.     ' Demo use single click info when doesn't interfere with dbl click evaluation, ie doesn't delay 2nd click detection
  54.     IF Mouse.Click AND (Mouse.ClickY > 10) THEN PRINT " You clicked outside then menu area: col"; Mouse.ClickX; ", row"; Mouse.ClickY
  55.     PRINT "You pressed "; ink$
  56.  
  57.  
  58.     '_DISPLAY
  59.  
  60.  
  61. SUB UpdateMouse ()
  62.  
  63.     SHARED Mouse AS MOUSE
  64.  
  65.     Mouse.x = _MOUSEX '                     actual X coordinate
  66.     Mouse.y = _MOUSEY '                     actual Y coordinate
  67.     Mouse.DoubleClick = FALSE
  68.     Mouse.Click = FALSE
  69.     SELECT CASE _MOUSEBUTTON(1) '                   current state of left button?
  70.         CASE TRUE '                                 button is down
  71.             IF NOT Mouse.LeftDown THEN '            already held down?
  72.                 Mouse.LeftDown = TRUE '             no, remember that it is now
  73.                 IF Mouse.DCTimer THEN '             was last click less than double click timer value?
  74.                     IF ABS(Mouse.ClickX - Mouse.x) < 3 AND ABS(Mouse.ClickY - Mouse.y) < 3 THEN
  75.                         Mouse.DoubleClick = TRUE '  yes, report a double click happened
  76.                     END IF
  77.                     Mouse.DCTimer = 0 '             reset the double click timer
  78.                 ELSE '                              no, double click timer has run out
  79.                     Mouse.DCTimer = FPS \ 3 '       reset double click timer to 1/3 second
  80.                     Mouse.ClickX = Mouse.x '        remember X location of click
  81.                     Mouse.ClickY = Mouse.y '        remember Y location of click
  82.                     Mouse.Click = TRUE '            report that a single click occurred
  83.                 END IF
  84.             END IF
  85.         CASE FALSE '                                button is up
  86.             IF Mouse.LeftDown THEN '                was the left button previously down?
  87.                 Mouse.LeftDown = FALSE '            yes, report that it's now up
  88.             END IF
  89.             IF Mouse.DCTimer THEN '                 time left in double click timer?
  90.                 Mouse.DCTimer = Mouse.DCTimer - 1 ' yes, decrement value
  91.             END IF
  92.     END SELECT
  93.  

OK works nice, it can distinguish a click from a double. :) and it's not using TIMER().

Don't know why I was having trouble with INKEY$ and mouse, a day or 2 ago.
Title: Re: double click
Post by: TempodiBasic on February 19, 2020, 08:02:27 pm
What plenty of wonderful codes about double click!

Thanks guys!

@krovit
my 2 cents

reading this
Quote
So it works but then, inserting it into the real program, no. Maybe it's some variable that disturbs...

In any case is this the cleanest code to monitor the double click or do you think and be another more efficient?
and this one
Quote
I can't insert double click! when I'm on the field (because I pressed a click or because I got there by scrolling through the fields with the keyboard) I can't manage the double click: it doesn't notice and during the attempts I get to the maximum that the first click is already mistaken for the second and activate - or try to activate even where it doesn't exist - the drop-down menu.
Well, no dragging action in your application!

So if less variables = less change to get a glitch

my first step is
Code: QB64: [Select]
  1.   WHILE 1
  2.     DO
  3.         j$ = INKEY$
  4.         xxw& = _KEYHIT
  5.  
  6.         num_click = 0
  7.         the_end = 0
  8.         IF _MOUSEINPUT THEN ' <-- user presses left button
  9.             mb% = _MOUSEBUTTON(1) ' <-- it stores the status of left button of mouse
  10.             DO WHILE mb% 'check for button release
  11.                 i = _MOUSEINPUT
  12.                 mb% = _MOUSEBUTTON(1)
  13.                 num_click = 1
  14.             LOOP
  15.             IF num_click = 1 THEN 'if button was pressed and released
  16.                 t = TIMER + .3
  17.                 DO WHILE TIMER < t 'check for a second press within .3 seconds
  18.                     i = _MOUSEINPUT
  19.                     IF _MOUSEBUTTON(1) THEN num_click = 2
  20.                 LOOP
  21.             END IF
  22.         END IF
  23.     LOOP UNTIL j$ <> "" OR the_end > 0 OR num_click > 0
  24.  
  25.     PRINT num_click
  26.  

and this is my second step to shrink your working code to fit well in the original application

Code: QB64: [Select]
  1.  WHILE 1
  2.     DO
  3.         j$ = INKEY$
  4.         xxw& = _KEYHIT
  5.  
  6.         num_click = 0
  7.         the_end = 0
  8.         IF _MOUSEINPUT AND _MOUSEBUTTON(1) THEN ' <-- user presses left button
  9.  
  10.             num_click = 1
  11.             WHILE _MOUSEBUTTON(1)
  12.                 dummy = _MOUSEINPUT
  13.             WEND
  14.  
  15.             t = TIMER + .3
  16.             DO WHILE TIMER < t 'check for a second press within .3 seconds
  17.                 i = _MOUSEINPUT
  18.                 IF _MOUSEBUTTON(1) THEN num_click = 2
  19.             LOOP
  20.         END IF
  21.  
  22.     LOOP UNTIL j$ <> "" OR the_end > 0 OR num_click > 0
  23.  
  24.     PRINT num_click
  25.  
  26.  

here your code in scheme
  [ This attachment cannot be displayed inline in 'Print Page' view ]  
Title: Re: double click
Post by: krovit on February 21, 2020, 04:43:27 pm
Grazie Tempodibasic per l'ulteriore contributo (non mi ero accorto che il 3d era andato avanti!).

Scrivo in italiano per intendersi (mi pare che tu sia un connazionale) e poi in inglese (traduttore permettendo) per tutti gli altri.
Il tuo codice è stringato e funziona benissimo: è la sintesi di molti altri che fanno un po' di tutto e anche loro lo fanno molto bene.
Purtroppo quando vado ad inserirlo nelle mie routine continuo ad ottenere comportamenti incomprensibili.

Una delle chiavi sta anche nel DOVE si inserisce il codice di controllo del mouse. Alle volte vedo che va inserito dove... non diresti di doverlo mettere. D'altra parte la programmazione non è totalmente "ad oggetti", un certo flusso sequenziale il codice, di per se, lo ha, e quindi il "dove" assume la sua importanza.

Ho individuato il dove. E ci ho messo un codice primitivo (nel senso che davvero è ben poco elegante) ma funziona. Però... però, a mio parere la funzione TIMER ha qualche problema e il programma ne risente. Le pause diventano troppo lunghe aggiungendo solo 0.3 secondi a TIMER. Se se ne aggiungono meno il doppio click non viene intercettato in nessun caso.

La funzione TIMER funziona e non rallenta l'applicazione se la tolgo dal DO-LOOP e mi limito ad intercettare il primo click e poi a verificare se il secondo avviene entro .3 secondi. Purtroppo il monitoraggio inizia, ovviamente, dopo il primo click per cui viene perso e davvero il programma agisce solo dopo aver verificato il secondo. In altre parole, se mi aspettassi un certo comportamento con il primo click (ad esempio saltare su un campo) in effetti dovrei aspettare il secondo per stabilire se davvero si trattava di un doppio click o piuttosto di due click singoli.

Ho scritto grandi quantità di codice e fatto molta fatica a concepire un sistema completo... ma la gestione del secondo click sembra diventare una chimera. Che peccato!

Per esperienza so, ed immagino sappiamo bene tutti, che se dai all'utente la possibilità di sbagliare ad inserisce un dato... quello sbaglierà di sicuro. Il doppio click mi serve all'interno di un sistema che gestisce fino allo sfinimento l'immissione dei dati. Ad esempio se voglio un numero a 5 cifre con due decimali mi deve impedire qualsiasi cosa tranne che scrivere fino a 5 cifre con 2 decimali (prevedendo l'uso del mouse, delle correzioni, del salto ad altro campo, e chi più ne ha più ne metta).

E' facile capire che il sistema non è semplicissimo e prevede molti casi nei quali la routine del doppio click s'inserisce male e con i risultati di ho parlato.

Continuerò a provare... ci vorrà ancora molto tempo (accidenti!) e comunque faccio tesoro dei contributi che trovo in questi forum. Sono dietro a queste cose dai tempi del C64 e devo dire che questa comunità è la migliore di sempre. Anche dal punto di vista umano: sempre disponibili, mai supponenti e naturalmente molto competenti. Se puoi, dillo in giro...

Oro provo a postare il pezzetto di codice che ho elaborato sulla base dei vostri contributi e a scrivere qualcosa in inglese...

Code: QB64: [Select]
  1. FUNCTION Click%
  2.     DO
  3.         WHILE _MOUSEINPUT: WEND
  4.         mb = _MOUSEBUTTON(1) OR _MOUSEBUTTON(2) OR _MOUSEBUTTON(3) ' >>>>>>>>>>>>> report mouse status
  5.                 IF mb AND oldMouse = 0 THEN
  6.                         if plusClick% = 2 then Click%=2 else Click%=1
  7.                 end if
  8.                 oldMouse = mb
  9.     LOOP UNTIL Click%
  10.  
  11. FUNCTION plusClick%
  12. static oldMouse2       
  13.         ct=0
  14.         t3#=timer(.1)+.3
  15.     DO while timer(.1)<t3#
  16.         WHILE _MOUSEINPUT: WEND
  17.         mb2 = _MOUSEBUTTON(1) OR _MOUSEBUTTON(2) OR _MOUSEBUTTON(3)
  18.         IF mb2 AND oldMouse2 = 0 THEN
  19.                 ct=ct+1
  20.         end if
  21.         oldMouse2 = mb2
  22.     LOOP
  23.    plusClick%=ct
  24.    oldMouse2=0
  25.  

First of all it seems important - or at least very useful - that the code is put in the form of a function (you are more sure not to make mistakes and the use is simple and lean).

I understand... it's not a nice code... it's primitive, inelegant maybe redundant but as I said the examples posted - lastly also that of TempodiBasic - in my application are not effective (and I do not understand because! alone work perfectly...).

The example I put works everywhere but has annoying delays that make it in fact not advisable.
In my humble opinion the TIMER function has some problems that in some cases that I can not say manifests itself. ...

An important thing is also the WHERE you insert the mouse control. The flowchart can be very complex and perhaps double-click control should not be put where you would think but elsewhere. On the other hand, QB is not a real object programming and its sequential execution and events can only partially be managed outside of this lienarity.

What a pity! I've written thousands of lines of code to create a development environment that can serve to produce applications of various kinds on the same platform... and (maybe) I'll have to do without the double click!

For a few more months (!!!) I will keep trying... a system there has to be...








Title: Re: double click
Post by: TempodiBasic on February 21, 2020, 05:20:58 pm
Ciao Krovit se per te va bene continuo a risponderti in inglese in modo da non renderlo una comunicazione personale, d'altronde il tuo quesito è generalizzabile per gli interessi di tutti.

So I'm saying that IMHO the issue is the logic structure of the events that you are managing.

A click of a button of the mouse is a complex event made by detecting in sequence these 3 simple events
1 mouse button is up (not pressed/activated) --> 2 mouse button is down (pressed/activated) --> 3 mouse button is up (not pressed/activated)

A double click of a button of the mouse is a bigger complex event made by 2 mouse single click in a set range of time
  1 click of mouse button  --> 2 click of mouse button
  ^                                        ^
3 |     set interval of time         |
   ---------------------------------

Thinking about these informations it is clear that you can detect a double click only after have spent the set interval of time both if  you use a loop with TIMER both if you use a loop with flags.

About my attempt to shrink your initial code, I'm working on a detection in a single loop to make easier the use of a function that is the goal of your code posted.
In the while you confirm that you needn't a dragging function?
The Dragging event is another complex event (I say always complex because we have no native function that detect it and we must build something to manage it) in  which is detected the following events in sequence
1. mouse button up --> 2 mouse button down (activate/pressed) --> 3 mouse position changes --> 4 mouse button up (released/ not pressed)

Good luck


Title: Re: double click
Post by: krovit on February 21, 2020, 07:23:08 pm
Yes, of course, detecting mouse drag is an opportunity to consider to reduce the cases in which to monitor mouse behavior.

In the event of this event it would certainly be a single click. The conditions for defining double clicks are in fact the simultaneous occurrence of two conditions: 1) the reduced time between clicks; 2) have not moved.

However, to reduce the number of cases observed, additional commands must be inserted. Perhaps we have to say how much is to be agreed.

However it really seems that TIMER within a series of nested DO-LOOPs creates a kind of redundancy, of echo... which leads to delays in execution.

Thank you for the interest and also for the interesting flowchart.


____
Being human - that is, belonging to mankind - is really a mysterious, fascinating and wonderful condition, even truly supernatural. And also unmanageable (there is no "code" that can predict all behaviors and all reactions...): we are here to talk about mouse and right around and near us the covid19 reaps real victims...

Title: Re: double click
Post by: krovit on February 25, 2020, 08:09:57 am
It took me days but maybe, I say maybe, I solved my problem (with the double click mouse...  to the rest of my life I'm still working on it)

I think the solution is useful for everyone.

This function provides the following result:
1) intercepts a single click isolated;
2) intercepts a click and deems it single if the next is pressed beyond the allowed time;
3) intercept one click, and check if you perform another one within the allotted time;
4) If the second click is within the allotted time then it's a double click!

The function does not cause delays in the execution of the application that remains fluid and you don't appreciate any slowing or jumping or momentary interruption.
 
The function intercepts the first click and places the cursor on the chosen field (of course you have to have a field to jump on).
On the field where you jumped intercept the possible second click.

Ultimately the routine handles clicks as naturally as on any other windows application.

Can it be improved?

Yes, Of course. For example, a first improvement would be to check whether the variables it carries with it and continuously monitors are really all indispensable.

I'm not going to try any change, at the moment ... ;) it took me too many days to get to this point and it only takes a nothing to destroy everything!


Code: QB64: [Select]
  1.   print Click%
  2.  
  3.  
  4.  
  5.  
  6. FUNCTION Click%
  7.         DO
  8.                 WHILE _MOUSEINPUT: WEND
  9.         mb = _MOUSEBUTTON(1) OR _MOUSEBUTTON(2) OR _MOUSEBUTTON(3)
  10.                         IF mb AND oldMouse = 0 THEN
  11.                                 passato=0
  12.                                 IF plusClick%(timer) = 2 THEN
  13.                                         Click%=2
  14.                                 else
  15.                                         Click%=1
  16.                                 end if
  17.                         END IF
  18.                         oldMouse = mb
  19.     LOOP UNTIL Click%
  20. FUNCTION plusClick%(t1#)
  21. static passato,t2#
  22.         DO
  23.         WHILE _MOUSEINPUT: WEND
  24.         mb2 = _MOUSEBUTTON(1) OR _MOUSEBUTTON(2) OR _MOUSEBUTTON(3)
  25.         IF mb2=0 THEN  'mouse up
  26.                         if timer-t2# <.3 then
  27.                                 t2#=timer
  28.                                 t1#=timer
  29.                                 passato=0
  30.                                 plusClick%=2
  31.                                 exit FUNCTION
  32.                         end     if
  33.                         if timer-t1#<.3 then
  34.                                 if passato=0 then  'first mouse in time
  35.                                         passato=38
  36.                                         t1#=timer
  37.                                         t2#=timer
  38.                                         plusClick%=1
  39.                                 elseif passato=38 then  'mouse up ignored
  40.                                         passato=0
  41.                                         t1#=timer
  42.                                         t2#=timer
  43.                                         plusClick%=1
  44.                                 end if
  45.                         else  'useless to monitor yet because time has passed
  46.                                 t1#=timer
  47.                                 t2#=timer
  48.                                 passato=0
  49.                                 plusClick%=1
  50.                         end if
  51.                 END IF
  52.     LOOP until plusClick%
  53.  
  54.  
  55.  



Title: Re: double click
Post by: TempodiBasic on February 25, 2020, 07:42:33 pm
Good Krovit, it seems that you have found your way to solve the issue.
Well done.
Title: Re: double click
Post by: bplus on February 25, 2020, 09:41:51 pm
Yes congratulations krovit.

I am glad to find Terry's system thank's to this thread.
Title: Re: double click
Post by: Pete on February 25, 2020, 10:28:22 pm
Hmm, this reminded me of the code I posted in Bill's program thread.

https://www.qb64.org/forum/index.php?topic=2226.msg114708#msg114708
Title: Re: double click
Post by: krovit on February 26, 2020, 03:43:59 am
Thank you all!

There is no perfect algorithm, there are only those that work better than others in certain contexts.
The real trick is to predict the predictable and try to manage it better writing a code that is as adaptable to any situation without too upheavals... but it's not always possible.
The greater the needs and the greater the difficulty in solving them.

Everyone's contribution is important: where one does not arrive another and vice versa.
And it applies to anything in life...