mirror of
https://github.com/vsariola/sointu.git
synced 2025-05-27 10:50:23 -04:00
feat: highlight sliders that are controlled by a send, and add tooltip (over value)
This commit is contained in:
parent
b423d04c17
commit
55c062a390
19
patch.go
19
patch.go
@ -464,3 +464,22 @@ func (p Patch) FindUnit(id int) (instrIndex int, unitIndex int, err error) {
|
|||||||
}
|
}
|
||||||
return 0, 0, fmt.Errorf("could not find a unit with id %v", id)
|
return 0, 0, fmt.Errorf("could not find a unit with id %v", id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func FindParamForModulationPort(unitName string, index int) *UnitParameter {
|
||||||
|
// qm210: couldn't see a function yet that matches the parameter index to the modulateable param.
|
||||||
|
// Not sure whether *UnitParameters is good here, would this make them mutable?
|
||||||
|
unitType, ok := UnitTypes[unitName]
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
for _, param := range unitType {
|
||||||
|
if index == 0 {
|
||||||
|
return ¶m
|
||||||
|
}
|
||||||
|
if param.CanModulate {
|
||||||
|
index--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// index outside range
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -72,3 +72,5 @@ var scrollBarColor = color.NRGBA{R: 255, G: 255, B: 255, A: 32}
|
|||||||
var warningColor = color.NRGBA{R: 251, G: 192, B: 45, A: 255}
|
var warningColor = color.NRGBA{R: 251, G: 192, B: 45, A: 255}
|
||||||
|
|
||||||
var dialogBgColor = color.NRGBA{R: 0, G: 0, B: 0, A: 224}
|
var dialogBgColor = color.NRGBA{R: 0, G: 0, B: 0, A: 224}
|
||||||
|
|
||||||
|
var paramIsSendTargetColor = color.NRGBA{R: 120, G: 120, B: 210, A: 255}
|
||||||
|
@ -3,10 +3,6 @@ package gioui
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"image"
|
|
||||||
"io"
|
|
||||||
"math"
|
|
||||||
|
|
||||||
"gioui.org/io/clipboard"
|
"gioui.org/io/clipboard"
|
||||||
"gioui.org/io/event"
|
"gioui.org/io/event"
|
||||||
"gioui.org/io/key"
|
"gioui.org/io/key"
|
||||||
@ -17,10 +13,17 @@ import (
|
|||||||
"gioui.org/unit"
|
"gioui.org/unit"
|
||||||
"gioui.org/widget"
|
"gioui.org/widget"
|
||||||
"gioui.org/widget/material"
|
"gioui.org/widget/material"
|
||||||
|
"gioui.org/x/component"
|
||||||
|
sointu "github.com/vsariola/sointu"
|
||||||
"github.com/vsariola/sointu/tracker"
|
"github.com/vsariola/sointu/tracker"
|
||||||
"golang.org/x/exp/shiny/materialdesign/icons"
|
"golang.org/x/exp/shiny/materialdesign/icons"
|
||||||
"golang.org/x/text/cases"
|
"golang.org/x/text/cases"
|
||||||
"golang.org/x/text/language"
|
"golang.org/x/text/language"
|
||||||
|
"image"
|
||||||
|
"io"
|
||||||
|
"iter"
|
||||||
|
"math"
|
||||||
|
"slices"
|
||||||
)
|
)
|
||||||
|
|
||||||
type UnitEditor struct {
|
type UnitEditor struct {
|
||||||
@ -240,6 +243,7 @@ type ParameterWidget struct {
|
|||||||
unitBtn widget.Clickable
|
unitBtn widget.Clickable
|
||||||
unitMenu Menu
|
unitMenu Menu
|
||||||
Parameter tracker.Parameter
|
Parameter tracker.Parameter
|
||||||
|
tipArea component.TipArea
|
||||||
}
|
}
|
||||||
|
|
||||||
type ParameterStyle struct {
|
type ParameterStyle struct {
|
||||||
@ -247,17 +251,21 @@ type ParameterStyle struct {
|
|||||||
w *ParameterWidget
|
w *ParameterWidget
|
||||||
Theme *material.Theme
|
Theme *material.Theme
|
||||||
Focus bool
|
Focus bool
|
||||||
|
sends []sointu.Unit
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Tracker) ParamStyle(th *material.Theme, paramWidget *ParameterWidget) ParameterStyle {
|
func (t *Tracker) ParamStyle(th *material.Theme, paramWidget *ParameterWidget) ParameterStyle {
|
||||||
|
sends := slices.Collect(t.Model.CollectSendsTo(paramWidget.Parameter))
|
||||||
return ParameterStyle{
|
return ParameterStyle{
|
||||||
tracker: t, // TODO: we need this to pull the instrument names for ID style parameters, find out another way
|
tracker: t, // TODO: we need this to pull the instrument names for ID style parameters, find out another way
|
||||||
Theme: th,
|
Theme: th,
|
||||||
w: paramWidget,
|
w: paramWidget,
|
||||||
|
sends: sends,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p ParameterStyle) Layout(gtx C) D {
|
func (p ParameterStyle) Layout(gtx C) D {
|
||||||
|
sends := slices.Collect(p.findSends())
|
||||||
return layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle}.Layout(gtx,
|
return layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle}.Layout(gtx,
|
||||||
layout.Rigid(func(gtx C) D {
|
layout.Rigid(func(gtx C) D {
|
||||||
gtx.Constraints.Min.X = gtx.Dp(unit.Dp(110))
|
gtx.Constraints.Min.X = gtx.Dp(unit.Dp(110))
|
||||||
@ -288,6 +296,10 @@ func (p ParameterStyle) Layout(gtx C) D {
|
|||||||
}
|
}
|
||||||
sliderStyle := material.Slider(p.Theme, &p.w.floatWidget)
|
sliderStyle := material.Slider(p.Theme, &p.w.floatWidget)
|
||||||
sliderStyle.Color = p.Theme.Fg
|
sliderStyle.Color = p.Theme.Fg
|
||||||
|
|
||||||
|
if len(sends) > 0 {
|
||||||
|
sliderStyle.Color = paramIsSendTargetColor
|
||||||
|
}
|
||||||
r := image.Rectangle{Max: gtx.Constraints.Min}
|
r := image.Rectangle{Max: gtx.Constraints.Min}
|
||||||
defer clip.Rect(r).Push(gtx.Ops).Pop()
|
defer clip.Rect(r).Push(gtx.Ops).Pop()
|
||||||
defer pointer.PassOp{}.Push(gtx.Ops).Pop()
|
defer pointer.PassOp{}.Push(gtx.Ops).Pop()
|
||||||
@ -336,15 +348,11 @@ func (p ParameterStyle) Layout(gtx C) D {
|
|||||||
targetInstrument := p.tracker.Instrument(targetI)
|
targetInstrument := p.tracker.Instrument(targetI)
|
||||||
instrName = targetInstrument.Name
|
instrName = targetInstrument.Name
|
||||||
units := targetInstrument.Units
|
units := targetInstrument.Units
|
||||||
unitName = fmt.Sprintf("%d: %s %s", targetU, units[targetU].Type, units[targetU].Comment)
|
unitName = unitNameFor(targetU, units[targetU])
|
||||||
unitItems = make([]MenuItem, len(units))
|
unitItems = make([]MenuItem, len(units))
|
||||||
for j, unit := range units {
|
for j, unit := range units {
|
||||||
id := unit.ID
|
id := unit.ID
|
||||||
text := unit.Type
|
unitItems[j].Text = unitNameFor(j, unit)
|
||||||
if unit.Comment != "" {
|
|
||||||
text = fmt.Sprintf("%s \"%s\"", text, unit.Comment)
|
|
||||||
}
|
|
||||||
unitItems[j].Text = fmt.Sprintf("%d: %s", j, text)
|
|
||||||
unitItems[j].IconBytes = icons.NavigationChevronRight
|
unitItems[j].IconBytes = icons.NavigationChevronRight
|
||||||
unitItems[j].Doer = tracker.Allow(func() {
|
unitItems[j].Doer = tracker.Allow(func() {
|
||||||
tracker.Int{IntData: p.w.Parameter}.Set(id)
|
tracker.Int{IntData: p.w.Parameter}.Set(id)
|
||||||
@ -365,9 +373,68 @@ func (p ParameterStyle) Layout(gtx C) D {
|
|||||||
}),
|
}),
|
||||||
layout.Rigid(func(gtx C) D {
|
layout.Rigid(func(gtx C) D {
|
||||||
if p.w.Parameter.Type() != tracker.IDParameter {
|
if p.w.Parameter.Type() != tracker.IDParameter {
|
||||||
return Label(p.w.Parameter.Hint(), white, p.tracker.Theme.Shaper)(gtx)
|
label := Label(p.w.Parameter.Hint(), white, p.tracker.Theme.Shaper)
|
||||||
|
info := p.buildTooltip(sends)
|
||||||
|
if info == "" {
|
||||||
|
return label(gtx)
|
||||||
|
}
|
||||||
|
tooltip := component.PlatformTooltip(p.Theme, info)
|
||||||
|
return p.w.tipArea.Layout(gtx, tooltip, label)
|
||||||
}
|
}
|
||||||
return D{}
|
return D{}
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func unitNameFor(index int, u sointu.Unit) string {
|
||||||
|
text := u.Type
|
||||||
|
if u.Comment != "" {
|
||||||
|
text = fmt.Sprintf("%s \"%s\"", text, u.Comment)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%d: %s", index, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p ParameterStyle) findSends() iter.Seq[sointu.Unit] {
|
||||||
|
return func(yield func(sointu.Unit) bool) {
|
||||||
|
param, ok := (p.w.Parameter).(tracker.NamedParameter)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, send := range p.sends {
|
||||||
|
port := send.Parameters["port"]
|
||||||
|
unitParam := sointu.FindParamForModulationPort(param.Unit().Type, port)
|
||||||
|
if unitParam.Name != param.Name() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !yield(send) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p ParameterStyle) buildTooltip(sends []sointu.Unit) string {
|
||||||
|
if len(sends) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
targetParam := (p.w.Parameter).(tracker.NamedParameter)
|
||||||
|
targetInstr := p.tracker.Model.InstrumentForUnit(targetParam.Unit().ID)
|
||||||
|
amounts := ""
|
||||||
|
for i := 0; i < len(sends); i++ {
|
||||||
|
sourceInstr := p.tracker.Model.InstrumentForUnit(sends[0].ID)
|
||||||
|
sourceInfo := ""
|
||||||
|
if sourceInstr != targetInstr {
|
||||||
|
sourceInfo = fmt.Sprintf(" (%s)", sourceInstr.Name)
|
||||||
|
}
|
||||||
|
if amounts == "" {
|
||||||
|
amounts = fmt.Sprintf("x %d%s", sends[i].Parameters["amount"], sourceInfo)
|
||||||
|
} else {
|
||||||
|
amounts = fmt.Sprintf("%s, x %d%s", amounts, sends[i].Parameters["amount"], sourceInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
count := "1 send"
|
||||||
|
if len(sends) > 1 {
|
||||||
|
count = fmt.Sprintf("%d sends")
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s [%s]", count, amounts)
|
||||||
|
}
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"iter"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
@ -597,3 +598,38 @@ func clamp(a, min, max int) int {
|
|||||||
}
|
}
|
||||||
return a
|
return a
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Model) CollectSendsTo(param Parameter) iter.Seq[sointu.Unit] {
|
||||||
|
return func(yield func(sointu.Unit) bool) {
|
||||||
|
p, ok := param.(NamedParameter)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, instr := range m.d.Song.Patch {
|
||||||
|
for _, unit := range instr.Units {
|
||||||
|
if unit.Type != "send" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
targetId, ok := unit.Parameters["target"]
|
||||||
|
if !ok || targetId != p.Unit().ID {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !yield(unit) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Model) InstrumentForUnit(id int) *sointu.Instrument {
|
||||||
|
for _, instr := range m.d.Song.Patch {
|
||||||
|
for _, unit := range instr.Units {
|
||||||
|
if unit.ID == id {
|
||||||
|
return &instr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ID does not exist
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -204,6 +204,10 @@ func (p NamedParameter) LargeStep() int {
|
|||||||
return 16
|
return 16
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p NamedParameter) Unit() sointu.Unit {
|
||||||
|
return *p.parameter.unit
|
||||||
|
}
|
||||||
|
|
||||||
// GmDlsEntryParameter
|
// GmDlsEntryParameter
|
||||||
|
|
||||||
func (p GmDlsEntryParameter) Name() string { return "sample" }
|
func (p GmDlsEntryParameter) Name() string { return "sample" }
|
||||||
|
Loading…
Reference in New Issue
Block a user