Codea Stats 9: A Scroll!
At last, I get to apply Dave’s scrolling thing. It’s simple, and obvious, and I never thought of it. And a couple of stories of a great programmer.
Today’s mission is simple. Now that we have a display of the Dung program’s project description up on the screen, let’s scroll it, since only a fraction of it will fit on the screen at a time. I’ll be stealinXXXXXXX borrowinXXXXXXX adopting the idea Dave1707 had in one of his programs. What we want is simple enough: when you drag your finger up and down the screen, the text follows.
Let’s do it, and then explain it. It’s so simple that I can’t imagine why I didn’t find it obvious.
function setup()
if CodeaUnit then
codeaTestsVisible(true)
runCodeaUnitTests()
end
DY = 0
dungPD = ProjectDescription("D2")
local rep = dungPD:fullReport()
repLines = {}
for line in rep:gmatch("(.-)\n") do
table.insert(repLines,line)
end
end
function draw()
background(10,10,10)
--if CodeaUnit then showCodeaUnitTests() end
pushStyle()
local y = HEIGHT - 20
stroke(255)
fill(255)
textAlign(LEFT)
textMode(CORNER)
for _i,line in ipairs(repLines) do
text(line, 300, y + DY)
y = y - 20
end
popStyle()
end
function touched(touch)
DY = DY + touch.delta.y
if DY < 0 then DY = 0 end
end
We have a new global, DY, constrained to be zero or greater. When we display a line at its computed coordinate (20 more pixels down for each line), we add DY.
Since DY is positive, it moves the line up.
We set DY to zero, then add the change in the y coordinate of every touch to it. Every time your finger moves up a pixel, the display moves up a pixel. Move finger down, display moves down. It tracks perfectly. Watch:
Just to see the code more readily, here’s the essence:
function setup()
....
DY = 0
,,,,
end
function draw()
....
for _i,line in ipairs(repLines) do
text(line, 300, y + DY)
y = y - 20
end
....
end
function touched(touch)
DY = DY + touch.delta.y
if DY < 0 then DY = 0 end
end
Commit: display scrolls with touch.
Why Do You LIke That, Ron?
Thanks for asking. I like it because it’s a very simple connection between display and touch that either I’d never thought about, or had forgotten.
I know all about matrix transformations, scaling and translation, quaternions, affine geometry (well, I’ve forgotten most of that). In general, the math to make the picture on the screen know where you’ve touched it involves backing out of whatever transformations are in place to see what room or treasure or monster you’re touching.
When the screen scale is set to 1.0, pixels are pixels. All the math, if you did the math, boils down to 1 = 1.
A good programmer is a master of complexity. A great programmer, often like a beginning programmer, works with simplicity. In all my knowledge, I had lost sight of the simple fact that to move the display up one pixel, I could move my finger up one pixel.
I am reminded of a time I’ve told of before, when I had the privilege of the great Ward Cunningham coming in to help me and my team. He sat with me, looked at what my code was, and asked me “So, you know how to write an interpreter in Smalltalk, right?”
I thought I did, and showed him what we had. He asked me the question, always gently, in different words, a few more times.
Then he sat down at my keyboard and wrote the single statement of Smalltalk that is the essence of an interpreter. That’s why he’s a great programmer.
I actually saw Ward do that another time. He and the equally great Kent Beck were doing some kind of Q&A session at a conference, probably OOPSLA. Someone asked a question about how to calculate some very complex sum of complex objects. Kent dug in and started figuring out the details. He was doing quite well.
Then Ward went to the board, and wrote the single statement of Smalltalk that is the essence of summing things up. We were all enlightened, including, to my recollection, Kent.
That’s why Ward is a great programmer.
Me, I doubt that I ever touch great, but I’ve had my moments. I’ve at least attained the point where I can recognize simplicity when it slaps me in the face.
And that’s why I like this simple idea that I didn’t think of.
Selecting a Project
As long as we’re working on ideas that I didn’t think of, let’s plug in Dave’s other good idea. This time I’ll describe what we’re going to do, then do it.
It turns out that when you use print
to display something in Codea’s console, and you touch that line in the output, it is copied to the pasteboard. Dave used this fact to select a project to report on. He gets all the project names, prints them, and when the pasteboard has anything on it, he reports on that project, clearing the pasteboard, of course.
So let’s do that.
Some delay …
I spent a lot of time figuring out that the pasteboard text includes the newline at the end of the print, and that project names don’t have newlines. Finally, this:
function setup()
if false and CodeaUnit then
codeaTestsVisible(true)
runCodeaUnitTests()
end
DY = 0
repLines = {}
pasteboard.copy("")
local names = listProjects()
table.sort(names)
for _i,name in ipairs(names) do
print(name)
end
end
function createReport(name)
dungPD = ProjectDescription(name)
local rep = dungPD:fullReport()
repLines = {}
for line in rep:gmatch("(.-)\n") do
table.insert(repLines,line)
end
end
function draw()
if pasteboard.text ~= "" then
local name = pasteboard.text
pasteboard.copy("")
DY = 0
name = name:gsub("\n","")
createReport(name)
end
...
I extracted the code that creates the report, so that I could call it. Could have moved it in toto1 into draw
but a function made more sense to me.
I think that the DY setting should be moved into createReport
, but I also wonder whether we should put up a default report on the screen. I guess not. Maybe a generic message. Let’s do that.
function draw()
if pasteboard.text ~= "" then
local name = pasteboard.text
pasteboard.copy("")
name = name:gsub("\n","")
createReport(name)
end
background(10,10,10)
--if CodeaUnit then showCodeaUnitTests() end
pushStyle()
local y = HEIGHT - 20
stroke(255)
fill(255)
textAlign(LEFT)
textMode(CORNER)
if #repLines > 0 then
for _i,line in ipairs(repLines) do
text(line, 300, y + DY)
y = y - 20
end
else
text("Touch a Project Name", WIDTH/2,HEIGHT/2)
end
popStyle()
end
Here’s the program in action:
Summary
We’ve done what we’ve set out to do. It all went smoothly, except for ages figuring out that the newline on the end of a project name made it fail to be found.
What have we learned about the problem? Here are a few things that come to mind:
- A lot of programs, especially samples, but also some of mine, have no classes or methods. We probably want to at least list bare functions.
- There are a lot of projects in my Codea folder, and scrolling through them, while elegant, isn’t very efficient. A better way of selecting them would be better. Perhaps a display on screen could allow you to touch one?
- The details aren’t very interesting in most cases. A simple summary of number of classes and methods in each might be sufficient for most purposes. A fancy possibility would be to touch a class name to expand its details.
- Class and method lists that are alphabetized seem easier to scan.
Overall, my learning is that the information we can provide, at least in this simple form, isn’t all that useful. I can imagine that giving the display a bit of study, or perhaps displaying it to the team on a big screen, could lead to questions, ideas, and observations like
Should GameRunner really have 61 methods? And what about Dungeon, with 40? Tile has 58 methods? What’s up with that?
So it’s not at all bad. We learned some tricks in writing this little app, and even as it stands it can raise questions about whatever we’re working on.
It might be worth alphabetizing the classes and methods (though I’d like to pull init
up to the front, I think). It might be worth providing another sort, perhaps to sort classes in decreasing order of number of methods. Big classes first.
My overall experience is that these tools, if we get a packaged one, are kind of useful, but not terribly valuable. However, if we have one of our own, we can often enhance it to answer questions that interest us. What kind of questions?
How about these:
List methods of the same name in different classes.
List methods whose names are similar, such as
availableTiles...
List methods starting with
is
because they are probably used inif
statements and might provide opportunities for new objects and polymorphism.
List methods starting with
get
as possibly representing accessors and Demeter violations.
A larger thought comes to me from my Smalltalk history. In Smaltalk, there is no separate IDE working on your program. There is just your program, and a lot of class and method browsers and other tools, all operating directly on your code … and all subject to your modification and enhancement. You have the source code for everything, all the way up and all the way down.
That would let you touch a method in our display here, and find yourself editing the source for that method. You can dynamically ask Smalltalk to give you a browser with all the senders of a given method, or all the implementors of that method. It’s literally two mouse clicks for operations like that.
Could we do something like that in Codea? I don’t see how, because we have no hooks from the running program back up to the IDE. The IDE, such as it is, surely isn’t even written in Lua, but in Swift or Xcode.
I wonder if they’d be willing to provide a few hooks. Even one, that said something like “terminate running and return to the editor, in this tab, at this line number” would be pretty useful.
I’ll bring up the idea.
What will we do next? I’m not sure. Maybe a bit more on this, but we really need to get that hex map working just to prove that we can.
Stop by next time to find out!
-
Small but famous movie dog. ↩