QB64.org Forum

Active Forums => Programs => Topic started by: SMcNeill on December 04, 2019, 10:58:01 am

Title: Extended Input
Post by: SMcNeill on December 04, 2019, 10:58:01 am
Several times over the years, I've seen people posting and asking questions like, "Why can't I paste text into INPUT?", or, "Why doesn't INPUT accept ALT-number input?" 

The answer has always been the same:  "It doesn't.  You'll just have to write your own input handler to do that!"

So, after ages of telling people they had to write their own, I finally needed to write my own input handler, so I thought I'd share:

Code: [Select]
_CONTROLCHR OFF
PRINT ExtendedInput$



FUNCTION ExtendedInput$
    PCOPY 0, 1
    A = _AUTODISPLAY: X = POS(0): Y = CSRLIN
    _KEYCLEAR
    DO
        PCOPY 1, 0
        IF _KEYDOWN(100307) OR _KEYDOWN(100308) THEN AltDown = -1 ELSE AltDown = 0
        k = _KEYHIT
        IF AltDown THEN
            SELECT CASE k 'ignore all keypresses except ALT-number presses
                CASE 48 TO 57: AltWasDown = -1: alt$ = alt$ + CHR$(k)
            END SELECT
        ELSE
            SELECT CASE k 'without alt, add any keypresses to our input
                CASE 8: oldin$ = in$: in$ = LEFT$(in$, LEN(in$) - 1) 'backspace to erase input
                CASE 9: oldin$ = in$: in$ = in$ + SPACE$(4) 'four spaces for any TAB entered
                CASE 32 TO 128
                    IF _KEYDOWN(100305) OR _KEYDOWN(100306) THEN
                        IF k = 118 OR k = 86 THEN oldin$ = in$: in$ = in$ + _CLIPBOARD$ 'ctrl-v paste
                        IF k = 122 OR k = 90 THEN SWAP in$, oldin$ 'ctrl-z undo
                    ELSE
                        oldin$ = in$: in$ = in$ + CHR$(k) 'add input to our string
                    END IF
            END SELECT
        END IF
        alt$ = RIGHT$(alt$, 3)
        IF AltWasDown = -1 AND AltDown = 0 THEN
            v = VAL(alt$)
            IF v >= 0 AND v <= 255 THEN in$ = in$ + CHR$(v)
            alt$ = "": AltWasDown = 0
        END IF
        blink = (blink + 1) MOD 30
        LOCATE Y, X: IF blink \ 15 THEN PRINT in$ ELSE PRINT in$ + "_"
        _DISPLAY
        _LIMIT 30
    LOOP UNTIL k = 13

    PCOPY 1, 0
    LOCATE Y, X: PRINT in$
    ExtendedInput$ = in$
    IF A THEN _AUTODISPLAY
END FUNCTION

This is used as basically a replacement for INPUT$, and handles quite a few special cases for us.


Like INPUT, this pauses program execution until we finish our input, and it plays nicely with background processes. 



Try it out and play around with it.  See if it performs as you'd expect/want it to.  Type some junk in it.  Then hold down ALT and type in a few numeric codes. (ALT-65 is "A", ALT-1 is the little smiley face, for example.)  Try CTRL-V to paste something, and CTRL-Z to undo your last input (which can be quite useful if you paste the wrong junk into your text with CTRL-V). 

I think this should be easily expandable so people could customize it to suit their own specific needs, if they have any which is different from what it's currently doing.  At the moment, it's doing all that I'd expect it to do, for me, and I hope others might find it useful as well. 

At least now, when somebody asks about "Why can't INPUT do XXXX" again in the future, I can at least say, "Take a look at this, see if it suits your needs, and if not, maybe it'll be a nice starting point to help you write your own input handler."  :D
Title: Re: Extended Input
Post by: bplus on December 04, 2019, 11:04:35 am
Very nice Steve, thanks!
Title: Re: Extended Input
Post by: SMcNeill on December 04, 2019, 11:12:36 am
Slight tweak made to make certain the display didn't leave a stray blinker behind when we hit enter.  :)
Title: Re: Extended Input
Post by: bplus on December 04, 2019, 11:18:29 am
Hey Steve is it possible to do multiple lines, CHR$(10) Line Feed? That would be really cool!
Title: Re: Extended Input
Post by: SMcNeill on December 04, 2019, 11:24:19 am
Hey Steve is it possible to do multiple lines, CHR$(10) Line Feed? That would be really cool!

Wouldn't you just take out the first line so that _CONTROLCHR was ON instead of OFF, and then type in input with ALT-10.   

Say an INPUT of "As" + ALT-10 + "the world turns"... 

The above should display as :

"As"
"the world turns"

(And that CHR$(10) is preserved in there for formatting.)
Title: Re: Extended Input
Post by: Pete on December 04, 2019, 11:34:48 am
What??? No INKEY$ model available??? BOOOOOOOOOOOOO!!!

