mirror of
https://github.com/vsariola/sointu.git
synced 2025-07-18 21:14:31 -04:00
refactor(tracker, gioui): get rid of EditMode, use gio focus instead
This commit is contained in:
247
tracker/gioui/ordereditor.go
Normal file
247
tracker/gioui/ordereditor.go
Normal file
@ -0,0 +1,247 @@
|
||||
package gioui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"image"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"gioui.org/f32"
|
||||
"gioui.org/io/key"
|
||||
"gioui.org/io/pointer"
|
||||
"gioui.org/layout"
|
||||
"gioui.org/op"
|
||||
"gioui.org/op/clip"
|
||||
"gioui.org/op/paint"
|
||||
"gioui.org/text"
|
||||
"gioui.org/unit"
|
||||
"gioui.org/widget"
|
||||
"github.com/vsariola/sointu/tracker"
|
||||
)
|
||||
|
||||
const patternCellHeight = 16
|
||||
const patternCellWidth = 16
|
||||
const patternRowMarkerWidth = 30
|
||||
|
||||
type OrderEditor struct {
|
||||
list *layout.List
|
||||
scrollBar *ScrollBar
|
||||
tag bool
|
||||
focused bool
|
||||
requestFocus bool
|
||||
}
|
||||
|
||||
func NewOrderEditor() *OrderEditor {
|
||||
return &OrderEditor{
|
||||
list: &layout.List{Axis: layout.Vertical},
|
||||
scrollBar: &ScrollBar{Axis: layout.Vertical},
|
||||
}
|
||||
}
|
||||
|
||||
func (oe *OrderEditor) Focus() {
|
||||
oe.requestFocus = true
|
||||
}
|
||||
|
||||
func (oe *OrderEditor) Focused() bool {
|
||||
return oe.focused
|
||||
}
|
||||
|
||||
func (oe *OrderEditor) Layout(gtx C, t *Tracker) D {
|
||||
return Surface{Gray: 24, Focus: oe.focused}.Layout(gtx, func(gtx C) D {
|
||||
return oe.doLayout(gtx, t)
|
||||
})
|
||||
}
|
||||
|
||||
func (oe *OrderEditor) doLayout(gtx C, t *Tracker) D {
|
||||
for _, e := range gtx.Events(&oe.tag) {
|
||||
switch e := e.(type) {
|
||||
case key.FocusEvent:
|
||||
oe.focused = e.Focus
|
||||
case pointer.Event:
|
||||
if e.Type == pointer.Press {
|
||||
key.FocusOp{Tag: &oe.tag}.Add(gtx.Ops)
|
||||
}
|
||||
case key.Event:
|
||||
if !oe.focused || e.State != key.Press {
|
||||
continue
|
||||
}
|
||||
switch e.Name {
|
||||
case key.NameDeleteForward, key.NameDeleteBackward:
|
||||
if e.Modifiers.Contain(key.ModShortcut) {
|
||||
t.DeleteOrderRow(e.Name == key.NameDeleteForward)
|
||||
} else {
|
||||
t.DeletePatternSelection()
|
||||
if !(t.NoteTracking() && t.player.Playing()) && t.Step.Value > 0 {
|
||||
t.SetCursor(t.Cursor().AddPatterns(1))
|
||||
t.SetSelectionCorner(t.Cursor())
|
||||
}
|
||||
}
|
||||
case "Space":
|
||||
_, playing := t.player.Position()
|
||||
if !playing {
|
||||
t.SetNoteTracking(!e.Modifiers.Contain(key.ModShortcut))
|
||||
startRow := t.Cursor().SongRow
|
||||
startRow.Row = 0
|
||||
t.player.Play(startRow)
|
||||
} else {
|
||||
t.player.Stop()
|
||||
}
|
||||
case key.NameReturn:
|
||||
t.AddOrderRow(!e.Modifiers.Contain(key.ModShortcut))
|
||||
case key.NameUpArrow:
|
||||
cursor := t.Cursor()
|
||||
if e.Modifiers.Contain(key.ModShortcut) {
|
||||
cursor.SongRow = tracker.SongRow{}
|
||||
} else {
|
||||
cursor.Row -= t.Song().Score.RowsPerPattern
|
||||
}
|
||||
t.SetNoteTracking(false)
|
||||
t.SetCursor(cursor)
|
||||
case key.NameDownArrow:
|
||||
cursor := t.Cursor()
|
||||
if e.Modifiers.Contain(key.ModShortcut) {
|
||||
cursor.Row = t.Song().Score.LengthInRows() - 1
|
||||
} else {
|
||||
cursor.Row += t.Song().Score.RowsPerPattern
|
||||
}
|
||||
t.SetNoteTracking(false)
|
||||
t.SetCursor(cursor)
|
||||
case key.NameLeftArrow:
|
||||
cursor := t.Cursor()
|
||||
if e.Modifiers.Contain(key.ModShortcut) {
|
||||
cursor.Track = 0
|
||||
} else {
|
||||
cursor.Track--
|
||||
}
|
||||
t.SetCursor(cursor)
|
||||
case key.NameRightArrow:
|
||||
cursor := t.Cursor()
|
||||
if e.Modifiers.Contain(key.ModShortcut) {
|
||||
cursor.Track = len(t.Song().Score.Tracks) - 1
|
||||
} else {
|
||||
cursor.Track++
|
||||
}
|
||||
t.SetCursor(cursor)
|
||||
}
|
||||
if (e.Name != key.NameLeftArrow &&
|
||||
e.Name != key.NameRightArrow &&
|
||||
e.Name != key.NameUpArrow &&
|
||||
e.Name != key.NameDownArrow) ||
|
||||
!e.Modifiers.Contain(key.ModShift) {
|
||||
t.SetSelectionCorner(t.Cursor())
|
||||
}
|
||||
if e.Modifiers.Contain(key.ModShortcut) {
|
||||
continue
|
||||
}
|
||||
if iv, err := strconv.Atoi(e.Name); err == nil {
|
||||
t.SetCurrentPattern(iv)
|
||||
if !(t.NoteTracking() && t.player.Playing()) && t.Step.Value > 0 {
|
||||
t.SetCursor(t.Cursor().AddPatterns(1))
|
||||
t.SetSelectionCorner(t.Cursor())
|
||||
}
|
||||
}
|
||||
if b := int(e.Name[0]) - 'A'; len(e.Name) == 1 && b >= 0 && b < 26 {
|
||||
t.SetCurrentPattern(b + 10)
|
||||
if !(t.NoteTracking() && t.player.Playing()) && t.Step.Value > 0 {
|
||||
t.SetCursor(t.Cursor().AddPatterns(1))
|
||||
t.SetSelectionCorner(t.Cursor())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
defer op.Save(gtx.Ops).Load()
|
||||
if oe.requestFocus {
|
||||
oe.requestFocus = false
|
||||
key.FocusOp{Tag: &oe.tag}.Add(gtx.Ops)
|
||||
}
|
||||
clip.Rect{Max: gtx.Constraints.Max}.Add(gtx.Ops)
|
||||
rect := image.Rect(0, 0, gtx.Constraints.Max.X, gtx.Constraints.Max.Y)
|
||||
pointer.Rect(rect).Add(gtx.Ops)
|
||||
pointer.InputOp{Tag: &oe.tag,
|
||||
Types: pointer.Press,
|
||||
}.Add(gtx.Ops)
|
||||
key.InputOp{Tag: &oe.tag}.Add(gtx.Ops)
|
||||
patternRect := tracker.SongRect{
|
||||
Corner1: tracker.SongPoint{SongRow: tracker.SongRow{Pattern: t.Cursor().Pattern}, Track: t.Cursor().Track},
|
||||
Corner2: tracker.SongPoint{SongRow: tracker.SongRow{Pattern: t.SelectionCorner().Pattern}, Track: t.SelectionCorner().Track},
|
||||
}
|
||||
|
||||
// draw the single letter titles for tracks
|
||||
{
|
||||
gtx := gtx
|
||||
curVoice := 0
|
||||
stack := op.Save(gtx.Ops)
|
||||
op.Offset(f32.Pt(patternRowMarkerWidth, 0)).Add(gtx.Ops)
|
||||
gtx.Constraints = layout.Exact(image.Pt(patternCellWidth, patternCellHeight))
|
||||
for _, track := range t.Song().Score.Tracks {
|
||||
instr, err := t.Song().Patch.InstrumentForVoice(curVoice)
|
||||
var title string
|
||||
if err == nil && len(t.Song().Patch[instr].Name) > 0 {
|
||||
title = string(t.Song().Patch[instr].Name[0])
|
||||
} else {
|
||||
title = "I"
|
||||
}
|
||||
LabelStyle{Alignment: layout.N, Text: title, FontSize: unit.Dp(12), Color: mediumEmphasisTextColor}.Layout(gtx)
|
||||
op.Offset(f32.Pt(patternCellWidth, 0)).Add(gtx.Ops)
|
||||
curVoice += track.NumVoices
|
||||
}
|
||||
stack.Load()
|
||||
}
|
||||
op.Offset(f32.Pt(0, patternCellHeight)).Add(gtx.Ops)
|
||||
gtx.Constraints.Max.Y -= patternCellHeight
|
||||
gtx.Constraints.Min.Y -= patternCellHeight
|
||||
element := func(gtx C, j int) D {
|
||||
if playPos, ok := t.player.Position(); ok && j == playPos.Pattern {
|
||||
paint.FillShape(gtx.Ops, patternPlayColor, clip.Rect{Max: image.Pt(gtx.Constraints.Max.X, patternCellHeight)}.Op())
|
||||
}
|
||||
paint.ColorOp{Color: rowMarkerPatternTextColor}.Add(gtx.Ops)
|
||||
widget.Label{}.Layout(gtx, textShaper, trackerFont, trackerFontSize, strings.ToUpper(fmt.Sprintf("%02x", j)))
|
||||
stack := op.Save(gtx.Ops)
|
||||
op.Offset(f32.Pt(patternRowMarkerWidth, 0)).Add(gtx.Ops)
|
||||
for i, track := range t.Song().Score.Tracks {
|
||||
paint.FillShape(gtx.Ops, patternCellColor, clip.Rect{Min: image.Pt(1, 1), Max: image.Pt(patternCellWidth-1, patternCellHeight-1)}.Op())
|
||||
paint.ColorOp{Color: patternTextColor}.Add(gtx.Ops)
|
||||
if j >= 0 && j < len(track.Order) && track.Order[j] >= 0 {
|
||||
gtx := gtx
|
||||
gtx.Constraints.Max.X = patternCellWidth
|
||||
op.Offset(f32.Pt(0, -2)).Add(gtx.Ops)
|
||||
widget.Label{Alignment: text.Middle}.Layout(gtx, textShaper, trackerFont, trackerFontSize, patternIndexToString(track.Order[j]))
|
||||
op.Offset(f32.Pt(0, 2)).Add(gtx.Ops)
|
||||
}
|
||||
point := tracker.SongPoint{Track: i, SongRow: tracker.SongRow{Pattern: j}}
|
||||
if oe.focused || t.TrackEditor.Focused() {
|
||||
if patternRect.Contains(point) {
|
||||
color := inactiveSelectionColor
|
||||
if oe.focused {
|
||||
color = selectionColor
|
||||
if point.Pattern == t.Cursor().Pattern && point.Track == t.Cursor().Track {
|
||||
color = cursorColor
|
||||
}
|
||||
}
|
||||
paint.FillShape(gtx.Ops, color, clip.Rect{Max: image.Pt(patternCellWidth, patternCellHeight)}.Op())
|
||||
}
|
||||
}
|
||||
op.Offset(f32.Pt(patternCellWidth, 0)).Add(gtx.Ops)
|
||||
}
|
||||
stack.Load()
|
||||
return D{Size: image.Pt(gtx.Constraints.Max.X, patternCellHeight)}
|
||||
}
|
||||
|
||||
return layout.Stack{Alignment: layout.NE}.Layout(gtx,
|
||||
layout.Expanded(func(gtx C) D {
|
||||
return oe.list.Layout(gtx, t.Song().Score.Length, element)
|
||||
}),
|
||||
layout.Expanded(func(gtx C) D {
|
||||
return oe.scrollBar.Layout(gtx, unit.Dp(10), t.Song().Score.Length, &oe.list.Position)
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
func patternIndexToString(index int) string {
|
||||
if index < 0 {
|
||||
return ""
|
||||
} else if index < 10 {
|
||||
return string('0' + byte(index))
|
||||
}
|
||||
return string('A' + byte(index-10))
|
||||
}
|
Reference in New Issue
Block a user