Add up/down keynav
Paragraph jumping could be better, but that can be refined later. Progress on #10
This commit is contained in:
		
							parent
							
								
									c1c0d2125d
								
							
						
					
					
						commit
						2f828b1ae8
					
				| @ -1,6 +1,7 @@ | |||||||
| package system | package system | ||||||
| 
 | 
 | ||||||
| import "image" | import "image" | ||||||
|  | import "unicode" | ||||||
| import "image/color" | import "image/color" | ||||||
| import "golang.org/x/image/font" | import "golang.org/x/image/font" | ||||||
| import "git.tebibyte.media/tomo/tomo" | import "git.tebibyte.media/tomo/tomo" | ||||||
| @ -31,9 +32,11 @@ type textBox struct { | |||||||
| 	selecting   bool | 	selecting   bool | ||||||
| 	selectStart int | 	selectStart int | ||||||
| 	dot         text.Dot | 	dot         text.Dot | ||||||
|  | 	desiredX    fixed.Int26_6 | ||||||
| 
 | 
 | ||||||
| 	drawer typeset.Drawer | 	drawer     typeset.Drawer | ||||||
| 	face   util.Cycler[font.Face] | 	face       util.Cycler[font.Face] | ||||||
|  | 	lineHeight util.Memo[fixed.Int26_6] | ||||||
| 
 | 
 | ||||||
| 	on struct { | 	on struct { | ||||||
| 		contentBoundsChange event.FuncBroadcaster | 		contentBoundsChange event.FuncBroadcaster | ||||||
| @ -46,6 +49,12 @@ func (this *System) NewTextBox () tomo.TextBox { | |||||||
| 	box.box = this.newBox(box) | 	box.box = this.newBox(box) | ||||||
| 	box.attrTextColor.SetFallback(tomo.ATextColor(color.Black)) | 	box.attrTextColor.SetFallback(tomo.ATextColor(color.Black)) | ||||||
| 	box.attrDotColor.SetFallback(tomo.ADotColor(color.RGBA { G: 255, B: 255, A: 255})) | 	box.attrDotColor.SetFallback(tomo.ADotColor(color.RGBA { G: 255, B: 255, A: 255})) | ||||||
|  | 	box.lineHeight = util.NewMemo(func () fixed.Int26_6 { | ||||||
|  | 		face := box.face.Value() | ||||||
|  | 		if face == nil { return 0 } | ||||||
|  | 		metrics := face.Metrics() | ||||||
|  | 		return metrics.Height | ||||||
|  | 	}) | ||||||
| 	return box | 	return box | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -89,13 +98,20 @@ func (this *textBox) SetSelectable (selectable bool) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (this *textBox) Select (dot text.Dot) { | func (this *textBox) Select (dot text.Dot) { | ||||||
| 	if !this.selectable { return } | 	if this.selectWithoutResettingDesiredX(dot) { | ||||||
| 	if this.dot == dot  { return } | 		this.desiredX = fixed.I(0) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (this *textBox) selectWithoutResettingDesiredX (dot text.Dot) bool { | ||||||
|  | 	if !this.selectable { return false } | ||||||
|  | 	if this.dot == dot  { return false } | ||||||
| 	this.SetFocused(true) | 	this.SetFocused(true) | ||||||
| 	this.dot = dot | 	this.dot = dot | ||||||
| 	this.scrollToDot() | 	this.scrollToDot() | ||||||
| 	this.on.dotChange.Broadcast() | 	this.on.dotChange.Broadcast() | ||||||
| 	this.invalidateDraw() | 	this.invalidateDraw() | ||||||
|  | 	return true | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (this *textBox) Dot () text.Dot { | func (this *textBox) Dot () text.Dot { | ||||||
| @ -351,6 +367,42 @@ func (this *textBox) handleKeyDown (key input.Key, numberPad bool) bool { | |||||||
| 	dot       := this.Dot() | 	dot       := this.Dot() | ||||||
| 	sel       := modifiers.Shift | 	sel       := modifiers.Shift | ||||||
| 	word      := modifiers.Control | 	word      := modifiers.Control | ||||||
|  | 
 | ||||||
|  | 	moveVertically := func (delta fixed.Int26_6) { | ||||||
|  | 		currentDot := 0 | ||||||
|  | 		if sel { | ||||||
|  | 			currentDot = dot.End | ||||||
|  | 		} else { | ||||||
|  | 			currentDot = dot.Canon().Start | ||||||
|  | 			if delta > fixed.I(0) { currentDot = dot.Canon().End } | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		nextDot := 0 | ||||||
|  | 		if word { | ||||||
|  | 			if delta > fixed.I(0) { | ||||||
|  | 				nextDot = nextParagraph(this.runes, currentDot) | ||||||
|  | 			} else { | ||||||
|  | 				nextDot = previousParagraph(this.runes, currentDot) | ||||||
|  | 			} | ||||||
|  | 		} else { | ||||||
|  | 			currentPosition := this.drawer.PositionAt(currentDot) | ||||||
|  | 			if this.desiredX != fixed.I(0) { | ||||||
|  | 				currentPosition.X = this.desiredX | ||||||
|  | 			} | ||||||
|  | 			nextPosition   := currentPosition | ||||||
|  | 			nextPosition.Y += this.lineHeight.Value().Mul(delta) | ||||||
|  | 			this.desiredX   = nextPosition.X | ||||||
|  | 			nextDot         = this.drawer.AtPosition(nextPosition) | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if sel { | ||||||
|  | 			dot.End = nextDot | ||||||
|  | 			this.selectWithoutResettingDesiredX(dot) | ||||||
|  | 		} else { | ||||||
|  | 			this.selectWithoutResettingDesiredX(text.EmptyDot(nextDot)) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
| 	switch { | 	switch { | ||||||
| 	case key == input.KeyHome || (modifiers.Alt && key == input.KeyLeft): | 	case key == input.KeyHome || (modifiers.Alt && key == input.KeyLeft): | ||||||
| 		dot.End = 0 | 		dot.End = 0 | ||||||
| @ -376,6 +428,12 @@ func (this *textBox) handleKeyDown (key input.Key, numberPad bool) bool { | |||||||
| 			this.Select(text.MoveRight(this.runes, dot, word)) | 			this.Select(text.MoveRight(this.runes, dot, word)) | ||||||
| 		} | 		} | ||||||
| 		return true | 		return true | ||||||
|  | 	case key == input.KeyUp: | ||||||
|  | 		moveVertically(fixed.I(-1)) | ||||||
|  | 		return true | ||||||
|  | 	case key == input.KeyDown: | ||||||
|  | 		moveVertically(fixed.I(1)) | ||||||
|  | 		return true | ||||||
| 	case key == input.Key('a') && modifiers.Control: | 	case key == input.Key('a') && modifiers.Control: | ||||||
| 		dot.Start = 0 | 		dot.Start = 0 | ||||||
| 		dot.End   = len(this.text) | 		dot.End   = len(this.text) | ||||||
| @ -396,6 +454,10 @@ func (this *textBox) handleKeyUp (key input.Key, numberPad bool) bool { | |||||||
| 		return true | 		return true | ||||||
| 	case key == input.KeyEnd || (modifiers.Alt && key == input.KeyRight): | 	case key == input.KeyEnd || (modifiers.Alt && key == input.KeyRight): | ||||||
| 		return true | 		return true | ||||||
|  | 	case key == input.KeyUp: | ||||||
|  | 		return true | ||||||
|  | 	case key == input.KeyDown: | ||||||
|  | 		return true | ||||||
| 	case key == input.KeyLeft: | 	case key == input.KeyLeft: | ||||||
| 		return true | 		return true | ||||||
| 	case key == input.KeyRight: | 	case key == input.KeyRight: | ||||||
| @ -502,6 +564,7 @@ func (this *textBox) handleFaceChange () { | |||||||
| 	this.drawer.SetFace(face) | 	this.drawer.SetFace(face) | ||||||
| 	this.invalidateMinimum() | 	this.invalidateMinimum() | ||||||
| 	this.invalidateLayout() | 	this.invalidateLayout() | ||||||
|  | 	this.lineHeight.Invalidate() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (this *textBox) recursiveReApply () { | func (this *textBox) recursiveReApply () { | ||||||
| @ -523,3 +586,32 @@ func (this *textBox) recursiveReApply () { | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | func previousParagraph (text []rune, index int) int { | ||||||
|  | 	consecLF := 0 | ||||||
|  | 	if index >= len(text) { index = len(text) - 1 } | ||||||
|  | 	for ; index > 0; index -- { | ||||||
|  | 		char := text[index] | ||||||
|  | 		if char == '\n' { | ||||||
|  | 			consecLF ++ | ||||||
|  | 		} else if !unicode.IsSpace(char) { | ||||||
|  | 			if consecLF >= 2 { return index + 1 } | ||||||
|  | 			consecLF  = 0 | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return index | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func nextParagraph (text []rune, index int) int { | ||||||
|  | 	consecLF := 0 | ||||||
|  | 	for ; index < len(text); index ++ { | ||||||
|  | 		char := text[index] | ||||||
|  | 		if char == '\n' { | ||||||
|  | 			consecLF ++ | ||||||
|  | 		} else if !unicode.IsSpace(char) { | ||||||
|  | 			if consecLF >= 2 { return index } | ||||||
|  | 			consecLF = 0 | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return index | ||||||
|  | } | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user