Pete :D

You know you can't please everyone, so you have to please myself...
Title: Re: Extended Input
Post by: Cobalt on December 14, 2019, 11:09:41 pm
What no SHIFT+INSERT support!?! what a rip!   WAAAAHHHHHhhhhh....  XD

As usual, nice work Steve.
Title: Re: Extended Input
Post by: SMcNeill on December 15, 2019, 02:54:31 pm
Extended Extended Input:

Code: QB64: [Select]
  1. PRINT ExtendedInput$
  2.  
  3.  
  4.  
  5. FUNCTION ExtendedInput$
  6.     PCOPY 0, 1
  7.     A = _AUTODISPLAY: X = POS(0): Y = CSRLIN
  8.     CP = 0: OldCP = 0 'Cursor Position
  9.     _KEYCLEAR
  10.     DO
  11.         PCOPY 1, 0
  12.         IF _KEYDOWN(100307) OR _KEYDOWN(100308) THEN AltDown = -1 ELSE AltDown = 0
  13.         k = _KEYHIT
  14.         IF AltDown THEN
  15.             SELECT CASE k 'ignore all keypresses except ALT-number presses
  16.                 CASE 48 TO 57: AltWasDown = -1: alt$ = alt$ + CHR$(k)
  17.             END SELECT
  18.         ELSE
  19.             SELECT CASE k 'without alt, add any keypresses to our input
  20.                 CASE 8
  21.                     oldin$ = in$
  22.                     IF CP > 0 THEN OldCP = CP: CP = CP - 1
  23.                     in$ = LEFT$(in$, CP) + MID$(in$, CP + 2) 'backspace to erase input
  24.                 CASE 9
  25.                     oldin$ = in$
  26.                     in$ = LEFT$(in$, CP) + SPACE$(4) + MID$(in$, CP + 1) 'four spaces for any TAB entered
  27.                     OldCP = CP
  28.                     CP = CP + 4
  29.                 CASE 32 TO 128
  30.                     IF _KEYDOWN(100305) OR _KEYDOWN(100306) THEN
  31.                         IF k = 118 OR k = 86 THEN
  32.                             oldin$ = in$
  33.                             in$ = LEFT$(in$, CP) + _CLIPBOARD$ + MID$(in$, CP + 1) 'ctrl-v paste
  34.                             'CTRL-V leaves cursor in position before the paste, without moving it after.
  35.                             'Feel free to modify that behavior here, if you want it to move to after the paste.
  36.                         END IF
  37.                         IF k = 122 OR k = 90 THEN SWAP in$, oldin$: SWAP OldCP, CP 'ctrl-z undo
  38.                     ELSE
  39.                         oldin$ = in$
  40.                         in$ = LEFT$(in$, CP) + CHR$(k) + MID$(in$, CP + 1) 'add input to our string
  41.                         OldCP = CP
  42.                         CP = CP + 1
  43.                     END IF
  44.                 CASE 18176 'Home
  45.                     CP = 0
  46.                 CASE 20224 'End
  47.                     CP = LEN(in$)
  48.                 CASE 21248 'Delete
  49.                     oldin$ = in$
  50.                     in$ = LEFT$(in$, CP) + MID$(in$, CP + 2)
  51.                 CASE 19200 'Left
  52.                     CP = CP - 1
  53.                     IF CP < 0 THEN CP = 0
  54.                 CASE 19712 'Right
  55.                     CP = CP + 1
  56.                     IF CP > LEN(in$) THEN CP = LEN(in$)
  57.             END SELECT
  58.         END IF
  59.         alt$ = RIGHT$(alt$, 3)
  60.         IF AltWasDown = -1 AND AltDown = 0 THEN
  61.             v = VAL(alt$)
  62.             IF v >= 0 AND v <= 255 THEN in$ = in$ + CHR$(v)
  63.             alt$ = "": AltWasDown = 0
  64.         END IF
  65.         blink = (blink + 1) MOD 30
  66.         LOCATE Y, X
  67.         PRINT LEFT$(in$, CP);
  68.         IF blink \ 15 THEN PRINT " "; ELSE PRINT "_";
  69.         PRINT MID$(in$, CP + 1)
  70.  
  71.         _DISPLAY
  72.         _LIMIT 30
  73.     LOOP UNTIL k = 13
  74.  
  75.     PCOPY 1, 0
  76.     LOCATE Y, X: PRINT in$
  77.     ExtendedInput$ = in$
  78.  

Added on to the basic functionality presented in the original post so that now:


These basic capabilities make going back and editing our text much simpler and efficient than just hitting backspace until we find our glitch and then retype everything over from that point forward.



