about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--edit.mu278
1 files changed, 173 insertions, 105 deletions
diff --git a/edit.mu b/edit.mu
index c38002e5..75eb67c3 100644
--- a/edit.mu
+++ b/edit.mu
@@ -49,8 +49,10 @@ container editor-data [
   cursor-row:number
   cursor-column:number
 
-  # pointer to another editor, responsible for a different area of screen
+  # pointer to another editor, responsible for a different area of screen.
+  # helps organize editors in a 'chain'.
   next-editor:address:editor-data
+  in-focus?:boolean  # set for the one editor in this chain currently being edited
 ]
 
 # editor:address, screen:address <- new-editor s:address:array:character, screen:address, top:number, left:number, bottom:number
@@ -92,6 +94,10 @@ recipe new-editor [
   y:address:address:duplex-list/deref <- copy d:address:address:duplex-list/deref
   init:address:address:duplex-list <- get-address result:address:editor-data/deref, top-of-screen:offset
   init:address:address:duplex-list/deref <- copy d:address:address:duplex-list/deref
+  # set focus
+  # if using multiple editors, must call reset-focus after chaining them all
+  b:address:boolean <- get-address result:address:editor-data/deref, in-focus?:offset
+  b:address:boolean/deref <- copy 1:literal/true
 #?   $print d:address:address:duplex-list/deref, [ 
 #? ] #? 1
   # early exit if s is empty
@@ -363,96 +369,126 @@ scenario editor-initializes-empty-text [
 
 ## handling events from the keyboard, mouse, touch screen, ...
 
+# Takes a chain of editors (chained using editor-data.next-editor), sends each
+# event from the console to each editor.
 recipe event-loop [
   default-space:address:array:location <- new location:type, 30:literal
   screen:address <- next-ingredient
   console:address <- next-ingredient
   editor:address:editor-data <- next-ingredient
   {
-    +next-event
+    # send each event to each editor
     e:event, console:address, found?:boolean, quit?:boolean <- read-event console:address
     loop-unless found?:boolean
     break-if quit?:boolean  # only in tests
     trace [app], [next-event]
-    # 'touch' event
+#?     $print [--- new event
+#? ] #? 1
+    curr:address:editor-data <- copy editor:address:editor-data
     {
-      t:address:touch-event <- maybe-convert e:event, touch:variant
-      break-unless t:address:touch-event
-#?       $print [aaa: touch] #? 1
-      editor:address:editor-data <- move-cursor-in-editor editor:address:editor-data, t:address:touch-event/deref
-      loop +next-event:label
+      break-unless curr:address:editor-data
+      handle-event screen:address, console:address, curr:address:editor-data, e:event
+      curr:address:editor-data <- get curr:address:editor-data/deref, next-editor:offset
+      loop
     }
-    # typing regular characters
+    # after each non-trivial event, render all editors
+    curr:address:editor-data <- copy editor:address:editor-data
     {
-      c:address:character <- maybe-convert e:event, text:variant
-      break-unless c:address:character
-      editor:address:editor-data <- insert-at-cursor editor:address:editor-data, c:address:character/deref
-      loop +next-event:label
+      break-unless curr:address:editor-data
+      render curr:address:editor-data
+      curr:address:editor-data <- get curr:address:editor-data/deref, next-editor:offset
+      loop
     }
-    # otherwise it's a special key to control the editor
-    k:address:number <- maybe-convert e:event, keycode:variant
-    assert k:address:number, [event was of unknown type; neither keyboard nor mouse]
-    d:address:duplex-list <- get editor:address:editor-data/deref, data:offset
-    before-cursor:address:address:duplex-list <- get-address editor:address:editor-data/deref, before-cursor:offset
-    cursor-row:address:number <- get-address editor:address:editor-data/deref, cursor-row:offset
-    cursor-column:address:number <- get-address editor:address:editor-data/deref, cursor-column:offset
-    # arrows; update cursor-row and cursor-column, leave before-cursor to 'render'
-    # right arrow
+    loop
+  }
+]
+
+recipe handle-event [
+  default-space:address:array:location <- new location:type, 30:literal
+  screen:address <- next-ingredient
+  console:address <- next-ingredient
+  editor:address:editor-data <- next-ingredient
+  e:event <- next-ingredient
+  # 'touch' event
+  {
+    t:address:touch-event <- maybe-convert e:event, touch:variant
+    break-unless t:address:touch-event
+    move-cursor-in-editor editor:address:editor-data, t:address:touch-event/deref
+    reply
+  }
+  # other events trigger only if this editor is in focus
+#?   $print [checking ], editor:address:editor-data, [ 
+#? ] #? 1
+#?   x:address:boolean <- get-address editor:address:editor-data/deref, in-focus?:offset #? 1
+#?   $print [address of focus: ], x:address:boolean, [ 
+#? ] #? 1
+  in-focus?:address:boolean <- get-address editor:address:editor-data/deref, in-focus?:offset
+#?   $print [ at ], in-focus?:address:boolean, [ 
+#? ] #? 1
+  reply-unless in-focus?:address:boolean/deref
+#?   $print [in focus: ], editor:address:editor-data, [ 
+#? ] #? 1
+  # typing regular characters
+  {
+    c:address:character <- maybe-convert e:event, text:variant
+    break-unless c:address:character
+    insert-at-cursor editor:address:editor-data, c:address:character/deref
+    reply
+  }
+  # otherwise it's a special key to control the editor
+  k:address:number <- maybe-convert e:event, keycode:variant
+  assert k:address:number, [event was of unknown type; neither keyboard nor mouse]
+  d:address:duplex-list <- get editor:address:editor-data/deref, data:offset
+  before-cursor:address:address:duplex-list <- get-address editor:address:editor-data/deref, before-cursor:offset
+  cursor-row:address:number <- get-address editor:address:editor-data/deref, cursor-row:offset
+  cursor-column:address:number <- get-address editor:address:editor-data/deref, cursor-column:offset
+  # arrows; update cursor-row and cursor-column, leave before-cursor to 'render'
+  # right arrow
+  {
+    next-character?:boolean <- equal k:address:number/deref, 65514:literal/right-arrow
+    break-unless next-character?:boolean
+    # if not at end of text
+    next:address:duplex-list <- next-duplex before-cursor:address:address:duplex-list/deref
+    break-unless next:address:duplex-list
+    # scan to next character
+    before-cursor:address:address:duplex-list/deref <- copy next:address:duplex-list
+    nextc:character <- get before-cursor:address:address:duplex-list/deref/deref, value:offset
+    # if it's a newline, move cursor to start of next row
     {
-      next-character?:boolean <- equal k:address:number/deref, 65514:literal/right-arrow
-      break-unless next-character?:boolean
-      # if not at end of text
-      next:address:duplex-list <- next-duplex before-cursor:address:address:duplex-list/deref
-      break-unless next:address:duplex-list
-      # scan to next character
-      before-cursor:address:address:duplex-list/deref <- copy next:address:duplex-list
-      nextc:character <- get before-cursor:address:address:duplex-list/deref/deref, value:offset
-      # if it's a newline, move cursor to start of next row
-      {
-        at-newline?:boolean <- equal nextc:character, 10:literal/newline
-        break-unless at-newline?:boolean
-        cursor-row:address:number/deref <- add cursor-row:address:number/deref, 1:literal
-        cursor-column:address:number/deref <- copy 0:literal
-        break +render:label
-      }
-      # otherwise move cursor one character right
-      cursor-column:address:number/deref <- add cursor-column:address:number/deref, 1:literal
+      at-newline?:boolean <- equal nextc:character, 10:literal/newline
+      break-unless at-newline?:boolean
+      cursor-row:address:number/deref <- add cursor-row:address:number/deref, 1:literal
+      cursor-column:address:number/deref <- copy 0:literal
+      reply
     }
-    # left arrow
+    # otherwise move cursor one character right
+    cursor-column:address:number/deref <- add cursor-column:address:number/deref, 1:literal
+  }
+  # left arrow
+  {
+    prev-character?:boolean <- equal k:address:number/deref, 65515:literal/left-arrow
+    break-unless prev-character?:boolean
+    # if not at start of text
+    prev:address:duplex-list <- prev-duplex before-cursor:address:address:duplex-list/deref
+    break-unless prev:address:duplex-list
+    # if cursor not at left margin, move one character left
     {
-      prev-character?:boolean <- equal k:address:number/deref, 65515:literal/left-arrow
-      break-unless prev-character?:boolean
-      # if not at start of text
-      prev:address:duplex-list <- prev-duplex before-cursor:address:address:duplex-list/deref
-      break-unless prev:address:duplex-list
-      # if cursor not at left margin, move one character left
-      {
-        at-left-margin?:boolean <- equal cursor-column:address:number/deref, 0:literal
-        break-if at-left-margin?:boolean
-        cursor-column:address:number/deref <- subtract cursor-column:address:number/deref, 1:literal
-        break +render:label
-      }
-      # if at left margin, figure out how long the previous line is (there's
-      # guaranteed to be a previous line, since we're not at start of text)
-      # and position cursor after it
-      # before-cursor must currently be at a newline
-      prevc:character <- get before-cursor:address:address:duplex-list/deref/deref, value:offset
-      previous-character-must-be-newline:boolean <- equal prevc:character, 10:literal/newline
-      assert previous-character-must-be-newline:boolean, [aaa]
-      # compute length of previous line
-      end-of-line:number <- previous-line-length before-cursor:address:address:duplex-list/deref, d:address:duplex-list
-#?       $print [before: ] cursor-row:address:number/deref, [/], cursor-row:address:number, [ ], cursor-column:address:number/deref, [/], cursor-column:address:number, [ ], end-of-line:number, [ 
-#? ]
-      cursor-row:address:number/deref <- subtract cursor-row:address:number/deref, 1:literal
-      cursor-column:address:number/deref <- copy end-of-line:number
-#?       $print [after: ] cursor-row:address:number/deref, [/], cursor-row:address:number, [ ], cursor-column:address:number/deref, [/], cursor-column:address:number, [ ], end-of-line:number, [ 
-#? ]
+      at-left-margin?:boolean <- equal cursor-column:address:number/deref, 0:literal
+      break-if at-left-margin?:boolean
+      cursor-column:address:number/deref <- subtract cursor-column:address:number/deref, 1:literal
+      reply
     }
-    +render
-    render editor:address:editor-data
-#?     $print [after render: ] cursor-row:address:number/deref, [/], cursor-row:address:number, [ ], cursor-column:address:number/deref, [/], cursor-column:address:number, [ ], end-of-line:number, [ 
-#? ]
-    loop
+    # if at left margin, figure out how long the previous line is (there's
+    # guaranteed to be a previous line, since we're not at start of text)
+    # and position cursor after it
+    # before-cursor must currently be at a newline
+    prevc:character <- get before-cursor:address:address:duplex-list/deref/deref, value:offset
+    previous-character-must-be-newline:boolean <- equal prevc:character, 10:literal/newline
+    assert previous-character-must-be-newline:boolean, [aaa]
+    # compute length of previous line
+    end-of-line:number <- previous-line-length before-cursor:address:address:duplex-list/deref, d:address:duplex-list
+    cursor-row:address:number/deref <- subtract cursor-row:address:number/deref, 1:literal
+    cursor-column:address:number/deref <- copy end-of-line:number
   }
 ]
 
@@ -460,28 +496,25 @@ recipe move-cursor-in-editor [
   default-space:address:array:location <- new location:type, 30:literal
   editor:address:editor-data <- next-ingredient
   t:touch-event <- next-ingredient
+  # always reset focus to start
+  in-focus?:address:boolean <- get-address editor:address:editor-data/deref, in-focus?:offset
+  in-focus?:address:boolean/deref <- copy 0:literal/true
   click-column:number <- get t:touch-event, column:offset
   left:number <- get editor:address:editor-data/deref, left:offset
   too-far-left?:boolean <- lesser-than click-column:number, left:number
-  jump-if too-far-left?:boolean, +try-next:label
+  reply-if too-far-left?:boolean
   right:number <- get editor:address:editor-data/deref, right:offset
   too-far-right?:boolean <- greater-than click-column:number, right:number
-  jump-if too-far-right?:boolean, +try-next:label
+  reply-if too-far-right?:boolean
+#?   $print [focus now at ], editor:address:editor-data, [ 
+#? ] #? 1
+  # click on this window; gain focus
+  in-focus?:address:boolean/deref <- copy 1:literal/true
   # update cursor
   cursor-row:address:number <- get-address editor:address:editor-data/deref, cursor-row:offset
   cursor-row:address:number/deref <- get t:touch-event, row:offset
   cursor-column:address:number <- get-address editor:address:editor-data/deref, cursor-column:offset
   cursor-column:address:number/deref <- get t:touch-event, column:offset
-  render editor:address:editor-data
-  +try-next
-  {
-    next-editor:address:editor-data <- get editor:address:editor-data/deref, next-editor:offset
-#?     $print next-editor:address:editor-data, [ 
-#? ] #? 1
-    break-unless next-editor:address:editor-data
-    move-cursor-in-editor next-editor:address:editor-data, t:touch-event
-  }
-  reply editor:address:editor-data/same-as-ingredient:0
 ]
 
 recipe insert-at-cursor [
@@ -490,11 +523,6 @@ recipe insert-at-cursor [
   c:character <- next-ingredient
   before-cursor:address:address:duplex-list <- get-address editor:address:editor-data/deref, before-cursor:offset
   d:address:duplex-list <- get editor:address:editor-data/deref, data:offset
-#?   $print before-cursor:address:address:duplex-list/deref, [ ], d:address:duplex-list, [ 
-#? ] #? 2
-#?   prev:character <- get before-cursor:address:address:duplex-list/deref/deref, value:offset #? 1
-#?   $print [inserting ], c:character, [ after ], prev:character, [ 
-#? ] #? 2
   insert-duplex c:character, before-cursor:address:address:duplex-list/deref
   # update cursor: if newline, move cursor to start of next line
   # todo: bottom of screen
@@ -505,23 +533,11 @@ recipe insert-at-cursor [
     cursor-row:address:number/deref <- add cursor-row:address:number/deref, 1:literal
     cursor-column:address:number <- get-address editor:address:editor-data/deref, cursor-column:offset
     cursor-column:address:number/deref <- copy 0:literal
-    break +render:label
+    reply
   }
   # otherwise move cursor right
-#?   $print [column 0: ], cursor-column:address:number/deref, [ 
-#? ] #? 1
   cursor-column:address:number <- get-address editor:address:editor-data/deref, cursor-column:offset
   cursor-column:address:number/deref <- add cursor-column:address:number/deref, 1:literal
-#?   $print [column 1: ], cursor-column:address:number/deref, [ 
-#? ] #? 1
-  +render
-  render editor:address:editor-data
-#?   new-prev:character <- get before-cursor:address:address:duplex-list/deref/deref, value:offset #? 1
-#?   $print [column 2: ], cursor-column:address:number/deref, [ 
-#? ] #? 1
-#?   $print [cursor now after ], new-prev:character, [ 
-#? ] #? 1
-  reply editor:address:editor-data/same-as-ingredient:0
 ]
 
 # takes a pointer 'curr' into the doubly-linked list and its sentinel, counts
@@ -545,8 +561,6 @@ recipe previous-line-length [
     result:number <- add result:number, 1:literal
     loop
   }
-#?   $print result:number, [ 
-#? ] #? 1
   reply result:number
 ]
 
@@ -1042,6 +1056,60 @@ scenario point-at-multiple-editors [
   ]
 ]
 
+scenario editors-chain-to-cover-multiple-columns [
+#?   $print [=== new test
+#? ] #? 1
+  assume-screen 10:literal/width, 5:literal/height
+  # initialize an editor covering left half of screen
+  1:address:array:character <- new [abc]
+  2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0:literal/top, 0:literal/left, 5:literal/right
+  3:address:array:character <- new [def]
+  # chain new editor to it, covering the right half of the screen
+  4:address:address:editor-data <- get-address 2:address:editor-data/deref, next-editor:offset
+  4:address:address:editor-data/deref <- new-editor 3:address:array:character, screen:address, 0:literal/top, 5:literal/left, 10:literal/right
+  reset-focus 2:address:editor-data
+  # type one letter in each of them
+  assume-console [
+    left-click 0, 1
+    type [0]
+    left-click 0, 6
+    type [1]
+  ]
+  run [
+    event-loop screen:address, console:address, 2:address:editor-data
+    5:number <- get 2:address:editor-data/deref, cursor-column:offset
+    6:number <- get 4:address:address:editor-data/deref/deref, cursor-column:offset
+  ]
+  screen-should-contain [
+    .a0bc d1ef .
+    .          .
+  ]
+  memory-should-contain [
+    5 <- 2
+    6 <- 7
+  ]
+]
+
+# set focus to first editor, reset it in later ones
+recipe reset-focus [
+  default-space:address:array:location <- new location:type, 30:literal
+  editor:address:editor-data <- next-ingredient
+  in-focus:address:boolean <- get-address editor:address:editor-data/deref, in-focus?:offset
+  in-focus:address:boolean/deref <- copy 1:literal/true
+  e:address:editor-data <- get editor:address:editor-data/deref, next-editor:offset
+  {
+    break-unless e:address:editor-data
+#?     $print [resetting focus in ], e:address:editor-data, [ 
+#? ] #? 1
+    x:address:boolean <- get-address e:address:editor-data/deref, in-focus?:offset
+#?     $print [ at ], x:address:boolean, [ 
+#? ] #? 1
+    x:address:boolean/deref <- copy 0:literal/false
+    e:address:editor-data <- get e:address:editor-data/deref, next-editor:offset
+    loop
+  }
+]
+
 ## helpers for drawing editor borders
 
 recipe draw-box [