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 }