And the reason I didn't just replace the original post with this one is simply in case someone wants to write their own extended input handler.  Often, it's best to keep the base process as simple as possible, if others are going to build upon it later, rather than overly complicate things which makes it harder for them to sort out what's going on and alter/add to the original.  So, I figure in this case, simply posting both versions is the simplest way to go -- a basic routine which folks can enhance themselves to suit their personal needs, and a more robust version which offers a few more bells and whistles for plug-n-play usability.
Title: Re: Extended Input
Post by: STxAxTIC on December 16, 2019, 10:04:43 pm
Heya Steve,

So I was thinking (over the years but) recently (due to this post) that a different (and in some cases, better) extended input would not hang up the whole program with a DO... LOOP right in the function, but instead run on a timer to capture input and keep track of a cursor in exactly the way you make it appear, but let other things run while this is happening.

Case in point: my "universal" Sprezzo program, in client mode, can also log into chat rooms. Problem is, in order to watch the chat happen, an INPUT-like statement can't be open. Having something that passively log the keystrokes is a lot better. The old Sprezzo in fact did this, but the function wasn't just a plug-n'-play black box like you're going for.

Short story long, do you find it worth making a passive extended input function? Cause I might, but don't wanna steal your thunder. BUT... since you're kindof setting a standard with this one, there's no reason not to have a second one look and feel the same.
Title: Re: Extended Input
Post by: SMcNeill on December 16, 2019, 10:34:30 pm
Heya Steve,

So I was thinking (over the years but) recently (due to this post) that a different (and in some cases, better) extended input would not hang up the whole program with a DO... LOOP right in the function, but instead run on a timer to capture input and keep track of a cursor in exactly the way you make it appear, but let other things run while this is happening.

Case in point: my "universal" Sprezzo program, in client mode, can also log into chat rooms. Problem is, in order to watch the chat happen, an INPUT-like statement can't be open. Having something that passively log the keystrokes is a lot better. The old Sprezzo in fact did this, but the function wasn't just a plug-n'-play black box like you're going for.

Short story long, do you find it worth making a passive extended input function? Cause I might, but don't wanna steal your thunder. BUT... since you're kindof setting a standard with this one, there's no reason not to have a second one look and feel the same.

Terry Ritchie’s GLInput Library is passive as you’re describing it.  Your program can run in the background, with it capturing input for you.  I’ve used it several times in the past, and it’s extremely powerful and flexible once you sort out how to use it.  What I’ve got here is mainly just a simple substitute for INPUT, and it’s intended to pause program execution until ENTER is hit (just as INPUT does).

If you need passive input, check out Terry’s library before you go about building your own.  Even if it’s not what you needed, it’s well commented and documented, and might help you on the path of crafting your own easier.  ;)
Title: Re: Extended Input
Post by: STxAxTIC on December 16, 2019, 10:43:00 pm
Quote
Terry Ritchie’s GLInput Library is passive as you’re describing it.  Your program can run in the background, with it capturing input for you.  I’ve used it several times in the past, and it’s extremely powerful and flexible once you sort out how to use it.  What I’ve got here is mainly just a simple substitute for INPUT, and it’s intended to pause program execution until ENTER is hit (just as INPUT does).

If you need passive input, check out Terry’s library before you go about building your own.  Even if it’s not what you needed, it’s well commented and documented, and might help you on the path of crafting your own easier.  ;)

Say, good to know. My ego pretty much doesn't allow me to use someone else's code, but I *may* give it a look. Tee hee hee.
Title: Re: Extended Input
Post by: bplus on December 16, 2019, 10:44:41 pm
Doesn't _LIMIT allow background processing if there is any extra time in the loop?
Title: Re: Extended Input
Post by: STxAxTIC on December 16, 2019, 11:36:22 pm
This could be true I suppose if the other things run on timers - otherwise I figure you'd be trying to violate sequence.
Title: Re: Extended Input
Post by: SMcNeill on February 02, 2021, 01:15:51 am
And an upgrade to this that no one was asking for -- OEInput!

