<commit_before>// Copyright 2015 <NAME>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"fmt"

	"github.com/thinkofdeath/steven/chat"
	"github.com/thinkofdeath/steven/render"
)

const (
	chatHistoryLines = 10
	maxLineWidth     = 300
)

type ChatUI struct {
	Elements []*chatUIElement

	dirty    bool
	Lines    [chatHistoryLines]chat.AnyComponent
	lineFade [chatHistoryLines]float64

	lineLength float64
}

type chatUIElement struct {
	text   *render.UIText
	offset int
	line   int
}

func (c *ChatUI) render(delta float64) {
	for i := range c.lineFade {
		if c.lineFade[i] > 0 {
			c.lineFade[i] -= 0.005 * delta
		} else {
			c.lineFade[i] = 0
		}
	}
	if c.dirty {
		c.dirty = false
		for _, e := range c.Elements {
			if e.text != nil {
				e.text.Free()
			}
		}
		c.Elements = nil

		for i, line := range c.Lines {
			c.newLine()
			if line.Value == nil {
				continue
			}
			c.lineLength = 0
			c.renderComponent(i, line.Value, nil)
		}
	}
	for _, e := range c.Elements {
		e.text.Alpha(c.lineFade[e.line])
	}
}

func (c *ChatUI) renderComponent(line int, co interface{}, color chatGetColorFunc) {
	switch co := co.(type) {
	case *chat.TextComponent:
		getColor := chatGetColor(&co.Component, color)
		width := 0
		runes := []rune(co.Text)
		r, g, b := chatColorRGB(getColor())
		for i := 0; i < len(runes); i++ {
			size := render.SizeOfCharacter(runes[i])
			if width+size > maxLineWidth {
				e := &chatUIElement{
					text:   render.AddUIText(string(runes[:i]), 2+c.lineLength, 480-18, r, g, b),
					offset: 0,
					line:   line,
				}
				c.Elements = append(c.Elements, e)
				c.lineLength = 0
				runes = runes[i:]
				i = 0
				width = 0
				c.newLine()
			}
			width += size
		}
		e := &chatUIElement{
			line:   line,
			text:   render.AddUIText(string(runes), 2+c.lineLength, 480-18, r, g, b),
			offset: 0,
		}
		c.Elements = append(c.Elements, e)
		c.lineLength += e.text.Width
		for _, e := range co.Extra {
			c.renderComponent(line, e.Value, getColor)
		}
	default:
		fmt.Printf("Can't handle %T\n", co)
	}<commit_msg>steven: clean up chat handling<commit_after>// Copyright 2015 <NAME>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"fmt"

	"github.com/thinkofdeath/steven/chat"
	"github.com/thinkofdeath/steven/render"
)

const (
	chatHistoryLines = 10
	maxLineWidth     = 300
)

type ChatUI struct {
	Elements []*chatUIElement

	dirty    bool
	Lines    [chatHistoryLines]chat.AnyComponent
	lineFade [chatHistoryLines]float64

	lineLength float64
}

type chatUIElement struct {
	text   *render.UIText
	offset int
	line   int
}

func (c *ChatUI) render(delta float64) {
	for i := range c.lineFade {
		if c.lineFade[i] > 0 {
			c.lineFade[i] -= 0.005 * delta
		} else {
			c.lineFade[i] = 0
		}
	}
	if c.dirty {
		c.dirty = false
		for _, e := range c.Elements {
			if e.text != nil {
				e.text.Free()
			}
		}
		c.Elements = nil

		for i, line := range c.Lines {
			c.newLine()
			if line.Value == nil {
				continue
			}
			c.lineLength = 0
			c.renderComponent(i, line.Value, nil)
		}
	}
	for _, e := range c.Elements {
		e.text.Alpha(c.lineFade[e.line])
	}
}

func (c *ChatUI) renderComponent(line int, co interface{}, color chatGetColorFunc) {
	switch co := co.(type) {
	case *chat.TextComponent:
		getColor := chatGetColor(&co.Component, color)
		width := 0
		runes := []rune(co.Text)
		r, g, b := chatColorRGB(getColor())
		for i := 0; i < len(runes); i++ {
			size := render.SizeOfCharacter(runes[i])
			if width+size > maxLineWidth {
				c.appendText(line, string(runes[:i]), r, g, b)
				c.lineLength = 0
				runes = runes[i:]
				i = 0
				width = 0
				c.newLine()
			}
			width += size
		}
		c.lineLength += c.appendText(line, string(runes), r, g, b)
		for _, e := range co.Extra {
			c.renderComponent(line, e.Value, getColor)
		}
	default:
		fmt.Printf("Can't handle %T\n", co)
	}
}

func (c *ChatUI) appendText(line int, str string, r, g, b int) float64 {
	e := &chatUIElement{
		text:   render.AddUIText(str, 2+c.lineLength, 480-18, r, g, b),
		offset: 0,
		line:   line,
	}
	c.Elements = append(c.Elements, e)
	return e.text.Width
{'fraction_non_alphanumeric': 0.13455181800505542, 'fraction_numerical': 0.01652731868559207, 'mean_word_length': 2.984508133230054, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3530547', 'n_tokens_mistral': 2103, 'n_tokens_neox': 1841, 'n_words': 531}