Jump to content


CodeWeaver's Content

There have been 4 items by CodeWeaver (Search limited from 10-February 22)


By content type

See this member's

Sort by                Order  

#268444 Editable Textfield (Persistent Value)

Posted by CodeWeaver on 22 July 2017 - 01:40 AM in Tutorials

TL;DR: Scroll to the bottom to see the final code, but I encourage you to read the background to understand why this is a confusing problem in the first place and how the solution came to be.

Early 2017, I created a program for which I wrote my own GUI. I wanted the user to be able to write some data -- for example, a name -- into a textfield. More importantly, I wanted the user to be free to change that data later. And most importantly, I wanted the textfield to act like they do in real computers: Selecting the textfield places the cursor on the existing text.

Unfortunately, read() always assumes you're inputting a new line. At best, it seems like the only option is to erase the textfield and expect a new input. But that's not what we want -- what if the user decides they don't want to change the value? It's cumbersome to expect them to re-type the previous data.

When I was researching this problem, the forum had it narrowed down to two options:
  • Use an existing GUI API that handles textfields gracefully.
  • Rewrite the built-in read() function.
I didn't want to go with either of these, so I dug deeper. Upon closer inspection of the wiki's page on read() (http://www.computercraft.info/wiki/Read), there's three optional parameters. The one that's particularly interesting is the second one -- history {}. This parameter lets you press "up" while in the terminal to access command history.

This is a way to retain the textfield's value. Even though the parameter is intended for command history, it's just a table of previous strings that can be cycled through. So when calling read() for the textfield, you can just pass the existing data, wrapped in a table: read(nil,{data}). We pass nil for the first parameter because we don't want to replace the characters that are printed to the screen.

Hang on. read() is still blank until the user presses "up". We don't want to expect this every time they change the text. But the computer is listening for an "up" keypress before it cycles through the history.

With os.queueEvent(), we can essentially tell the computer that the user pressed the "up" key. But there's another problem. You can't just queue the event directly after the read() function -- read() is still running! Is there any way around this one?

I first looked at the third optional parameter autocomplete (function). All we really need is a function that can queue the "up" keypress. Unfortunately, autocomplete isn't documented and I couldn't figure out how it works. But that did give me another thought. We basically want read() to be invoked, and then "up" to be queued before read() finishes.

By using the Parallel API, we can do this. Specifically, we need to use parallel.waitForAll(), because the alternative (waitForAny()) will quit once "up" is queued. Using waitForAll(), the first parameter will be invoked first (it will have to be read() in this case), and then once that function yields (e.g. to read user input), the second parameter will be invoked (os.queueEvent()). Once it's finished, it'll go back to read(), and the user can now edit the existing data!

Note: The Parallel API accepts functions as parameters. All we have to do to make our code work with Parallel is wrap our code in anonymous functions.

The final code:

parallel.waitForAll(
  function() value = read(nil,{data}) end,
  function() os.queueEvent("key",200,false) end
)

Here, value is the end value that the user enters. data is the existing value already in the textfield. The arguments to os.queueEvent() tell CC that we pressed the "up" key, but we're not holding it down.

In my application, I wrapped this code in a small function readfield(), which also sets the cursor to the location of the textfield. You can implement this however you want. Writing a program with a GUI in ComputerCraft was one of the most fun things I've ever coded! I highly recommend it. This was probably the most valuable thing I learned throughout the experience. It taught me a lot about the built-in functions, as well as the important Parallel API.



#265052 Idea Exchange

Posted by CodeWeaver on 23 February 2017 - 08:52 PM in General

View PostTheOddByte, on 05 January 2017 - 10:09 PM, said:

Connect Four with a twist, the "board" rotates 90 degrees every turn.

I can see it now. "Connect for ..."
That's a really fun idea, actually. Good exercise for CC networking, too (for multiplayer).



#265037 Idea Exchange

Posted by CodeWeaver on 23 February 2017 - 04:10 AM in General

Working on a massive project right now involving a GUI, and developed a means for a fully-user-customizable color scheme. Was wondering if anyone else has ever done/seen this in CC, and if so, how it was implemented!



#264986 STD-GUI - A multi-appstore appstore! Advanced/Normal compatible! Work...

Posted by CodeWeaver on 20 February 2017 - 11:18 PM in Programs

Interesting. What are all the repositories this program is pulling from?

Also, you say that a program can be added if it's a "self-sufficient" file. I'm assuming that excludes programs that require multiple files -- but are "installers" acceptable, to get around this?