Code: QB64: [Select]
  1. OEInput "{P}Enter a password =>", a$
  2.  
  3. OEInput "{UI}Enter an unsigned integer =>", a$
  4.  
  5. OEInput "{I}Enter an integer=>", a$
  6.  
  7. OEInput "{F}Enter a float=>", a$
  8.  
  9. OEInput "{IL10}Enter an integer of less than 10 digits =>", a$
  10.  
  11. OEInput "{X15,Y15}Enter whatever at loc 15,15 =>", a$
  12.  
  13. OEInput "{D}Check to make certain paste is disabled here =>", a$
  14.  
  15.  
  16.  
  17.  
  18. SUB OEInput (prompt$, result$) 'Over Engineered Input
  19.     PCOPY 0, 1
  20.     A = _AUTODISPLAY: X = POS(0): Y = CSRLIN
  21.     CP = 0: OldCP = 0 'Cursor Position
  22.     _KEYCLEAR
  23.  
  24.     IF LEFT$(prompt$, 1) = "{" THEN 'possible limiter
  25.         i = INSTR(prompt$, "}")
  26.         IF i THEN 'yep, we have something!
  27.             limiter$ = UCASE$(MID$(prompt$, 2, i - 2))
  28.             IF INSTR(limiter$, "U") THEN limit = limit OR 1 'Unsigned
  29.             IF INSTR(limiter$, "I") THEN 'can't limit to BOTH an integer AND a float
  30.                 limit = limit OR 2 'Integer
  31.             ELSEIF INSTR(limiter$, "F") THEN
  32.                 limit = limit OR 4 'Float
  33.             END IF
  34.             IF INSTR(limiter$, "P") THEN password_protected = -1: limit = limit OR 8 'don't show passwords.
  35.             IF INSTR(limiter$, "L") THEN 'Length Limitation
  36.                 limit = limit OR 16
  37.                 jstart = INSTR(limiter$, "L")
  38.                 DO
  39.                     j = j + 1
  40.                     m$ = MID$(limiter$, jstart + j, 1)
  41.                 LOOP UNTIL m$ < "0" OR m$ > "9"
  42.                 length_limit = VAL(MID$(limiter$, jstart + 1, j - 1))
  43.             END IF
  44.             IF INSTR(limiter$, "X") THEN 'X position on screen
  45.                 limit = limit OR 32
  46.                 jstart = INSTR(limiter$, "X")
  47.                 DO
  48.                     j = j + 1
  49.                     m$ = MID$(limiter$, jstart + j, 1)
  50.                 LOOP UNTIL m$ < "0" OR m$ > "9"
  51.                 X = VAL(MID$(limiter$, jstart + 1, j - 1))
  52.             END IF
  53.             IF INSTR(limiter$, "Y") THEN 'Y position on scren
  54.                 limit = limit OR 64
  55.                 jstart = INSTR(limiter$, "Y")
  56.                 DO
  57.                     j = j + 1
  58.                     m$ = MID$(limiter$, jstart + j, 1)
  59.                 LOOP UNTIL m$ < "0" OR m$ > "9"
  60.                 Y = VAL(MID$(limiter$, jstart + 1, j - 1))
  61.             END IF
  62.             IF INSTR(limiter$, "D") THEN disable_paste = -1: limit = limit OR 128 'disable paste
  63.         END IF
  64.         IF limit <> 0 THEN prompt$ = MID$(prompt$, i + 1)
  65.     END IF
  66.  
  67.     DO
  68.         PCOPY 1, 0
  69.         IF _KEYDOWN(100307) OR _KEYDOWN(100308) THEN AltDown = -1 ELSE AltDown = 0
  70.         k = _KEYHIT
  71.         IF AltDown THEN
  72.             SELECT CASE k 'ignore all keypresses except ALT-number presses
  73.                 CASE -57 TO -48: AltWasDown = -1: alt$ = alt$ + CHR$(-k)
  74.             END SELECT
  75.         ELSE
  76.             SELECT CASE k 'without alt, add any keypresses to our input
  77.                 CASE 8
  78.                     oldin$ = in$
  79.                     IF CP > 0 THEN OldCP = CP: CP = CP - 1
  80.                     in$ = LEFT$(in$, CP) + MID$(in$, CP + 2) 'backspace to erase input
  81.                 CASE 9
  82.                     oldin$ = in$
  83.                     in$ = LEFT$(in$, CP) + SPACE$(4) + MID$(in$, CP + 1) 'four spaces for any TAB entered
  84.                     OldCP = CP
  85.                     CP = CP + 4
  86.                 CASE 32 TO 128
  87.                     IF _KEYDOWN(100305) OR _KEYDOWN(100306) THEN
  88.                         IF k = 118 OR k = 86 THEN
  89.                             IF disable_paste = 0 THEN
  90.                                 oldin$ = in$
  91.                                 in$ = LEFT$(in$, CP) + _CLIPBOARD$ + MID$(in$, CP + 1) 'ctrl-v paste
  92.                                 'CTRL-V leaves cursor in position before the paste, without moving it after.
  93.                                 'Feel free to modify that behavior here, if you want it to move to after the paste.
  94.                             END IF
  95.                         END IF
  96.                         IF k = 122 OR k = 90 THEN SWAP in$, oldin$: SWAP OldCP, CP 'ctrl-z undo
  97.                     ELSE
  98.                         check_input:
  99.                         oldin$ = in$
  100.                         IF limit AND 1 THEN 'unsigned
  101.                             IF k = 43 OR k = 45 THEN _CONTINUE 'remove signs +/-
  102.                         END IF
  103.                         IF limit AND 2 THEN 'integer
  104.                             IF k = 45 AND CP = 0 THEN GOTO good_input 'only allow a - sign for the first digit
  105.                             IF k < 48 OR k > 57 THEN _CONTINUE 'remove anything non-numeric
  106.                         END IF
  107.                         IF limit AND 4 THEN 'float
  108.                             IF k = 45 AND CP = 0 THEN GOTO good_input 'only allow a - sign for the first digit
  109.                             IF k = 46 AND period = 0 THEN period = -1: GOTO good_input 'only one decimal point
  110.                             IF k < 48 OR k > 57 THEN _CONTINUE 'remove anything non-numeric
  111.                         END IF
  112.                         good_input:
  113.                         IF CP < length_limit OR length_limit = 0 THEN
  114.                             in$ = LEFT$(in$, CP) + CHR$(k) + MID$(in$, CP + 1) 'add input to our string
  115.  
  116.                             OldCP = CP
  117.                             CP = CP + 1
  118.                         END IF
  119.                     END IF
  120.                 CASE 18176 'Home
  121.                     CP = 0
  122.                 CASE 20224 'End
  123.                     CP = LEN(in$)
  124.                 CASE 21248 'Delete
  125.                     oldin$ = in$
  126.                     in$ = LEFT$(in$, CP) + MID$(in$, CP + 2)
  127.                 CASE 19200 'Left
  128.                     CP = CP - 1
  129.                     IF CP < 0 THEN CP = 0
  130.                 CASE 19712 'Right
  131.                     CP = CP + 1
  132.                     IF CP > LEN(in$) THEN CP = LEN(in$)
  133.             END SELECT
  134.         END IF
  135.         alt$ = RIGHT$(alt$, 3)
  136.         IF AltWasDown = -1 AND AltDown = 0 THEN
  137.             v = VAL(alt$)
  138.             IF v >= 0 AND v <= 255 THEN
  139.                 k = v
  140.                 alt$ = "": AltWasDown = 0
  141.                 GOTO check_input
  142.             END IF
  143.         END IF
  144.         blink = (blink + 1) MOD 30
  145.         LOCATE Y, X
  146.         PRINT prompt$;
  147.         IF password_protected THEN
  148.             PRINT STRING$(LEN(LEFT$(in$, CP)), "*");
  149.             IF blink \ 15 THEN PRINT " "; ELSE PRINT "_";
  150.             PRINT STRING$(LEN(MID$(in$, CP + 1)), "*")
  151.         ELSE
  152.             PRINT LEFT$(in$, CP);
  153.             IF blink \ 15 THEN PRINT " "; ELSE PRINT "_";
  154.             PRINT MID$(in$, CP + 1)
  155.         END IF
  156.  
  157.         _DISPLAY
  158.         _LIMIT 30
  159.     LOOP UNTIL k = 13
  160.  
  161.     PCOPY 1, 0
  162.     LOCATE Y, X:
  163.     IF password_protected THEN
  164.         PRINT prompt$; STRING$(LEN(in$), "*")
  165.     ELSE
  166.         PRINT prompt$; in$
  167.     END IF
  168.     result$ = in$
  169.  

