github.com/kintar/etxt@v0.0.9/renderer_iterator.go (about)

     1  package etxt
     2  
     3  import "unicode/utf8"
     4  
     5  // Definitions of private types used to iterate strings and glyphs
     6  // on Traverse* operations. Sometimes we iterate lines in reverse,
     7  // so there's a bit of trickiness here and there.
     8  
     9  // A string iterator that can be used to go through lines in regular
    10  // order or in reverse, which is needed for some combinations of text
    11  // direction and horizontal alignments during rendering.
    12  type strIterator struct {
    13  	str        string
    14  	index      int
    15  	regression int // -1 if not reverse
    16  }
    17  
    18  // Will return -1 if no rune is left
    19  func (self *strIterator) Next() rune {
    20  	if self.regression == -1 {
    21  		codePoint, size := utf8.DecodeRuneInString(self.str[self.index:])
    22  		if size == 0 {
    23  			return -1
    24  		} // reached end
    25  		self.index += size
    26  		return codePoint
    27  	} else { // reverse order
    28  		iterPoint := (self.index - self.regression)
    29  		if iterPoint <= 0 {
    30  			return -1
    31  		} // reached end
    32  		codePoint, size := utf8.DecodeLastRuneInString(self.str[:iterPoint])
    33  		self.regression += size
    34  		if self.regression >= self.index {
    35  			self.str = self.str[self.index:]
    36  			self.LineSlide()
    37  		}
    38  		return codePoint
    39  	}
    40  }
    41  
    42  // used when working in reverse mode
    43  func (self *strIterator) LineSlide() {
    44  	self.regression = 0
    45  	for index, codePoint := range self.str {
    46  		if codePoint == '\n' {
    47  			if index == 0 {
    48  				self.index = 1 // force line break inclusion
    49  			} else {
    50  				self.index = index
    51  			}
    52  			return
    53  		}
    54  	}
    55  
    56  	// reached end
    57  	self.index = len(self.str)
    58  }
    59  
    60  func (self *strIterator) UntilNextLineBreak() string {
    61  	if self.regression == -1 {
    62  		start := self.index
    63  		if start >= len(self.str) {
    64  			return ""
    65  		}
    66  		for index, codePoint := range self.str[start:] {
    67  			if codePoint == '\n' {
    68  				return self.str[start:(start + index)]
    69  			}
    70  		}
    71  		return self.str[start:]
    72  	} else { // reverse order
    73  		iterPoint := (self.index - self.regression)
    74  		if iterPoint <= 0 {
    75  			return ""
    76  		} // reached end
    77  		start := iterPoint
    78  		curr := start
    79  		for curr >= 1 {
    80  			codePoint, size := utf8.DecodeLastRuneInString(self.str[:curr])
    81  			if codePoint == '\n' {
    82  				return self.str[curr:start]
    83  			}
    84  			curr -= size
    85  		}
    86  		return self.str[:start]
    87  	}
    88  }
    89  
    90  func newStrIterator(text string, reverse bool) strIterator {
    91  	iter := strIterator{str: text, index: 0, regression: -1}
    92  	if reverse {
    93  		iter.LineSlide()
    94  	}
    95  	return iter
    96  }
    97  
    98  type glyphsIterator struct {
    99  	glyphs []GlyphIndex
   100  	index  int // -N if reverse
   101  }
   102  
   103  // The bool indicates if we already reached the end. The returned
   104  // GlyphIndex must be ignored if bool == true
   105  func (self *glyphsIterator) Next() (GlyphIndex, bool) {
   106  	index := self.index
   107  	glyphs := self.glyphs
   108  	if index >= 0 {
   109  		if index >= len(glyphs) {
   110  			return 0, true
   111  		}
   112  		glyphIndex := glyphs[index]
   113  		self.index = index + 1
   114  		return glyphIndex, false
   115  	} else { // self.index < 0 (reverse mode)
   116  		glyphIndex := glyphs[-index-1]
   117  
   118  		// update index
   119  		if index == -1 {
   120  			self.index = len(glyphs)
   121  		} else {
   122  			self.index = index + 1
   123  		}
   124  
   125  		return glyphIndex, false
   126  	}
   127  }
   128  
   129  func newGlyphsIterator(glyphs []GlyphIndex, reverse bool) glyphsIterator {
   130  	if !reverse {
   131  		return glyphsIterator{glyphs, 0}
   132  	}
   133  	return glyphsIterator{glyphs, -len(glyphs)}
   134  }