mirror of
https://github.com/vsariola/sointu.git
synced 2025-07-23 07:24:47 -04:00
drafting
This commit is contained in:
parent
4e295a3a2f
commit
3c6c24c6af
@ -93,6 +93,16 @@ type (
|
||||
*Model
|
||||
}
|
||||
ShowLicense Model
|
||||
|
||||
ChooseSendSource struct {
|
||||
ID int
|
||||
*Model
|
||||
}
|
||||
ChooseSendTarget struct {
|
||||
ID int
|
||||
Port int
|
||||
*Model
|
||||
}
|
||||
)
|
||||
|
||||
// Action methods
|
||||
@ -517,6 +527,40 @@ func (d DeleteOrderRow) Do() {
|
||||
m.d.Cursor2.OrderRow = m.d.Cursor.OrderRow
|
||||
}
|
||||
|
||||
// ChooseSendSource
|
||||
|
||||
func (m *Model) IsChoosingSendTarget() bool {
|
||||
return m.d.SendSource > 0
|
||||
}
|
||||
|
||||
func (m *Model) ChooseSendSource(id int) Action {
|
||||
return MakeEnabledAction(ChooseSendSource{ID: id, Model: m})
|
||||
}
|
||||
func (s ChooseSendSource) Do() {
|
||||
defer (*Model)(s.Model).change("ChooseSendSource", NoChange, MinorChange)()
|
||||
s.Model.d.SendSource = s.ID
|
||||
}
|
||||
|
||||
// ChooseSendTarget
|
||||
|
||||
func (m *Model) ChooseSendTarget(id int, port int) Action {
|
||||
return MakeEnabledAction(ChooseSendTarget{ID: id, Port: port, Model: m})
|
||||
}
|
||||
func (s ChooseSendTarget) Do() {
|
||||
defer (*Model)(s.Model).change("ChooseSendTarget", SongChange, MinorChange)()
|
||||
sourceID := (*Model)(s.Model).d.SendSource
|
||||
s.d.SendSource = 0
|
||||
if sourceID <= 0 || s.ID <= 0 || s.Port < 0 || s.Port > 7 {
|
||||
return
|
||||
}
|
||||
si, su, err := s.d.Song.Patch.FindUnit(sourceID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s.d.Song.Patch[si].Units[su].Parameters["target"] = s.ID
|
||||
s.d.Song.Patch[si].Units[su].Parameters["port"] = s.Port
|
||||
}
|
||||
|
||||
// NewSong
|
||||
|
||||
func (m *Model) NewSong() Action { return MakeEnabledAction((*NewSong)(m)) }
|
||||
|
@ -176,6 +176,7 @@ func (m *Model) deriveParams(unit *sointu.Unit) []Parameter {
|
||||
if !ok {
|
||||
return ret
|
||||
}
|
||||
portIndex := 0
|
||||
for i, up := range unitType {
|
||||
if !up.CanSet && !up.CanModulate {
|
||||
continue // skip parameters that cannot be set or modulated
|
||||
@ -186,7 +187,12 @@ func (m *Model) deriveParams(unit *sointu.Unit) []Parameter {
|
||||
if unit.Type == "send" && up.Name == "port" {
|
||||
continue
|
||||
}
|
||||
ret = append(ret, Parameter{m: m, unit: unit, up: &unitType[i], vtable: &namedParameter{}})
|
||||
q := 0
|
||||
if up.CanModulate {
|
||||
portIndex++
|
||||
q = portIndex
|
||||
}
|
||||
ret = append(ret, Parameter{m: m, unit: unit, up: &unitType[i], vtable: &namedParameter{}, port: q})
|
||||
}
|
||||
if unit.Type == "oscillator" && unit.Parameters["type"] == sointu.Sample {
|
||||
ret = append(ret, Parameter{m: m, unit: unit, vtable: &gmDlsEntryParameter{}})
|
||||
|
@ -149,15 +149,6 @@ func (k *KnobWidget) update(gtx C) {
|
||||
}
|
||||
}
|
||||
|
||||
func (k *KnobWidget) strokeBg(gtx C) {
|
||||
diam := gtx.Dp(k.Style.Diameter)
|
||||
circle := clip.Ellipse{
|
||||
Min: image.Pt(0, 0),
|
||||
Max: image.Pt(diam, diam),
|
||||
}.Op(gtx.Ops)
|
||||
paint.FillShape(gtx.Ops, k.Style.Bg, circle)
|
||||
}
|
||||
|
||||
func (k *KnobWidget) strokeKnobArc(gtx C, color color.NRGBA, strokeWidth, diameter int, start, end float32) {
|
||||
rad := float32(diameter) / 2
|
||||
end = min(max(end, 0), 1)
|
||||
|
66
tracker/gioui/port.go
Normal file
66
tracker/gioui/port.go
Normal file
@ -0,0 +1,66 @@
|
||||
package gioui
|
||||
|
||||
import (
|
||||
"image"
|
||||
"image/color"
|
||||
"math"
|
||||
|
||||
"gioui.org/f32"
|
||||
"gioui.org/gesture"
|
||||
"gioui.org/io/event"
|
||||
"gioui.org/op/clip"
|
||||
"gioui.org/op/paint"
|
||||
"gioui.org/unit"
|
||||
)
|
||||
|
||||
type (
|
||||
PortState struct {
|
||||
click gesture.Click
|
||||
}
|
||||
|
||||
PortStyle struct {
|
||||
Diameter unit.Dp
|
||||
StrokeWidth unit.Dp
|
||||
Color color.NRGBA
|
||||
}
|
||||
|
||||
PortWidget struct {
|
||||
Theme *Theme
|
||||
Style *PortStyle
|
||||
State *PortState
|
||||
}
|
||||
)
|
||||
|
||||
func Port(t *Theme, p *PortState) PortWidget {
|
||||
return PortWidget{Theme: t, Style: &t.Port, State: p}
|
||||
}
|
||||
|
||||
func (p *PortWidget) Layout(gtx C) D {
|
||||
d := gtx.Dp(p.Style.Diameter)
|
||||
defer clip.Rect(image.Rectangle{Max: image.Pt(d, d)}).Push(gtx.Ops).Pop()
|
||||
event.Op(gtx.Ops, p.State)
|
||||
p.State.click.Add(gtx.Ops)
|
||||
p.strokeCircle(gtx)
|
||||
return D{Size: image.Pt(d, d)}
|
||||
}
|
||||
|
||||
func (p *PortState) Clicked(gtx C) bool {
|
||||
ev, ok := p.click.Update(gtx.Source)
|
||||
return ok && ev.Kind == gesture.KindClick
|
||||
}
|
||||
|
||||
func (p *PortWidget) strokeCircle(gtx C) {
|
||||
sw := float32(gtx.Dp(p.Style.StrokeWidth))
|
||||
d := float32(gtx.Dp(p.Style.Diameter))
|
||||
rad := d / 2
|
||||
center := f32.Point{X: rad, Y: rad}
|
||||
var path clip.Path
|
||||
path.Begin(gtx.Ops)
|
||||
path.MoveTo(f32.Pt(sw/2, rad))
|
||||
path.ArcTo(center, center, float32(math.Pi*2))
|
||||
paint.FillShape(gtx.Ops, p.Style.Color,
|
||||
clip.Stroke{
|
||||
Path: path.End(),
|
||||
Width: sw,
|
||||
}.Op())
|
||||
}
|
@ -85,16 +85,20 @@ type Theme struct {
|
||||
}
|
||||
}
|
||||
UnitEditor struct {
|
||||
Name LabelStyle
|
||||
Chooser LabelStyle
|
||||
Hint LabelStyle
|
||||
InvalidParam color.NRGBA
|
||||
SendTarget color.NRGBA
|
||||
Width unit.Dp
|
||||
Height unit.Dp
|
||||
RowTitle LabelStyle
|
||||
RowTitleWidth unit.Dp
|
||||
Error color.NRGBA
|
||||
Name LabelStyle
|
||||
Chooser LabelStyle
|
||||
Hint LabelStyle
|
||||
InvalidParam color.NRGBA
|
||||
SendTarget color.NRGBA
|
||||
Width unit.Dp
|
||||
Height unit.Dp
|
||||
UnitList struct {
|
||||
LabelWidth unit.Dp
|
||||
Name LabelStyle
|
||||
Disabled LabelStyle
|
||||
Error color.NRGBA
|
||||
}
|
||||
Error color.NRGBA
|
||||
}
|
||||
Cursor CursorStyle
|
||||
Selection CursorStyle
|
||||
@ -110,6 +114,7 @@ type Theme struct {
|
||||
ScrollBar ScrollBarStyle
|
||||
Knob KnobStyle
|
||||
SignalRail SignalRailStyle
|
||||
Port PortStyle
|
||||
|
||||
// iconCache is used to cache the icons created from iconvg data
|
||||
iconCache map[*byte]*widget.Icon
|
||||
|
@ -214,9 +214,12 @@ uniteditor:
|
||||
sendtarget: *secondarycolor
|
||||
width: 60
|
||||
height: 70
|
||||
rowtitle: { textsize: 12, color: *white, alignment: 2 }
|
||||
rowtitlewidth: 16
|
||||
error: *errorcolor
|
||||
unitlist:
|
||||
labelwidth: 16
|
||||
name: { textsize: 12, color: *white, alignment: 2 }
|
||||
disabled:
|
||||
{ textsize: 12, color: *disabled, font: { style: 1 }, alignment: 2 }
|
||||
error: *errorcolor
|
||||
knob:
|
||||
diameter: 36
|
||||
value: { textsize: 12, color: *highemphasis }
|
||||
@ -231,3 +234,7 @@ signalrail:
|
||||
linewidth: 2
|
||||
portdiameter: 8
|
||||
portcolor: *primarycolor
|
||||
port:
|
||||
diameter: 36
|
||||
strokewidth: 4
|
||||
color: *secondarycolor
|
||||
|
@ -31,7 +31,7 @@ type (
|
||||
UnitEditor struct {
|
||||
paramTable *ScrollTable
|
||||
searchList *DragList
|
||||
Parameters [][]*ParameterWidget
|
||||
Parameters [][]*ParameterState
|
||||
DeleteUnitBtn *Clickable
|
||||
CopyUnitBtn *Clickable
|
||||
ClearUnitBtn *Clickable
|
||||
@ -157,18 +157,18 @@ func (pe *UnitEditor) layoutRack(gtx C) D {
|
||||
// create enough parameter widget to match the number of parameters
|
||||
width := pe.paramTable.Table.Width()
|
||||
for len(pe.Parameters) < pe.paramTable.Table.Height() {
|
||||
pe.Parameters = append(pe.Parameters, make([]*ParameterWidget, 0))
|
||||
pe.Parameters = append(pe.Parameters, make([]*ParameterState, 0))
|
||||
}
|
||||
cellWidth := gtx.Dp(t.Theme.UnitEditor.Width)
|
||||
cellHeight := gtx.Dp(t.Theme.UnitEditor.Height)
|
||||
rowTitleLabelWidth := gtx.Dp(t.Theme.UnitEditor.RowTitleWidth)
|
||||
rowTitleLabelWidth := gtx.Dp(t.Theme.UnitEditor.UnitList.LabelWidth)
|
||||
rowTitleSignalWidth := gtx.Dp(t.Theme.SignalRail.SignalWidth) * t.SignalRail().MaxWidth()
|
||||
rowTitleWidth := rowTitleLabelWidth + rowTitleSignalWidth
|
||||
signalError := t.SignalRail().Error()
|
||||
columnTitleHeight := gtx.Dp(0)
|
||||
for i := range pe.Parameters {
|
||||
for len(pe.Parameters[i]) < width {
|
||||
pe.Parameters[i] = append(pe.Parameters[i], &ParameterWidget{})
|
||||
pe.Parameters[i] = append(pe.Parameters[i], &ParameterState{})
|
||||
}
|
||||
}
|
||||
coltitle := func(gtx C, x int) D {
|
||||
@ -184,9 +184,12 @@ func (pe *UnitEditor) layoutRack(gtx C) D {
|
||||
}
|
||||
|
||||
sr := SignalRail(t.Theme, t.SignalRail().Item(y))
|
||||
label := Label(t.Theme, &t.Theme.UnitEditor.RowTitle, t.Units().Item(y).Type)
|
||||
if signalError.Err != nil && signalError.UnitIndex == y {
|
||||
label.Color = t.Theme.UnitEditor.Error
|
||||
label := Label(t.Theme, &t.Theme.UnitEditor.UnitList.Name, t.Units().Item(y).Type)
|
||||
switch {
|
||||
case t.Units().Item(y).Disabled:
|
||||
label.LabelStyle = t.Theme.UnitEditor.UnitList.Disabled
|
||||
case signalError.Err != nil && signalError.UnitIndex == y:
|
||||
label.Color = t.Theme.UnitEditor.UnitList.Error
|
||||
}
|
||||
gtx.Constraints = layout.Exact(image.Pt(rowTitleWidth, cellHeight))
|
||||
sr.Layout(gtx)
|
||||
@ -210,10 +213,8 @@ func (pe *UnitEditor) layoutRack(gtx C) D {
|
||||
paint.FillShape(gtx.Ops, c, clip.Rect{Min: image.Pt(0, 0), Max: image.Pt(gtx.Constraints.Min.X, gtx.Constraints.Min.Y)}.Op())
|
||||
}
|
||||
|
||||
param := t.Model.Params().Item(tracker.Point{X: x, Y: y})
|
||||
pe.Parameters[y][x].Parameter = param
|
||||
paramStyle := t.ParamStyle(t.Theme, pe.Parameters[y][x])
|
||||
paramStyle.Focus = pe.paramTable.Table.Cursor() == tracker.Point{X: x, Y: y}
|
||||
param := t.Model.Params().Item(point)
|
||||
paramStyle := t.ParamStyle(param, t.Theme, pe.Parameters[y][x], pe.paramTable.Table.Cursor() == point)
|
||||
paramStyle.Layout(gtx)
|
||||
return D{Size: image.Pt(gtx.Constraints.Max.X, gtx.Constraints.Max.Y)}
|
||||
}
|
||||
@ -351,63 +352,61 @@ func (t *UnitEditor) Tags(level int, yield TagYieldFunc) bool {
|
||||
return yield(level, widget) && yield(level+1, &t.commentEditor.widgetEditor)
|
||||
}
|
||||
|
||||
type ParameterWidget struct {
|
||||
type ParameterState struct {
|
||||
knobState KnobState
|
||||
boolWidget widget.Bool
|
||||
instrBtn Clickable
|
||||
instrMenu MenuState
|
||||
unitBtn Clickable
|
||||
unitMenu MenuState
|
||||
Parameter tracker.Parameter
|
||||
tipArea TipArea
|
||||
clickable Clickable
|
||||
portState PortState
|
||||
}
|
||||
|
||||
type ParameterStyle struct {
|
||||
tracker *Tracker
|
||||
w *ParameterWidget
|
||||
Theme *Theme
|
||||
SendTargetTheme *material.Theme
|
||||
Focus bool
|
||||
Parameter tracker.Parameter
|
||||
State *ParameterState
|
||||
Theme *Theme
|
||||
Focus bool
|
||||
}
|
||||
|
||||
func (t *Tracker) ParamStyle(th *Theme, paramWidget *ParameterWidget) ParameterStyle {
|
||||
sendTargetTheme := th.Material.WithPalette(material.Palette{
|
||||
Bg: th.Material.Bg,
|
||||
Fg: th.UnitEditor.SendTarget,
|
||||
ContrastBg: th.Material.ContrastBg,
|
||||
ContrastFg: th.Material.ContrastFg,
|
||||
})
|
||||
func (t *Tracker) ParamStyle(Parameter tracker.Parameter, th *Theme, paramWidget *ParameterState, focus bool) ParameterStyle {
|
||||
return ParameterStyle{
|
||||
tracker: t, // TODO: we need this to pull the instrument names for ID style parameters, find out another way
|
||||
Theme: th,
|
||||
SendTargetTheme: &sendTargetTheme,
|
||||
w: paramWidget,
|
||||
Theme: th,
|
||||
State: paramWidget,
|
||||
Parameter: Parameter,
|
||||
Focus: focus,
|
||||
}
|
||||
}
|
||||
|
||||
func (p ParameterStyle) Layout(gtx C) D {
|
||||
//_, _ := p.w.Parameter.Info()
|
||||
title := Label(p.Theme, &p.Theme.UnitEditor.Name, p.w.Parameter.Name())
|
||||
title := Label(p.Theme, &p.Theme.UnitEditor.Name, p.Parameter.Name())
|
||||
t := TrackerFromContext(gtx)
|
||||
widget := func(gtx C) D {
|
||||
switch p.w.Parameter.Type() {
|
||||
if port, ok := p.Parameter.Port(); t.IsChoosingSendTarget() && ok {
|
||||
for p.State.portState.Clicked(gtx) {
|
||||
t.ChooseSendTarget(p.Parameter.UnitID(), port).Do()
|
||||
}
|
||||
k := Port(p.Theme, &p.State.portState)
|
||||
return k.Layout(gtx)
|
||||
}
|
||||
switch p.Parameter.Type() {
|
||||
case tracker.IntegerParameter:
|
||||
k := Knob(p.w.Parameter, p.Theme, &p.w.knobState, p.w.Parameter.Hint().Label, p.Focus)
|
||||
k := Knob(p.Parameter, p.Theme, &p.State.knobState, p.Parameter.Hint().Label, p.Focus)
|
||||
return k.Layout(gtx)
|
||||
case tracker.BoolParameter:
|
||||
ra := p.w.Parameter.Range()
|
||||
p.w.boolWidget.Value = p.w.Parameter.Value() > ra.Min
|
||||
boolStyle := material.Switch(&p.Theme.Material, &p.w.boolWidget, "Toggle boolean parameter")
|
||||
ra := p.Parameter.Range()
|
||||
p.State.boolWidget.Value = p.Parameter.Value() > ra.Min
|
||||
boolStyle := material.Switch(&p.Theme.Material, &p.State.boolWidget, "Toggle boolean parameter")
|
||||
boolStyle.Color.Disabled = p.Theme.Material.Fg
|
||||
defer pointer.PassOp{}.Push(gtx.Ops).Pop()
|
||||
dims := layout.Center.Layout(gtx, boolStyle.Layout)
|
||||
if p.w.boolWidget.Value {
|
||||
p.w.Parameter.SetValue(ra.Max)
|
||||
if p.State.boolWidget.Value {
|
||||
p.Parameter.SetValue(ra.Max)
|
||||
} else {
|
||||
p.w.Parameter.SetValue(ra.Min)
|
||||
p.Parameter.SetValue(ra.Min)
|
||||
}
|
||||
return dims
|
||||
case tracker.IDParameter:
|
||||
return drawCircle(gtx, gtx.Dp(p.Theme.Knob.Diameter), p.Theme.Knob.Pos.Bg)
|
||||
btn := ActionBtn(t.ChooseSendSource(p.Parameter.UnitID()), t.Theme, &p.State.clickable, "Set", p.Parameter.Hint().Label)
|
||||
return btn.Layout(gtx)
|
||||
/*instrItems := make([]ActionMenuItem, p.tracker.Instruments().Count())
|
||||
for i := range instrItems {
|
||||
i := i
|
||||
@ -449,6 +448,10 @@ func (p ParameterStyle) Layout(gtx C) D {
|
||||
}),
|
||||
)*/
|
||||
}
|
||||
if _, ok := p.Parameter.Port(); ok {
|
||||
k := Port(p.Theme, &p.State.portState)
|
||||
return k.Layout(gtx)
|
||||
}
|
||||
return D{}
|
||||
}
|
||||
title.Layout(gtx)
|
||||
|
@ -240,6 +240,7 @@ func (m *Model) change(kind string, t ChangeType, severity ChangeSeverity) func(
|
||||
m.d.UnitIndex2 = clamp(m.d.UnitIndex2, 0, unitCount-1)
|
||||
m.d.UnitSearching = false // if we change anything in the patch, reset the unit searching
|
||||
m.d.UnitSearchString = ""
|
||||
m.d.SendSource = 0
|
||||
m.updateDerivedPatchData()
|
||||
TrySend(m.broker.ToPlayer, any(m.d.Song.Patch.Copy()))
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ type (
|
||||
up *sointu.UnitParameter
|
||||
index int
|
||||
vtable parameterVtable
|
||||
port int
|
||||
}
|
||||
|
||||
parameterVtable interface {
|
||||
@ -70,6 +71,12 @@ func (p *Parameter) Value() int {
|
||||
}
|
||||
return p.vtable.Value(p)
|
||||
}
|
||||
func (p *Parameter) Port() (int, bool) {
|
||||
if p.port <= 0 {
|
||||
return 0, false
|
||||
}
|
||||
return p.port - 1, true
|
||||
}
|
||||
func (p *Parameter) SetValue(value int) bool {
|
||||
if p.vtable == nil {
|
||||
return false
|
||||
@ -123,6 +130,12 @@ func (p *Parameter) Reset() {
|
||||
}
|
||||
p.vtable.Reset(p)
|
||||
}
|
||||
func (p *Parameter) UnitID() int {
|
||||
if p.unit == nil {
|
||||
return 0
|
||||
}
|
||||
return p.unit.ID
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
@ -212,6 +225,9 @@ func (n *namedParameter) Range(p *Parameter) IntRange {
|
||||
return IntRange{Min: p.up.MinValue, Max: p.up.MaxValue}
|
||||
}
|
||||
func (n *namedParameter) Type(p *Parameter) ParameterType {
|
||||
if p.up == nil || !p.up.CanSet {
|
||||
return NoParameter
|
||||
}
|
||||
if p.unit.Type == "send" && p.up.Name == "target" {
|
||||
return IDParameter
|
||||
}
|
||||
|
Reference in New Issue
Block a user