It does all it did before, but now you can send it a prompt to print to the screen, much like INPUT "Enter a number => "; n$

And, since I'm adding a prompt, I'm adding it's own little control system of limiters which we play around with for our text.

Control starts as the first digits of your prompt and begin with an "{" and end with an "}", and we add our control codes inside those braces.

We can limit by number (integer, unsigned, or float).  We can limit by length.  We can position our prompt without using locate. We can hide user input for passwords.  We can disable paste capabilities (which probably don't make sense to have, if we're going to limit input with other things...) Alt-### can be used to enter ASCII characters via the num pad now.

We can do one of these things, or all of these things, or none of these things!

And, if folks need something else added to this, feel free to speak up while I'm still at home and have a few days where I don't have to take care of mom.  (Which gives you the next 3 days, or so, to make requests for additions/alterations.)

My philosophy is, "If you're going to over engineer something, then by God, do it right!" 
Title: Re: Extended Input
Post by: STxAxTIC on February 02, 2021, 01:21:20 am
Quote
My philosophy is, "If you're going to over engineer something, then by God, do it right!"

Ahem, where's OPTION _EXPLICIT?
Title: Re: Extended Input
Post by: STxAxTIC on February 02, 2021, 01:27:34 am
Aright here's a bug for ya.

In "enter a float"... type something with a decimal, and then backspace through the decimal... you can never type a decimal again after that
Title: Re: Extended Input
Post by: SMcNeill on February 02, 2021, 01:33:56 am
Ahem, where's OPTION _EXPLICIT?
I dunno.  In somebody else’s code??  Personally, I never use it.  It defeats the purpose of having BASIC allocate and define your variables on the fly for you.  :P

