Rudimentary text selection with keybaord keys
This commit is contained in:
		
							parent
							
								
									4bc8566820
								
							
						
					
					
						commit
						21abd147bf
					
				| @ -97,16 +97,30 @@ func (element *TextBox) HandleKeyDown(key input.Key, modifiers input.Modifiers) | |||||||
| 		textChanged = true | 		textChanged = true | ||||||
| 			 | 			 | ||||||
| 	case key == input.KeyLeft: | 	case key == input.KeyLeft: | ||||||
|  | 		if modifiers.Shift { | ||||||
|  | 			element.dot = textmanip.SelectLeft ( | ||||||
|  | 				element.text, | ||||||
|  | 				element.dot, | ||||||
|  | 				modifiers.Control) | ||||||
|  | 		} else { | ||||||
| 			element.dot = textmanip.MoveLeft ( | 			element.dot = textmanip.MoveLeft ( | ||||||
| 				element.text, | 				element.text, | ||||||
| 				element.dot, | 				element.dot, | ||||||
| 				modifiers.Control) | 				modifiers.Control) | ||||||
|  | 		} | ||||||
| 			 | 			 | ||||||
| 	case key == input.KeyRight: | 	case key == input.KeyRight: | ||||||
|  | 		if modifiers.Shift { | ||||||
|  | 			element.dot = textmanip.SelectRight ( | ||||||
|  | 				element.text, | ||||||
|  | 				element.dot, | ||||||
|  | 				modifiers.Control) | ||||||
|  | 		} else { | ||||||
| 			element.dot = textmanip.MoveRight ( | 			element.dot = textmanip.MoveRight ( | ||||||
| 				element.text, | 				element.text, | ||||||
| 				element.dot, | 				element.dot, | ||||||
| 				modifiers.Control) | 				modifiers.Control) | ||||||
|  | 		} | ||||||
| 		 | 		 | ||||||
| 	case key.Printable(): | 	case key.Printable(): | ||||||
| 		element.text, element.dot = textmanip.Type ( | 		element.text, element.dot = textmanip.Type ( | ||||||
| @ -299,14 +313,28 @@ func (element *TextBox) draw () { | |||||||
| 	} | 	} | ||||||
| 	pattern := element.theme.Pattern(theme.PatternSunken, state) | 	pattern := element.theme.Pattern(theme.PatternSunken, state) | ||||||
| 	artist.FillRectangle(element.core, pattern, bounds) | 	artist.FillRectangle(element.core, pattern, bounds) | ||||||
| 
 |  | ||||||
| 	if len(element.text) == 0 && !element.Focused() { |  | ||||||
| 		// draw placeholder |  | ||||||
| 		textBounds := element.placeholderDrawer.LayoutBounds() |  | ||||||
| 	offset := bounds.Min.Add (image.Point { | 	offset := bounds.Min.Add (image.Point { | ||||||
| 			X: element.config.Padding(), | 		X: element.config.Padding() - element.scroll, | ||||||
| 		Y: element.config.Padding(), | 		Y: element.config.Padding(), | ||||||
| 	}) | 	}) | ||||||
|  | 
 | ||||||
|  | 	if element.Focused() && !element.dot.Empty() { | ||||||
|  | 		// draw selection bounds | ||||||
|  | 		accent := element.theme.Pattern ( | ||||||
|  | 			theme.PatternAccent,  state) | ||||||
|  | 		canon := element.dot.Canon() | ||||||
|  | 		start := element.valueDrawer.PositionOf(canon.Start).Add(offset) | ||||||
|  | 		end   := element.valueDrawer.PositionOf(canon.End).Add(offset) | ||||||
|  | 		end.Y += element.valueDrawer.LineHeight().Round() | ||||||
|  | 		artist.FillRectangle ( | ||||||
|  | 			element.core, | ||||||
|  | 			accent, | ||||||
|  | 			image.Rectangle { start, end }) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if len(element.text) == 0 { | ||||||
|  | 		// draw placeholder | ||||||
|  | 		textBounds := element.placeholderDrawer.LayoutBounds() | ||||||
| 		foreground := element.theme.Pattern ( | 		foreground := element.theme.Pattern ( | ||||||
| 			theme.PatternForeground, | 			theme.PatternForeground, | ||||||
| 			theme.PatternState { Disabled: true }) | 			theme.PatternState { Disabled: true }) | ||||||
| @ -317,20 +345,18 @@ func (element *TextBox) draw () { | |||||||
| 	} else { | 	} else { | ||||||
| 		// draw input value | 		// draw input value | ||||||
| 		textBounds := element.valueDrawer.LayoutBounds() | 		textBounds := element.valueDrawer.LayoutBounds() | ||||||
| 		offset := bounds.Min.Add (image.Point { |  | ||||||
| 			X: element.config.Padding() - element.scroll, |  | ||||||
| 			Y: element.config.Padding(), |  | ||||||
| 		}) |  | ||||||
| 		foreground := element.theme.Pattern ( | 		foreground := element.theme.Pattern ( | ||||||
| 			theme.PatternForeground,  state) | 			theme.PatternForeground,  state) | ||||||
| 		element.valueDrawer.Draw ( | 		element.valueDrawer.Draw ( | ||||||
| 			element.core, | 			element.core, | ||||||
| 			foreground, | 			foreground, | ||||||
| 			offset.Sub(textBounds.Min)) | 			offset.Sub(textBounds.Min)) | ||||||
|  | 	} | ||||||
| 	 | 	 | ||||||
| 		if element.Focused() { | 	if element.Focused() && element.dot.Empty() { | ||||||
| 			// cursor | 		// draw cursor | ||||||
| 			// TODO: draw selection if exists | 		foreground := element.theme.Pattern ( | ||||||
|  | 			theme.PatternForeground,  state) | ||||||
| 		cursorPosition := element.valueDrawer.PositionOf ( | 		cursorPosition := element.valueDrawer.PositionOf ( | ||||||
| 			element.dot.End) | 			element.dot.End) | ||||||
| 		artist.Line ( | 		artist.Line ( | ||||||
| @ -342,5 +368,4 @@ func (element *TextBox) draw () { | |||||||
| 				cursorPosition.Y + element.valueDrawer. | 				cursorPosition.Y + element.valueDrawer. | ||||||
| 				LineHeight().Round()).Add(offset)) | 				LineHeight().Round()).Add(offset)) | ||||||
| 	} | 	} | ||||||
| 	} |  | ||||||
| } | } | ||||||
|  | |||||||
| @ -34,12 +34,19 @@ func (dot Dot) Sub (delta int) Dot { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func WordToLeft (text []rune, dot Dot) (length int) { | func (dot Dot) Constrain (length int) Dot { | ||||||
| 	cursor := dot.End | 	if dot.Start < 0      { dot.Start = 0 } | ||||||
| 	if cursor < 1 { return } | 	if dot.Start > length { dot.Start = length} | ||||||
| 	if cursor > len(text) { cursor = len(text) } | 	if dot.End < 0        { dot.End = 0 } | ||||||
|  | 	if dot.End > length   { dot.End = length} | ||||||
|  | 	return dot | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| 	index := cursor - 1 | func WordToLeft (text []rune, position int) (length int) { | ||||||
|  | 	if position < 1 { return } | ||||||
|  | 	if position > len(text) { position = len(text) } | ||||||
|  | 
 | ||||||
|  | 	index := position - 1 | ||||||
| 	for index >= 0 && unicode.IsSpace(text[index]) { | 	for index >= 0 && unicode.IsSpace(text[index]) { | ||||||
| 		length ++ | 		length ++ | ||||||
| 		index -- | 		index -- | ||||||
| @ -51,12 +58,11 @@ func WordToLeft (text []rune, dot Dot) (length int) { | |||||||
| 	return | 	return | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func WordToRight (text []rune, dot Dot) (length int) { | func WordToRight (text []rune, position int) (length int) { | ||||||
| 	cursor := dot.End | 	if position < 0 { return } | ||||||
| 	if cursor < 0 { return } | 	if position > len(text) { position = len(text) } | ||||||
| 	if cursor > len(text) { cursor = len(text) } |  | ||||||
| 
 | 
 | ||||||
| 	index := cursor | 	index := position | ||||||
| 	for index < len(text) && unicode.IsSpace(text[index]) { | 	for index < len(text) && unicode.IsSpace(text[index]) { | ||||||
| 		length ++ | 		length ++ | ||||||
| 		index ++ | 		index ++ | ||||||
| @ -68,41 +74,46 @@ func WordToRight (text []rune, dot Dot) (length int) { | |||||||
| 	return | 	return | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func Backspace (text []rune, dot Dot, word bool) (result []rune, moved Dot) { | func WordAround (text []rune, position int) (around Dot) { | ||||||
| 	if dot.Empty() { | 	return Dot { | ||||||
| 		cursor := dot.End | 		WordToLeft(text, position), | ||||||
| 		if cursor < 1         { return text, dot } | 		WordToRight(text, position), | ||||||
| 		if cursor > len(text) { cursor = len(text) } | 	} | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
|  | func Backspace (text []rune, dot Dot, word bool) (result []rune, moved Dot) { | ||||||
|  | 	dot = dot.Constrain(len(text)) | ||||||
|  | 	if dot.Empty() { | ||||||
| 		distance := 1 | 		distance := 1 | ||||||
| 		if word { | 		if word { | ||||||
| 			distance = WordToLeft(text, dot) | 			distance = WordToLeft(text, dot.End) | ||||||
| 		} | 		} | ||||||
| 		result = append(result, text[:cursor - distance]...) | 		result = append ( | ||||||
| 		result = append(result, text[cursor:]...) | 			result, | ||||||
| 		moved = EmptyDot(cursor - distance) | 			text[:dot.Sub(distance).Constrain(len(text)).End]...) | ||||||
|  | 		result = append(result, text[dot.End:]...) | ||||||
|  | 		moved = EmptyDot(dot.Sub(distance).Start) | ||||||
|  | 		return | ||||||
| 	} else { | 	} else { | ||||||
| 		return Delete(text, dot, word) | 		return Delete(text, dot, word) | ||||||
| 	} | 	} | ||||||
| 
 |  | ||||||
| 	return |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func Delete (text []rune, dot Dot, word bool) (result []rune, moved Dot) { | func Delete (text []rune, dot Dot, word bool) (result []rune, moved Dot) { | ||||||
|  | 	dot = dot.Constrain(len(text)) | ||||||
| 	if dot.Empty() { | 	if dot.Empty() { | ||||||
| 		cursor := dot.End |  | ||||||
| 		if cursor < 0         { return text, dot } |  | ||||||
| 		if cursor > len(text) { cursor = len(text) } |  | ||||||
| 
 |  | ||||||
| 		distance := 1 | 		distance := 1 | ||||||
| 		if word { | 		if word { | ||||||
| 			distance = WordToRight(text, dot) | 			distance = WordToRight(text, dot.End) | ||||||
| 		} | 		} | ||||||
| 		result = append(result, text[:cursor]...) | 		result = append(result, text[:dot.End]...) | ||||||
| 		result = append(result, text[cursor + distance:]...) | 		result = append ( | ||||||
|  | 			result, | ||||||
|  | 			text[dot.Add(distance).Constrain(len(text)).End:]...) | ||||||
| 		moved = dot | 		moved = dot | ||||||
| 		return	 | 		return	 | ||||||
| 	} else { | 	} else { | ||||||
|  | 		dot = dot.Canon() | ||||||
| 		result = append(result, text[:dot.Start]...) | 		result = append(result, text[:dot.Start]...) | ||||||
| 		result = append(result, text[dot.End:]...) | 		result = append(result, text[dot.End:]...) | ||||||
| 		moved = EmptyDot(dot.Start) | 		moved = EmptyDot(dot.Start) | ||||||
| @ -111,50 +122,61 @@ func Delete (text []rune, dot Dot, word bool) (result []rune, moved Dot) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func Type (text []rune, dot Dot, character rune) (result []rune, moved Dot) { | func Type (text []rune, dot Dot, character rune) (result []rune, moved Dot) { | ||||||
|  | 	dot = dot.Constrain(len(text)) | ||||||
| 	if dot.Empty() { | 	if dot.Empty() { | ||||||
| 		cursor := dot.End | 		result = append(result, text[:dot.End]...) | ||||||
| 		if cursor < 0         { cursor = 0 } |  | ||||||
| 		if cursor > len(text) { cursor = len(text) } |  | ||||||
| 		result = append(result, text[:cursor]...) |  | ||||||
| 		result = append(result, character) | 		result = append(result, character) | ||||||
| 		if cursor < len(text) { | 		if dot.End < len(text) { | ||||||
| 			result = append(result, text[cursor:]...) | 			result = append(result, text[dot.End:]...) | ||||||
| 		} | 		} | ||||||
| 		moved = EmptyDot(cursor + 1) | 		moved = EmptyDot(dot.Add(1).End) | ||||||
| 		return | 		return | ||||||
| 	} else { | 	} else { | ||||||
|  | 		dot = dot.Canon() | ||||||
| 		result = append(result, text[:dot.Start]...) | 		result = append(result, text[:dot.Start]...) | ||||||
| 		result = append(result, character) | 		result = append(result, character) | ||||||
| 		result = append(result, text[dot.End:]...) | 		result = append(result, text[dot.End:]...) | ||||||
| 		moved = EmptyDot(dot.Start) | 		moved = EmptyDot(dot.Add(1).Start) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func MoveLeft (text []rune, dot Dot, word bool) (moved Dot) { | func MoveLeft (text []rune, dot Dot, word bool) (moved Dot) { | ||||||
| 	cursor := dot.Start | 	dot = dot.Constrain(len(text)) | ||||||
| 
 |  | ||||||
| 	if cursor < 1         { return EmptyDot(cursor) } |  | ||||||
| 	if cursor > len(text) { cursor = len(text) } |  | ||||||
| 
 |  | ||||||
| 	distance := 1 | 	distance := 1 | ||||||
| 	if word { | 	if word { | ||||||
| 		distance = WordToLeft(text, dot) | 		distance = WordToLeft(text, dot.Start) | ||||||
| 	} | 	} | ||||||
| 	moved = EmptyDot(cursor - distance) | 	moved = EmptyDot(dot.Sub(distance).Start) | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func MoveRight (text []rune, dot Dot, word bool) (moved Dot) { | func MoveRight (text []rune, dot Dot, word bool) (moved Dot) { | ||||||
| 	cursor := dot.End | 	dot = dot.Constrain(len(text)) | ||||||
| 	 |  | ||||||
| 	if cursor < 0         { return EmptyDot(cursor) } |  | ||||||
| 	if cursor > len(text) { cursor = len(text) } |  | ||||||
| 
 |  | ||||||
| 	distance := 1 | 	distance := 1 | ||||||
| 	if word { | 	if word { | ||||||
| 		distance = WordToRight(text, dot) | 		distance = WordToRight(text, dot.End) | ||||||
| 	} | 	} | ||||||
| 	moved = EmptyDot(cursor + distance) | 	moved = EmptyDot(dot.Add(distance).End) | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | func SelectLeft (text []rune, dot Dot, word bool) (moved Dot) { | ||||||
|  | 	dot = dot.Constrain(len(text)) | ||||||
|  | 	distance := 1 | ||||||
|  | 	if word { | ||||||
|  | 		distance = WordToLeft(text, dot.Start) | ||||||
|  | 	} | ||||||
|  | 	dot.Start -= distance | ||||||
|  | 	return dot | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func SelectRight (text []rune, dot Dot, word bool) (moved Dot) { | ||||||
|  | 	dot = dot.Constrain(len(text)) | ||||||
|  | 	distance := 1 | ||||||
|  | 	if word { | ||||||
|  | 		distance = WordToRight(text, dot.End) | ||||||
|  | 	} | ||||||
|  | 	dot.Start += distance | ||||||
|  | 	return dot | ||||||
|  | } | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user