3942
No, my conclusion in the previous commit was wrong. When you print a character on the right margin, the cursor coordinates always wrap around to the left margin on the next row. It's just that if you're at the bottom of the screen, scrolling gives the impression that the row didn't change.
This commit is contained in:
parent
b1e558cfe4
commit
b2eff9f97e
108
081print.mu
108
081print.mu
|
@ -1,5 +1,16 @@
|
|||
# Wrappers around print primitives that take a 'screen' object and are thus
|
||||
# easier to test.
|
||||
#
|
||||
# Screen objects are intended to exactly mimic the behavior of traditional
|
||||
# terminals. Moving a cursor too far write wraps it to the next line,
|
||||
# scrolling if necessary. The details are subtle:
|
||||
#
|
||||
# a) Rows can take unbounded values, and such values are preserved rather than
|
||||
# clamped or wrapped around.
|
||||
#
|
||||
# b) If you print to a square (row, right) on the right margin, the cursor
|
||||
# position depends on whether 'row' is in range. If it is, the new cursor
|
||||
# position is (row+1, 0). If it isn't, the new cursor position is (row, 0).
|
||||
|
||||
container screen [
|
||||
num-rows:num
|
||||
|
@ -7,6 +18,7 @@ container screen [
|
|||
cursor-row:num
|
||||
cursor-column:num
|
||||
data:&:@:screen-cell # capacity num-rows*num-columns
|
||||
pending-scroll?:bool
|
||||
top-idx:num # index inside data that corresponds to top-left of screen
|
||||
# modified on scroll, wrapping around to the top of data
|
||||
]
|
||||
|
@ -16,6 +28,32 @@ container screen-cell [
|
|||
color:num
|
||||
]
|
||||
|
||||
def main [
|
||||
local-scope
|
||||
open-console
|
||||
clear-screen
|
||||
print 0/screen [a]
|
||||
#? a:char <- copy 97
|
||||
#? i:num <- copy 0
|
||||
#? {
|
||||
#? done?:bool <- greater-or-equal i, 6
|
||||
#? break-if done?
|
||||
#? move-cursor 0/screen i, 2527
|
||||
#? print 0/screen a
|
||||
#? a <- add a, 1
|
||||
#? i <- add i, 1
|
||||
#? loop
|
||||
#? }
|
||||
move-cursor 0/screen 5, 2527
|
||||
a:num b:num <- cursor-position 0/screen
|
||||
print 0/screen [f]
|
||||
c:num d:num <- cursor-position 0/screen
|
||||
wait-for-some-interaction
|
||||
close-console
|
||||
$print a [ ] b 10/newline
|
||||
$print c [ ] d 10/newline
|
||||
]
|
||||
|
||||
def new-fake-screen w:num, h:num -> result:&:screen [
|
||||
local-scope
|
||||
load-ingredients
|
||||
|
@ -26,14 +64,14 @@ def new-fake-screen w:num, h:num -> result:&:screen [
|
|||
assert non-zero-height?, [screen can't have zero height]
|
||||
bufsize:num <- multiply w, h
|
||||
data:&:@:screen-cell <- new screen-cell:type, bufsize
|
||||
*result <- merge h/num-rows, w/num-columns, 0/cursor-row, 0/cursor-column, data, 0/top-idx
|
||||
*result <- merge h/num-rows, w/num-columns, 0/cursor-row, 0/cursor-column, data, 0/pending-scroll?, 0/top-idx
|
||||
result <- clear-screen result
|
||||
]
|
||||
|
||||
def clear-screen screen:&:screen -> screen:&:screen [
|
||||
local-scope
|
||||
load-ingredients
|
||||
stash [clear-screen]
|
||||
#? stash [clear-screen]
|
||||
{
|
||||
break-if screen
|
||||
# real screen
|
||||
|
@ -61,7 +99,7 @@ def clear-screen screen:&:screen -> screen:&:screen [
|
|||
def fake-screen-is-empty? screen:&:screen -> result:bool [
|
||||
local-scope
|
||||
load-ingredients
|
||||
stash [fake-screen-is-empty?]
|
||||
#? stash [fake-screen-is-empty?]
|
||||
return-unless screen, 1/true # do nothing for real screens
|
||||
buf:&:@:screen-cell <- get *screen, data:offset
|
||||
i:num <- copy 0
|
||||
|
@ -150,9 +188,13 @@ def print screen:&:screen, c:char -> screen:&:screen [
|
|||
{
|
||||
at-bottom?:bool <- equal row, height
|
||||
break-unless at-bottom?
|
||||
$dump-screen
|
||||
$print [scroll1] 10/newline
|
||||
scroll-fake-screen screen
|
||||
row <- subtract height, 1
|
||||
*screen <- put *screen, cursor-row:offset, row
|
||||
*screen <- put *screen, pending-scroll?:offset, 0/false
|
||||
$dump-screen
|
||||
}
|
||||
# if row is below bottom margin (error), reset to bottom margin
|
||||
{
|
||||
|
@ -162,6 +204,17 @@ def print screen:&:screen, c:char -> screen:&:screen [
|
|||
*screen <- put *screen, cursor-row:offset, row
|
||||
}
|
||||
# }
|
||||
# if there's a pending scroll, perform it
|
||||
{
|
||||
pending-scroll?:bool <- get *screen, pending-scroll?:offset
|
||||
#? $print pending-scroll? 10/newline
|
||||
break-unless pending-scroll?
|
||||
$print [scroll2] 10/newline
|
||||
$dump-screen
|
||||
scroll-fake-screen screen
|
||||
*screen <- put *screen, pending-scroll?:offset, 0/false
|
||||
$dump-screen
|
||||
}
|
||||
#? stash [print] row column
|
||||
#? $print [print-character (], row, [, ], column, [): ], c, 10/newline
|
||||
# special-case: newline
|
||||
|
@ -195,16 +248,30 @@ def print screen:&:screen, c:char -> screen:&:screen [
|
|||
index:num <- data-index row, column, width, height, top-idx
|
||||
cursor:screen-cell <- merge c, color
|
||||
*buf <- put-index *buf, index, cursor
|
||||
# move cursor to next character
|
||||
# move cursor to next character, wrapping as necessary
|
||||
# however, don't scroll just yet
|
||||
# (but don't bother making it valid; we'll do that before the next print)
|
||||
column <- add column, 1
|
||||
{
|
||||
past-right?:bool <- greater-or-equal column, width
|
||||
break-unless past-right?
|
||||
column <- copy 0
|
||||
row <- add row, 1
|
||||
past-bottom?:bool <- greater-or-equal row, height
|
||||
break-unless past-bottom?
|
||||
# queue up a scroll
|
||||
$print [pending scroll] 10/newline
|
||||
*screen <- put *screen, pending-scroll?:offset, 1/true
|
||||
row <- subtract row, 1 # update cursor as if scroll already happened
|
||||
}
|
||||
*screen <- put *screen, cursor-row:offset, row
|
||||
*screen <- put *screen, cursor-column:offset, column
|
||||
]
|
||||
|
||||
def cursor-down-on-fake-screen screen:&:screen -> screen:&:screen [
|
||||
local-scope
|
||||
load-ingredients
|
||||
stash [cursor-down]
|
||||
#? stash [cursor-down]
|
||||
row:num <- get *screen, cursor-row:offset
|
||||
height:num <- get *screen, num-rows:offset
|
||||
bottom:num <- subtract height, 1
|
||||
|
@ -223,7 +290,7 @@ def cursor-down-on-fake-screen screen:&:screen -> screen:&:screen [
|
|||
def scroll-fake-screen screen:&:screen -> screen:&:screen [
|
||||
local-scope
|
||||
load-ingredients
|
||||
stash [scroll-fake-screen]
|
||||
#? stash [scroll-fake-screen]
|
||||
width:num <- get *screen, num-columns:offset
|
||||
height:num <- get *screen, num-rows:offset
|
||||
buf:&:@:screen-cell <- get *screen, data:offset
|
||||
|
@ -515,7 +582,7 @@ def assert-no-scroll screen:&:screen, old-top-idx:num [
|
|||
def clear-line screen:&:screen -> screen:&:screen [
|
||||
local-scope
|
||||
load-ingredients
|
||||
stash [clear-line]
|
||||
#? stash [clear-line]
|
||||
space:char <- copy 0/nul
|
||||
{
|
||||
break-if screen
|
||||
|
@ -545,7 +612,7 @@ def clear-line-until screen:&:screen, right:num/inclusive -> screen:&:screen [
|
|||
local-scope
|
||||
load-ingredients
|
||||
row:num, column:num <- cursor-position screen
|
||||
stash [clear-line-until] row column
|
||||
#? stash [clear-line-until] row column
|
||||
height:num <- screen-height screen
|
||||
past-bottom?:bool <- greater-or-equal row, height
|
||||
return-if past-bottom?
|
||||
|
@ -582,7 +649,7 @@ def cursor-position screen:&:screen -> row:num, column:num [
|
|||
def move-cursor screen:&:screen, new-row:num, new-column:num -> screen:&:screen [
|
||||
local-scope
|
||||
load-ingredients
|
||||
stash [move-cursor]
|
||||
#? stash [move-cursor]
|
||||
{
|
||||
break-if screen
|
||||
# real screen
|
||||
|
@ -592,6 +659,8 @@ def move-cursor screen:&:screen, new-row:num, new-column:num -> screen:&:screen
|
|||
# fake screen
|
||||
*screen <- put *screen, cursor-row:offset, new-row
|
||||
*screen <- put *screen, cursor-column:offset, new-column
|
||||
$print [move cursor ] new-row [ ] new-column 10/newline
|
||||
*screen <- put *screen, pending-scroll?:offset, 0/false
|
||||
]
|
||||
|
||||
scenario clear-line-erases-printed-characters [
|
||||
|
@ -628,7 +697,7 @@ scenario clear-line-erases-printed-characters [
|
|||
def cursor-down screen:&:screen -> screen:&:screen [
|
||||
local-scope
|
||||
load-ingredients
|
||||
stash [cursor-down]
|
||||
#? stash [cursor-down]
|
||||
{
|
||||
break-if screen
|
||||
# real screen
|
||||
|
@ -671,7 +740,7 @@ scenario cursor-down-scrolls [
|
|||
def cursor-up screen:&:screen -> screen:&:screen [
|
||||
local-scope
|
||||
load-ingredients
|
||||
stash [cursor-up]
|
||||
#? stash [cursor-up]
|
||||
{
|
||||
break-if screen
|
||||
# real screen
|
||||
|
@ -689,7 +758,7 @@ def cursor-up screen:&:screen -> screen:&:screen [
|
|||
def cursor-right screen:&:screen -> screen:&:screen [
|
||||
local-scope
|
||||
load-ingredients
|
||||
stash [cursor-right]
|
||||
#? stash [cursor-right]
|
||||
{
|
||||
break-if screen
|
||||
# real screen
|
||||
|
@ -709,7 +778,7 @@ def cursor-right screen:&:screen -> screen:&:screen [
|
|||
def cursor-left screen:&:screen -> screen:&:screen [
|
||||
local-scope
|
||||
load-ingredients
|
||||
stash [cursor-left]
|
||||
#? stash [cursor-left]
|
||||
{
|
||||
break-if screen
|
||||
# real screen
|
||||
|
@ -727,16 +796,15 @@ def cursor-left screen:&:screen -> screen:&:screen [
|
|||
def cursor-to-start-of-line screen:&:screen -> screen:&:screen [
|
||||
local-scope
|
||||
load-ingredients
|
||||
stash [cursor-to-start-of-line]
|
||||
#? stash [cursor-to-start-of-line]
|
||||
row:num <- cursor-position screen
|
||||
column:num <- copy 0
|
||||
screen <- move-cursor screen, row, column
|
||||
screen <- move-cursor screen, row, 0/column
|
||||
]
|
||||
|
||||
def cursor-to-next-line screen:&:screen -> screen:&:screen [
|
||||
local-scope
|
||||
load-ingredients
|
||||
stash [cursor-to-next-line]
|
||||
#? stash [cursor-to-next-line]
|
||||
screen <- cursor-down screen
|
||||
screen <- cursor-to-start-of-line screen
|
||||
]
|
||||
|
@ -745,14 +813,14 @@ def move-cursor-to-column screen:&:screen, column:num -> screen:&:screen [
|
|||
local-scope
|
||||
load-ingredients
|
||||
row:num, _ <- cursor-position screen
|
||||
stash [move-cursor-to-column] row
|
||||
#? stash [move-cursor-to-column] row
|
||||
move-cursor screen, row, column
|
||||
]
|
||||
|
||||
def screen-width screen:&:screen -> width:num [
|
||||
local-scope
|
||||
load-ingredients
|
||||
stash [screen-width]
|
||||
#? stash [screen-width]
|
||||
{
|
||||
break-unless screen
|
||||
# fake screen
|
||||
|
@ -766,7 +834,7 @@ def screen-width screen:&:screen -> width:num [
|
|||
def screen-height screen:&:screen -> height:num [
|
||||
local-scope
|
||||
load-ingredients
|
||||
stash [screen-height]
|
||||
#? stash [screen-height]
|
||||
{
|
||||
break-unless screen
|
||||
# fake screen
|
||||
|
|
|
@ -207,7 +207,7 @@ def render screen:&:screen, editor:&:editor -> last-row:num, last-column:num, sc
|
|||
def clear-screen-from screen:&:screen, row:num, column:num, left:num, right:num -> screen:&:screen [
|
||||
local-scope
|
||||
load-ingredients
|
||||
stash [clear-screen-from] row column [between] left [and] right
|
||||
#? stash [clear-screen-from] row column [between] left [and] right
|
||||
# if it's the real screen, use the optimized primitive
|
||||
{
|
||||
break-if screen
|
||||
|
@ -218,7 +218,7 @@ def clear-screen-from screen:&:screen, row:num, column:num, left:num, right:num
|
|||
screen <- move-cursor screen, row, column
|
||||
clear-line-until screen, right
|
||||
clear-rest-of-screen screen, row, left, right
|
||||
screen <- move-cursor screen, row, column
|
||||
#? screen <- move-cursor screen, row, column
|
||||
]
|
||||
|
||||
def clear-rest-of-screen screen:&:screen, row:num, left:num, right:num -> screen:&:screen [
|
||||
|
|
|
@ -1116,11 +1116,11 @@ after <handle-special-key> [
|
|||
def draw-horizontal screen:&:screen, row:num, x:num, right:num -> screen:&:screen [
|
||||
local-scope
|
||||
load-ingredients
|
||||
a:num, b:num <- cursor-position screen
|
||||
stash [draw-horizontal] row [--] a b
|
||||
#? a:num, b:num <- cursor-position screen
|
||||
#? stash [draw-horizontal] row [--] a b
|
||||
height:num <- screen-height screen
|
||||
past-bottom?:bool <- greater-or-equal row, height
|
||||
stash [ past-bottom?] past-bottom?
|
||||
#? stash [ past-bottom?] past-bottom?
|
||||
return-if past-bottom?
|
||||
style:char, style-found?:bool <- next-ingredient
|
||||
{
|
||||
|
@ -1138,18 +1138,18 @@ def draw-horizontal screen:&:screen, row:num, x:num, right:num -> screen:&:scree
|
|||
break-if bg-color-found?
|
||||
bg-color <- copy 0/black
|
||||
}
|
||||
stash [aa] x
|
||||
#? stash [aa] x
|
||||
screen <- move-cursor screen, row, x
|
||||
{
|
||||
continue?:bool <- lesser-or-equal x, right # right is inclusive, to match editor semantics
|
||||
break-unless continue?
|
||||
a b <- cursor-position screen
|
||||
stash [bb] x [--] a b
|
||||
#? a b <- cursor-position screen
|
||||
#? stash [bb] x [--] a b
|
||||
print screen, style, color, bg-color
|
||||
a b <- cursor-position screen
|
||||
stash [cc] x [--] a b
|
||||
#? a b <- cursor-position screen
|
||||
#? stash [cc] x [--] a b
|
||||
x <- add x, 1
|
||||
loop
|
||||
}
|
||||
stash [draw-horizontal done]
|
||||
#? stash [draw-horizontal done]
|
||||
]
|
||||
|
|
|
@ -447,7 +447,7 @@ def render-recipes screen:&:screen, env:&:environment, render-editor:render-reci
|
|||
clear-screen-from screen, row, left, left, right
|
||||
#
|
||||
assert-no-scroll screen, old-top-idx
|
||||
stash [render recipes done]
|
||||
#? stash [render recipes done]
|
||||
]
|
||||
|
||||
# replaced in a later layer
|
||||
|
@ -468,7 +468,7 @@ def render-sandbox-side screen:&:screen, env:&:environment, render-editor:render
|
|||
clear-screen-from screen, row, left, left, right
|
||||
#
|
||||
assert-no-scroll screen, old-top-idx
|
||||
stash [render sandbox side0 done]
|
||||
#? stash [render sandbox side0 done]
|
||||
]
|
||||
|
||||
def update-cursor screen:&:screen, recipes:&:editor, current-sandbox:&:editor, sandbox-in-focus?:bool, env:&:environment -> screen:&:screen [
|
||||
|
|
|
@ -270,15 +270,15 @@ def! render-sandbox-side screen:&:screen, env:&:environment, render-editor:rende
|
|||
clear-rest-of-screen screen, row, left, right
|
||||
#
|
||||
assert-no-scroll screen, old-top-idx
|
||||
stash [render sandbox side done]
|
||||
#? stash [render sandbox side done]
|
||||
]
|
||||
|
||||
def render-sandboxes screen:&:screen, sandbox:&:sandbox, left:num, right:num, row:num, render-from:num, idx:num -> row:num, screen:&:screen, sandbox:&:sandbox [
|
||||
local-scope
|
||||
load-ingredients
|
||||
return-unless sandbox
|
||||
a:num b:num <- cursor-position screen
|
||||
stash [render-sandboxes] idx [:] row [--] a b
|
||||
#? a:num b:num <- cursor-position screen
|
||||
#? stash [render-sandboxes] idx [:] row [--] a b
|
||||
screen-height:num <- screen-height screen
|
||||
at-bottom?:bool <- greater-or-equal row, screen-height
|
||||
return-if at-bottom?
|
||||
|
@ -361,11 +361,11 @@ scenario skip-rendering-sandbox-menu-past-bottom-row [
|
|||
# create two sandboxes such that the top one just barely fills the screen
|
||||
env:&:environment <- new-programming-environment resources, screen, []
|
||||
env <- restore-sandboxes env, resources
|
||||
$clear-trace
|
||||
#? $clear-trace
|
||||
run [
|
||||
render-all screen, env, render
|
||||
]
|
||||
$dump-trace [app]
|
||||
#? $dump-trace [app]
|
||||
screen-should-contain [
|
||||
. run (F4) .
|
||||
. ┊ .
|
||||
|
|
Loading…
Reference in New Issue