Feel free to add it if you need it.  I usually don’t get around to stuff like that until I’m completely finished tinkering with the code.

I’m thinking for floats to have them in a F12.2 format.  The example there would allow up to 12 digits before the decimal, 2 after.

I'll also add a command to move cursor to after a paste.

Maybe even add an option to auto-comma values...

So still some more tweaking to go here.
Title: Re: Extended Input
Post by: SMcNeill on February 02, 2021, 01:34:37 am
Aright here's a bug for ya.

In "enter a float"... type something with a decimal, and then backspace through the decimal... you can never type a decimal again after that

Good catch.  I'll address that tomorrow as well. ;)
Title: Re: Extended Input
Post by: STxAxTIC on February 02, 2021, 01:40:27 am
While the hood is open, maybe it's worth dressing up the whole thing in an ascii-box too? Then it'll be an alternative to this box used in several projects around here:

  [ This attachment cannot be displayed inline in 'Print Page' view ]  
Title: Re: Extended Input
Post by: SMcNeill on February 02, 2021, 10:41:17 am
@STxAxTIC -- Try this out and see if it's working as intended.  We now limit floats by digits before and after the decimal point, and that period shouldn't be a problem anymore, like you reported up above.

Code: QB64: [Select]
  1. OEInput "{F-.2}Enter a float (Unlimited.2)=>", a$
  2. OEInput "{F0.2}Enter a float (0.2) =>", a$
  3. OEInput "{F9.2}Enter a float (9.2) =>", a$
  4. OEInput "{F9.-}Enter a float (9.unlimited) =>", a$
  5.  
  6. OEInput "{P}Enter a password =>", a$
  7.  
  8. OEInput "{UI}Enter an unsigned integer =>", a$
  9.  
  10. OEInput "{I}Enter an integer=>", a$
  11.  
  12. OEInput "{IL10}Enter an integer of less than 10 digits =>", a$
  13.  
  14. OEInput "{X15,Y15}Enter whatever at loc 15,15 =>", a$
  15.  
  16. OEInput "{D}Check to make certain paste is disabled here =>", a$
  17.  
  18. OEInput "Enter whatever here, but use paste.  See if the cursor remains BEFORE the paste. => ", a$
  19.  
  20. OEInput "{V}Enter whatever here, but use paste.  See if the cursor moves AFTER the paste. => ", a$
  21.  
  22. PRINT "Press <ANY KEY> to clear the screen -- we're getting full!"
  23.  
  24. OEInput "{H}This should hide itself, after you enter whatever =>", a$
  25. PRINT "Is the question and input gone?  This line should be at the top left corner of your screen."
  26. PRINT "And here's your input => "; a$
  27.  
  28.  
  29.  
  30. SUB OEInput (prompt$, result$) 'Over Engineered Input
  31.     'limit VALUES:
  32.     '1 = Unsigned
  33.     '2 = Integer
  34.     '4 = Float
  35.     '8 = Who cares. It's handled via internal variables and we don't need to know a type for it.
  36.  
  37.     PCOPY 0, 1
  38.     A = _AUTODISPLAY: X = POS(0): Y = CSRLIN
  39.     OX = X: OY = Y 'original x and y positions
  40.     CP = 0: OldCP = 0 'Cursor Position
  41.     _KEYCLEAR
  42.     length_limit = -1 'unlimited length input, by default
  43.  
  44.     IF LEFT$(prompt$, 1) = "{" THEN 'possible limiter
  45.         i = INSTR(prompt$, "}")
  46.         IF i THEN 'yep, we have something!
  47.             limiter$ = UCASE$(MID$(prompt$, 2, i - 2))
  48.             IF INSTR(limiter$, "U") THEN limit = limit OR 1 'Unsigned
  49.             IF INSTR(limiter$, "I") THEN 'can't limit to BOTH an integer AND a float
  50.                 limit = limit OR 2 'Integer
  51.             ELSEIF INSTR(limiter$, "F") THEN
  52.                 limit = limit OR 4 'Float
  53.                 float_before_limit = GetValue(limiter$, "F")
  54.                 float_after_limit = GetValue(MID$(limiter$, INSTR(limiter$, "F") + 1), ".")
  55.             END IF
  56.         END IF
  57.         IF INSTR(limiter$, "P") THEN password_protected = -1: limit = limit OR 8 'don't show passwords.
  58.         IF INSTR(limiter$, "L") THEN 'Length Limitation
  59.             limit = limit OR 8
  60.             length_limit = GetValue(limiter$, "L")
  61.         END IF
  62.         IF INSTR(limiter$, "X") THEN 'X position on screen
  63.             limit = limit OR 8
  64.             X = GetValue(limiter$, "X")
  65.         END IF
  66.         IF INSTR(limiter$, "Y") THEN 'Y position on scren
  67.             limit = limit OR 8
  68.             Y = GetValue(limiter$, "Y")
  69.         END IF
  70.         IF INSTR(limiter$, "D") THEN disable_paste = -1: limit = limit OR 8 'disable paste
  71.         IF INSTR(limiter$, "V") THEN cursor_after_paste = -1: limit = limit OR 8 'disable paste
  72.         IF INSTR(limiter$, "H") THEN clean_exit = -1: limit = limit OR 8 'disable paste
  73.     END IF
  74.     IF limit <> 0 THEN prompt$ = MID$(prompt$, i + 1)
  75.  
  76.  
  77.     DO
  78.         PCOPY 1, 0
  79.         IF _KEYDOWN(100307) OR _KEYDOWN(100308) THEN AltDown = -1 ELSE AltDown = 0
  80.         k = _KEYHIT
  81.         IF AltDown THEN
  82.             SELECT CASE k 'ignore all keypresses except ALT-number presses
  83.                 CASE -57 TO -48: AltWasDown = -1: alt$ = alt$ + CHR$(-k)
  84.             END SELECT
  85.         ELSE
  86.             SELECT CASE k 'without alt, add any keypresses to our input
  87.                 CASE 8
  88.                     oldin$ = in$
  89.                     IF CP > 0 THEN OldCP = CP: CP = CP - 1
  90.                     in$ = LEFT$(in$, CP) + MID$(in$, CP + 2) 'backspace to erase input
  91.                 CASE 9
  92.                     oldin$ = in$
  93.                     in$ = LEFT$(in$, CP) + SPACE$(4) + MID$(in$, CP + 1) 'four spaces for any TAB entered
  94.                     OldCP = CP
  95.                     CP = CP + 4
  96.                 CASE 32 TO 128
  97.                     IF _KEYDOWN(100305) OR _KEYDOWN(100306) THEN
  98.                         IF k = 118 OR k = 86 THEN
  99.                             IF disable_paste = 0 THEN
  100.                                 oldin$ = in$
  101.                                 temp$ = _CLIPBOARD$
  102.                                 in$ = LEFT$(in$, CP) + temp$ + MID$(in$, CP + 1) 'ctrl-v paste
  103.                                 'CTRL-V leaves cursor in position before the paste, without moving it after.
  104.                                 'Feel free to modify that behavior here, if you want it to move to after the paste.
  105.                                 IF cursor_after_paste THEN CP = CP + LEN(temp$)
  106.                             END IF
  107.                         END IF
  108.                         IF k = 122 OR k = 90 THEN SWAP in$, oldin$: SWAP OldCP, CP 'ctrl-z undo
  109.                     ELSE
  110.                         check_input:
  111.                         oldin$ = in$
  112.                         IF limit AND 1 THEN 'unsigned
  113.                             IF k = 43 OR k = 45 THEN _CONTINUE 'remove signs +/-
  114.                         END IF
  115.                         IF limit AND 2 THEN 'integer
  116.                             IF k = 45 AND CP = 0 THEN GOTO good_input 'only allow a - sign for the first digit
  117.                             IF k < 48 OR k > 57 THEN _CONTINUE 'remove anything non-numeric
  118.                         END IF
  119.                         IF limit AND 4 THEN 'float
  120.                             IF k = 45 AND CP = 0 THEN GOTO good_input 'only allow a - sign for the first digit
  121.                             IF k = 46 AND INSTR(in$, ".") = 0 THEN GOTO good_input 'only one decimal point
  122.                             IF k < 48 OR k > 57 THEN _CONTINUE 'remove anything non-numeric
  123.                             IF LEFT$(in$, 1) = "-" THEN temp$ = MID$(in$, 2) ELSE temp$ = in$
  124.                             IF INSTR(in$, ".") = 0 OR CP < INSTR(in$, ".") THEN
  125.                                 IF LEN(temp$) < float_before_limit OR float_before_limit = -1 THEN
  126.                                     in$ = LEFT$(in$, CP) + CHR$(k) + MID$(in$, CP + 1) 'add input to our string
  127.                                     OldCP = CP
  128.                                     CP = CP + 1
  129.                                 END IF
  130.                             ELSE
  131.                                 temp$ = MID$(in$, INSTR(in$, ".") + 1)
  132.                                 IF LEN(temp$) < float_after_limit OR float_after_limit = -1 THEN
  133.                                     in$ = LEFT$(in$, CP) + CHR$(k) + MID$(in$, CP + 1) 'add input to our string
  134.                                     OldCP = CP
  135.                                     CP = CP + 1
  136.                                 END IF
  137.                             END IF
  138.                             _CONTINUE
  139.                         END IF
  140.                         good_input:
  141.                         IF CP < length_limit OR length_limit < 0 THEN
  142.                             in$ = LEFT$(in$, CP) + CHR$(k) + MID$(in$, CP + 1) 'add input to our string
  143.  
  144.                             OldCP = CP
  145.                             CP = CP + 1
  146.                         END IF
  147.                     END IF
  148.                 CASE 18176 'Home
  149.                     CP = 0
  150.                 CASE 20224 'End
  151.                     CP = LEN(in$)
  152.                 CASE 21248 'Delete
  153.                     oldin$ = in$
  154.                     in$ = LEFT$(in$, CP) + MID$(in$, CP + 2)
  155.                 CASE 19200 'Left
  156.                     CP = CP - 1
  157.                     IF CP < 0 THEN CP = 0
  158.                 CASE 19712 'Right
  159.                     CP = CP + 1
  160.                     IF CP > LEN(in$) THEN CP = LEN(in$)
  161.             END SELECT
  162.         END IF
  163.         alt$ = RIGHT$(alt$, 3)
  164.         IF AltWasDown = -1 AND AltDown = 0 THEN
  165.             v = VAL(alt$)
  166.             IF v >= 0 AND v <= 255 THEN
  167.                 k = v
  168.                 alt$ = "": AltWasDown = 0
  169.                 GOTO check_input
  170.             END IF
  171.         END IF
  172.         blink = (blink + 1) MOD 30
  173.         LOCATE Y, X
  174.         PRINT prompt$;
  175.         IF password_protected THEN
  176.             PRINT STRING$(LEN(LEFT$(in$, CP)), "*");
  177.             IF blink \ 15 THEN PRINT " "; ELSE PRINT "_";
  178.             PRINT STRING$(LEN(MID$(in$, CP + 1)), "*")
  179.         ELSE
  180.             PRINT LEFT$(in$, CP);
  181.             IF blink \ 15 THEN PRINT " "; ELSE PRINT "_";
  182.             PRINT MID$(in$, CP + 1)
  183.         END IF
  184.  
  185.         _DISPLAY
  186.         _LIMIT 30
  187.     LOOP UNTIL k = 13
  188.  
  189.     PCOPY 1, 0
  190.     LOCATE OY, OX
  191.     IF clean_exit = 0 THEN
  192.         LOCATE Y, X
  193.         IF password_protected THEN
  194.             PRINT prompt$; STRING$(LEN(in$), "*")
  195.         ELSE
  196.             PRINT prompt$; in$
  197.         END IF
  198.     END IF
  199.     result$ = in$
  200.  
  201.  
  202. FUNCTION GetValue (limiter$, what$)
  203.     jstart = INSTR(limiter$, what$): j = 0
  204.     IF MID$(limiter$, INSTR(limiter$, what$) + 1, 1) = "-" THEN
  205.         GetValue = -1 'unlimited
  206.         EXIT FUNCTION
  207.     END IF
  208.  
  209.     DO
  210.         j = j + 1
  211.         m$ = MID$(limiter$, jstart + j, 1)
  212.     LOOP UNTIL m$ < "0" OR m$ > "9"
  213.     GetValue = VAL(MID$(limiter$, jstart + 1, j - 1))

