<commit_before>package editor

import (
	"image"
	"time"

	"sigint.ca/graphics/editor/internal/address"

	"golang.org/x/mobile/event/mouse"
)

// SendMouseEvent sends a mouse event to be interpreted by the Editor.
func (ed *Editor) SendMouseEvent(e mouse.Event) {
	ed.handleMouseEvent(e)
}

// SendScrollEvent sends a scroll event to be interpreted by the Editor.
func (ed *Editor) SendScrollEvent(e mouse.ScrollEvent) {
	var pt image.Point
	if e.Precise {
		pt.X = int(e.Dx)
		pt.Y = int(e.Dy)
	} else {
		pt.X = int(e.Dx * float32(ed.font.height))
		pt.Y = int(e.Dy * float32(ed.font.height))
	}
	oldPt := ed.scrollPt
	ed.scroll(pt)
	if ed.scrollPt != oldPt {
		ed.dirty = true
	}
}

const (
	b1 = 1 << uint(mouse.ButtonLeft)
	b2 = 1 << uint(mouse.ButtonMiddle)
	b3 = 1 << uint(mouse.ButtonRight)
)

const dClickPause = 500 * time.Millisecond
const twitch = 3 // pixels

type mouseState struct {
	buttons       uint32 // a bit field of mouse buttons currently pressed
	chording      bool
	lastClickTime time.Time // used to detect a double-click

	pt image.Point
	a  address.Simple

	sweepOrigin image.Point    // the origin of a sweep
	sweepLast   address.Simple // the last column that was swept
}

func (ed *Editor) handleMouseEvent(e mouse.Event) {
	if e.Button == mouse.ButtonNone {
		return
	}

	// a mouse event commits any pending transformation
	ed.commitTransformation()

	pt := image.Pt(int(e.X), int(e.Y))
	ed.m.pt = pt.Add(ed.visible().Min) // adjust for scrolling
	ed.m.a = ed.getAddress(ed.m.pt)

	if pt.In(ed.sbRect()) && e.Direction != mouse.DirRelease {
		ed.clickSb(e)
	} else if e.Direction == mouse.DirPress {
		ed.click(e)
	} else if e.Direction == mouse.DirNone {
		ed.sweep(e)
	} else if e.Direction == mouse.DirRelease {
		ed.release(e)
	}
}

// scrollbar click
func (ed *Editor) clickSb(e mouse.Event) {
	height := float64(ed.visible().Dy())
	percent := (float64(e.Y) + float64(ed.r.Min.Y)) / height
	// disregard any chording; act on individual mouse.DirPress events
	switch e.Button {
	case mouse.ButtonLeft:
		ed.scroll(image.Pt(0, int(height*percent)))
	case mouse.ButtonMiddle:
		ed.scrollPt.Y = int(float64(ed.docHeight()) * percent)
	case mouse.ButtonRight:
		ed.scroll(image.Pt(0, int(-height*percent)))
	}
	ed.dirty = true
}

func (ed *Editor) click(e mouse.Event) {
	a, pt := ed.m.a, ed.m.pt

	ed.m.buttons |= 1 << uint(e.Button)
	ed.m.sweepOrigin = pt
<commit_msg>editor: fix some scrollbar edge cases<commit_after>package editor

import (
	"image"
	"time"

	"sigint.ca/graphics/editor/internal/address"

	"golang.org/x/mobile/event/mouse"
)

// SendMouseEvent sends a mouse event to be interpreted by the Editor.
func (ed *Editor) SendMouseEvent(e mouse.Event) {
	ed.handleMouseEvent(e)
}

// SendScrollEvent sends a scroll event to be interpreted by the Editor.
func (ed *Editor) SendScrollEvent(e mouse.ScrollEvent) {
	var pt image.Point
	if e.Precise {
		pt.X = int(e.Dx)
		pt.Y = int(e.Dy)
	} else {
		pt.X = int(e.Dx * float32(ed.font.height))
		pt.Y = int(e.Dy * float32(ed.font.height))
	}
	oldPt := ed.scrollPt
	ed.scroll(pt)
	if ed.scrollPt != oldPt {
		ed.dirty = true
	}
}

const (
	b1 = 1 << uint(mouse.ButtonLeft)
	b2 = 1 << uint(mouse.ButtonMiddle)
	b3 = 1 << uint(mouse.ButtonRight)
)

const dClickPause = 500 * time.Millisecond
const twitch = 3 // pixels

type mouseState struct {
	buttons       uint32    // a bit field of mouse buttons currently pressed
	chording      bool      // a chord has been initiated
	scrolling     bool      // the scroll bar is being manipulated
	lastClickTime time.Time // used to detect a double-click

	pt image.Point
	a  address.Simple

	sweepOrigin image.Point    // the origin of a sweep
	sweepLast   address.Simple // the last column that was swept
}

func (ed *Editor) handleMouseEvent(e mouse.Event) {
	if e.Button == mouse.ButtonNone {
		return
	}

	// a mouse event commits any pending transformation
	ed.commitTransformation()

	pt := image.Pt(int(e.X), int(e.Y))
	ed.m.pt = pt.Add(ed.visible().Min) // adjust for scrolling
	ed.m.a = ed.getAddress(ed.m.pt)

	if ed.m.scrolling || pt.In(ed.sbRect()) {
		if e.Direction == mouse.DirRelease {
			ed.m.scrolling = false
			return
		}
		ed.m.scrolling = true
		ed.clickSb(e)
	} else if e.Direction == mouse.DirPress {
		ed.click(e)
	} else if e.Direction == mouse.DirNone {
		ed.sweep(e)
	} else if e.Direction == mouse.DirRelease {
		ed.release(e)
	}
}

// scrollbar click
func (ed *Editor) clickSb(e mouse.Event) {
	height := float64(ed.visible().Dy())
	percent := (float64(e.Y) + float64(ed.r.Min.Y)) / height

	switch e.Button {
	case mouse.ButtonLeft:
		d := int(height * percent)
		if d < 0 {
			d = 0
		}
		ed.scroll(image.Pt(0, d))

	case mouse.ButtonRight:
		d := int(-height * percent)
		if d > 0 {
			d = 0
		}
		ed.scroll(image.Pt(0, d))

	case mouse.ButtonMiddle:
		ed.scrollPt.Y = int(float64(ed.docHeight()) * percent)
		ed.scroll(image.ZP) // fix potential invalid scrollPt

	}
	ed.dirty = true
}

func (ed *Editor) click(e mouse.Event) {
	a, pt := ed.m.a, ed.m.pt

	ed.m.buttons |= 1 << uint(e.Button)
	ed.m.sweepOrigin = pt

{'fraction_non_alphanumeric': 0.1402439024390244, 'fraction_numerical': 0.011408339889850511, 'mean_word_length': 3.2410341951626354, 'pattern_counts': {'":': 0, '<': 20, '<?xml version=': 0, '>': 4, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, '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': '178754', 'n_tokens_mistral': 2015, 'n_tokens_neox': 1886, 'n_words': 482}