Personally, I think even what we have here so far, is a pretty neat little bit of input handling for us.  Being able to limit input by types (integer, unsigned, float, or string), is a pretty nice little option for any input routine, as it helps prevent errors from the user-entry level -- and that can stop a whole lot of error checking from ever being necessary.  After all, if I *KNOW* that all an user can input is values from 0 to 9 for a numeric input, then I don't have to error-check later for dollar signs, commas, or stray sneeze input.

We can now limit floats by a before the decimal and after the decimal syntax.  We have an option to move our cursor to after a paste (default has it staying before).  We have an option to turn our text into a pop-up style input, where it completely disappears off the screen once we're done with it.

I guess all the basic legwork is done now.  Now, I guess next, I'll work up an {A} option to create an ASCII-Box similar to the one STx presented above.  Unless  someone comes across a glitch somewhere for me to sort out?

(Which wouldn't surprise me one bit.  After all, OEInput is starting to become a rather complex little set of work here!  The more complex something gets, the more likely it is to have unexpected glitches pop-up in things!)
Title: Re: Extended Input
Post by: FellippeHeitor on February 02, 2021, 11:57:17 am
While the hood is open, maybe it's worth dressing up the whole thing in an ascii-box too? Then it'll be an alternative to this box used in several projects around here:

 


🎶🎵 Don't go ditching my box
I know it's not so alright
Oh honey if it gets used then
there is something of good... pam pam, pam pam, pam 🎶🎵
Title: Re: Extended Input
Post by: STxAxTIC on February 02, 2021, 02:47:58 pm
That box^ is the best one anyone bothered to make. Still that way, so thank you muchly Fellippe. My versions of the input box are lazy and inferior.

But I don't want Steve to fall into the same trap. Now there's a milestone to *pass*, not just drive under.