mirror of
				https://github.com/vsariola/sointu.git
				synced 2025-10-30 23:45:53 -04:00 
			
		
		
		
	| @ -33,11 +33,6 @@ func (t *Tracker) layoutInstruments(gtx C) D { | ||||
| 	pointer.InputOp{Tag: &instrumentPointerTag, | ||||
| 		Types: pointer.Press, | ||||
| 	}.Add(gtx.Ops) | ||||
| 	if t.CurrentInstrument > 7 { | ||||
| 		t.InstrumentDragList.List.Position.First = t.CurrentInstrument - 7 | ||||
| 	} else { | ||||
| 		t.InstrumentDragList.List.Position.First = 0 | ||||
| 	} | ||||
| 	for t.NewInstrumentBtn.Clicked() { | ||||
| 		t.AddInstrument() | ||||
| 	} | ||||
| @ -53,7 +48,14 @@ func (t *Tracker) layoutInstruments(gtx C) D { | ||||
| 		layout.Rigid(func(gtx C) D { | ||||
| 			return layout.Flex{}.Layout( | ||||
| 				gtx, | ||||
| 				layout.Flexed(1, t.layoutInstrumentNames), | ||||
| 				layout.Flexed(1, func(gtx C) D { | ||||
| 					return layout.Stack{}.Layout(gtx, | ||||
| 						layout.Stacked(t.layoutInstrumentNames), | ||||
| 						layout.Expanded(func(gtx C) D { | ||||
| 							return t.InstrumentScrollBar.Layout(gtx, unit.Dp(6), len(t.song.Patch.Instruments), &t.InstrumentDragList.List.Position) | ||||
| 						}), | ||||
| 					) | ||||
| 				}), | ||||
| 				layout.Rigid(func(gtx C) D { | ||||
| 					return layout.E.Layout(gtx, btnStyle.Layout) | ||||
| 				}), | ||||
| @ -180,7 +182,6 @@ func (t *Tracker) layoutInstrumentEditor(gtx C) D { | ||||
| 	} | ||||
| 	addUnitBtnStyle := material.IconButton(t.Theme, t.AddUnitBtn, widgetForIcon(icons.ContentAdd)) | ||||
| 	addUnitBtnStyle.Inset = layout.UniformInset(unit.Dp(4)) | ||||
| 	margin := layout.UniformInset(unit.Dp(2)) | ||||
|  | ||||
| 	for len(t.StackUse) < len(t.song.Patch.Instruments[t.CurrentInstrument].Units) { | ||||
| 		t.StackUse = append(t.StackUse, 0) | ||||
| @ -212,10 +213,12 @@ func (t *Tracker) layoutInstrumentEditor(gtx C) D { | ||||
| 			} | ||||
| 		} | ||||
| 		stackLabel := LabelStyle{Text: stackText, ShadeColor: black, Color: mediumEmphasisTextColor, Font: labelDefaultFont, FontSize: unit.Sp(12)} | ||||
|  | ||||
| 		rightMargin := layout.Inset{Right: unit.Dp(10)} | ||||
| 		return layout.Flex{Axis: layout.Horizontal}.Layout(gtx, | ||||
| 			layout.Flexed(1, unitNameLabel.Layout), | ||||
| 			layout.Rigid(stackLabel.Layout), | ||||
| 			layout.Rigid(func(gtx C) D { | ||||
| 				return rightMargin.Layout(gtx, stackLabel.Layout) | ||||
| 			}), | ||||
| 		) | ||||
| 	} | ||||
|  | ||||
| @ -239,7 +242,11 @@ func (t *Tracker) layoutInstrumentEditor(gtx C) D { | ||||
| 						} | ||||
| 						return dims | ||||
| 					}), | ||||
| 					layout.Expanded(func(gtx C) D { | ||||
| 						return t.UnitScrollBar.Layout(gtx, unit.Dp(10), len(t.song.Patch.Instruments[t.CurrentInstrument].Units), &t.UnitDragList.List.Position) | ||||
| 					}), | ||||
| 					layout.Stacked(func(gtx C) D { | ||||
| 						margin := layout.Inset{Right: unit.Dp(20), Bottom: unit.Dp(1)} | ||||
| 						return margin.Layout(gtx, addUnitBtnStyle.Layout) | ||||
| 					})) | ||||
| 			}), | ||||
|  | ||||
							
								
								
									
										128
									
								
								tracker/scrollbar.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								tracker/scrollbar.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,128 @@ | ||||
| package tracker | ||||
|  | ||||
| import ( | ||||
| 	"image" | ||||
|  | ||||
| 	"gioui.org/f32" | ||||
| 	"gioui.org/io/pointer" | ||||
| 	"gioui.org/layout" | ||||
| 	"gioui.org/op" | ||||
| 	"gioui.org/op/clip" | ||||
| 	"gioui.org/op/paint" | ||||
| 	"gioui.org/unit" | ||||
| ) | ||||
|  | ||||
| type ScrollBar struct { | ||||
| 	Axis      layout.Axis | ||||
| 	dragStart float32 | ||||
| 	hovering  bool | ||||
| 	dragging  bool | ||||
| } | ||||
|  | ||||
| func (s *ScrollBar) Layout(gtx C, width unit.Value, numItems int, pos *layout.Position) D { | ||||
| 	defer op.Save(gtx.Ops).Load() | ||||
| 	clip.Rect{Max: gtx.Constraints.Min}.Add(gtx.Ops) | ||||
| 	gradientSize := gtx.Px(unit.Dp(4)) | ||||
| 	var totalPixelsEstimate, scrollBarRelLength float32 | ||||
| 	switch s.Axis { | ||||
| 	case layout.Vertical: | ||||
| 		if pos.First > 0 || pos.Offset > 0 { | ||||
| 			paint.LinearGradientOp{Color1: black, Color2: transparent, Stop2: f32.Pt(0, float32(gradientSize))}.Add(gtx.Ops) | ||||
| 			paint.PaintOp{}.Add(gtx.Ops) | ||||
| 		} | ||||
| 		if pos.BeforeEnd { | ||||
| 			paint.LinearGradientOp{Color1: black, Color2: transparent, Stop1: f32.Pt(0, float32(gtx.Constraints.Min.Y)), Stop2: f32.Pt(0, float32(gtx.Constraints.Min.Y-gradientSize))}.Add(gtx.Ops) | ||||
| 			paint.PaintOp{}.Add(gtx.Ops) | ||||
| 		} | ||||
| 		totalPixelsEstimate = float32(gtx.Constraints.Min.Y+pos.Offset-pos.OffsetLast) * float32(numItems) / float32(pos.Count) | ||||
| 		scrollBarRelLength = float32(gtx.Constraints.Min.Y) / float32(totalPixelsEstimate) | ||||
|  | ||||
| 	case layout.Horizontal: | ||||
| 		if pos.First > 0 || pos.Offset > 0 { | ||||
| 			paint.LinearGradientOp{Color1: black, Color2: transparent, Stop2: f32.Pt(float32(gradientSize), 0)}.Add(gtx.Ops) | ||||
| 			paint.PaintOp{}.Add(gtx.Ops) | ||||
| 		} | ||||
| 		if pos.BeforeEnd { | ||||
| 			paint.LinearGradientOp{Color1: black, Color2: transparent, Stop1: f32.Pt(float32(gtx.Constraints.Min.X), 0), Stop2: f32.Pt(float32(gtx.Constraints.Min.X-gradientSize), 0)}.Add(gtx.Ops) | ||||
| 			paint.PaintOp{}.Add(gtx.Ops) | ||||
| 		} | ||||
| 		totalPixelsEstimate = float32(gtx.Constraints.Min.X+pos.Offset-pos.OffsetLast) * float32(numItems) / float32(pos.Count) | ||||
| 		scrollBarRelLength = float32(gtx.Constraints.Min.X) / float32(totalPixelsEstimate) | ||||
| 	} | ||||
|  | ||||
| 	scrollBarRelStart := (float32(pos.First)*totalPixelsEstimate/float32(numItems) + float32(pos.Offset)) / totalPixelsEstimate | ||||
| 	scrWidth := gtx.Px(width) | ||||
|  | ||||
| 	stack := op.Save(gtx.Ops) | ||||
| 	switch s.Axis { | ||||
| 	case layout.Vertical: | ||||
| 		if scrollBarRelLength < 1 && (s.dragging || s.hovering) { | ||||
| 			y1 := int(scrollBarRelStart * float32(gtx.Constraints.Min.Y)) | ||||
| 			y2 := int((scrollBarRelStart + scrollBarRelLength) * float32(gtx.Constraints.Min.Y)) | ||||
| 			paint.FillShape(gtx.Ops, scrollBarColor, clip.Rect{Min: image.Pt(gtx.Constraints.Min.X-scrWidth, y1), Max: image.Pt(gtx.Constraints.Min.X, y2)}.Op()) | ||||
| 		} | ||||
| 		rect := image.Rect(gtx.Constraints.Min.X-scrWidth, 0, gtx.Constraints.Min.X, gtx.Constraints.Min.Y) | ||||
| 		pointer.Rect(rect).Add(gtx.Ops) | ||||
| 	case layout.Horizontal: | ||||
| 		if scrollBarRelLength < 1 && (s.dragging || s.hovering) { | ||||
| 			x1 := int(scrollBarRelStart * float32(gtx.Constraints.Min.X)) | ||||
| 			x2 := int((scrollBarRelStart + scrollBarRelLength) * float32(gtx.Constraints.Min.X)) | ||||
| 			paint.FillShape(gtx.Ops, scrollBarColor, clip.Rect{Min: image.Pt(x1, gtx.Constraints.Min.Y-scrWidth), Max: image.Pt(x2, gtx.Constraints.Min.Y)}.Op()) | ||||
| 		} | ||||
| 		rect := image.Rect(0, gtx.Constraints.Min.Y-scrWidth, gtx.Constraints.Min.X, gtx.Constraints.Min.Y) | ||||
| 		pointer.Rect(rect).Add(gtx.Ops) | ||||
| 	} | ||||
| 	pointer.InputOp{Tag: &s.dragStart, | ||||
| 		Types: pointer.Drag | pointer.Press | pointer.Cancel | pointer.Release, | ||||
| 	}.Add(gtx.Ops) | ||||
| 	stack.Load() | ||||
|  | ||||
| 	for _, ev := range gtx.Events(&s.dragStart) { | ||||
| 		e, ok := ev.(pointer.Event) | ||||
| 		if !ok { | ||||
| 			continue | ||||
| 		} | ||||
| 		switch e.Type { | ||||
| 		case pointer.Press: | ||||
| 			if s.Axis == layout.Horizontal { | ||||
| 				s.dragStart = e.Position.X | ||||
| 				s.dragging = true | ||||
| 			} else { | ||||
| 				s.dragStart = e.Position.Y | ||||
| 				s.dragging = true | ||||
| 			} | ||||
| 		case pointer.Drag: | ||||
| 			if s.Axis == layout.Horizontal { | ||||
| 				pos.Offset += int(e.Position.X - s.dragStart + 0.5) | ||||
| 				s.dragStart = e.Position.X | ||||
| 			} else { | ||||
| 				pos.Offset += int(e.Position.Y - s.dragStart + 0.5) | ||||
| 				s.dragStart = e.Position.Y | ||||
| 			} | ||||
| 		case pointer.Release, pointer.Cancel: | ||||
| 			s.dragging = false | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	pointer.PassOp{Pass: true}.Add(gtx.Ops) | ||||
| 	rect := image.Rect(0, 0, gtx.Constraints.Min.X, gtx.Constraints.Min.Y) | ||||
| 	pointer.Rect(rect).Add(gtx.Ops) | ||||
| 	pointer.InputOp{Tag: s, | ||||
| 		Types: pointer.Enter | pointer.Leave, | ||||
| 	}.Add(gtx.Ops) | ||||
|  | ||||
| 	for _, ev := range gtx.Events(s) { | ||||
| 		e, ok := ev.(pointer.Event) | ||||
| 		if !ok { | ||||
| 			continue | ||||
| 		} | ||||
| 		switch e.Type { | ||||
| 		case pointer.Enter: | ||||
| 			s.hovering = true | ||||
| 		case pointer.Leave: | ||||
| 			s.hovering = false | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return D{Size: gtx.Constraints.Min} | ||||
| } | ||||
| @ -69,3 +69,5 @@ var inactiveSelectionColor = color.NRGBA{R: 140, G: 140, B: 140, A: 16} | ||||
| var errorColor = color.NRGBA{R: 207, G: 102, B: 121, A: 255} | ||||
|  | ||||
| var menuHoverColor = color.NRGBA{R: 30, G: 31, B: 38, A: 255} | ||||
|  | ||||
| var scrollBarColor = color.NRGBA{R: 255, G: 255, B: 255, A: 32} | ||||
|  | ||||
| @ -63,14 +63,18 @@ type Tracker struct { | ||||
| 	CopyInstrumentBtn     *widget.Clickable | ||||
| 	ParameterSliders      []*widget.Float | ||||
| 	ParameterList         *layout.List | ||||
| 	ParameterScrollBar    *ScrollBar | ||||
| 	UnitDragList          *DragList | ||||
| 	UnitScrollBar         *ScrollBar | ||||
| 	DeleteUnitBtn         *widget.Clickable | ||||
| 	ClearUnitBtn          *widget.Clickable | ||||
| 	ChooseUnitTypeList    *layout.List | ||||
| 	ChooseUnitScrollBar   *ScrollBar | ||||
| 	ChooseUnitTypeBtns    []*widget.Clickable | ||||
| 	AddUnitBtn            *widget.Clickable | ||||
| 	ParameterLabelBtns    []*widget.Clickable | ||||
| 	InstrumentDragList    *DragList | ||||
| 	InstrumentScrollBar   *ScrollBar | ||||
| 	TrackHexCheckBox      *widget.Bool | ||||
| 	TrackShowHex          []bool | ||||
| 	VuMeter               VuMeter | ||||
| @ -702,15 +706,19 @@ func New(audioContext sointu.AudioContext, synthService sointu.SynthService) *Tr | ||||
| 		Menus:                 make([]Menu, 2), | ||||
| 		MenuBar:               make([]widget.Clickable, 2), | ||||
| 		UnitDragList:          &DragList{List: &layout.List{Axis: layout.Vertical}}, | ||||
| 		UnitScrollBar:         &ScrollBar{Axis: layout.Vertical}, | ||||
| 		refresh:               make(chan struct{}, 1), // use non-blocking sends; no need to queue extra ticks if one is queued already | ||||
| 		undoStack:             []sointu.Song{}, | ||||
| 		redoStack:             []sointu.Song{}, | ||||
| 		InstrumentDragList:    &DragList{List: &layout.List{Axis: layout.Horizontal}}, | ||||
| 		InstrumentScrollBar:   &ScrollBar{Axis: layout.Horizontal}, | ||||
| 		ParameterList:         &layout.List{Axis: layout.Vertical}, | ||||
| 		ParameterScrollBar:    &ScrollBar{Axis: layout.Vertical}, | ||||
| 		TopHorizontalSplit:    new(Split), | ||||
| 		BottomHorizontalSplit: new(Split), | ||||
| 		VerticalSplit:         new(Split), | ||||
| 		ChooseUnitTypeList:    &layout.List{Axis: layout.Vertical}, | ||||
| 		ChooseUnitScrollBar:   &ScrollBar{Axis: layout.Vertical}, | ||||
| 		KeyPlaying:            make(map[string]func()), | ||||
| 	} | ||||
| 	t.UnitDragList.HoverItem = -1 | ||||
|  | ||||
| @ -150,7 +150,14 @@ func (t *Tracker) layoutUnitSliders(gtx C) D { | ||||
| 	if u.Type == "oscillator" && u.Parameters["type"] == sointu.Sample { | ||||
| 		l++ | ||||
| 	} | ||||
| 	return t.ParameterList.Layout(gtx, l, listElements) | ||||
| 	return layout.Stack{}.Layout(gtx, | ||||
| 		layout.Stacked(func(gtx C) D { | ||||
| 			return t.ParameterList.Layout(gtx, l, listElements) | ||||
| 		}), | ||||
| 		layout.Stacked(func(gtx C) D { | ||||
| 			gtx.Constraints.Min = gtx.Constraints.Max | ||||
| 			return t.ParameterScrollBar.Layout(gtx, unit.Dp(10), l, &t.ParameterList.Position) | ||||
| 		})) | ||||
| } | ||||
|  | ||||
| func (t *Tracker) layoutUnitFooter() layout.Widget { | ||||
| @ -173,17 +180,19 @@ func (t *Tracker) layoutUnitFooter() layout.Widget { | ||||
| 		} | ||||
| 		if t.song.Patch.Instruments[t.CurrentInstrument].Units[t.CurrentUnit].Type == "" { | ||||
| 			return layout.Flex{Axis: layout.Horizontal}.Layout(gtx, | ||||
| 				layout.Rigid(deleteUnitBtnStyle.Layout), | ||||
| 				layout.Flexed(1, func(gtx C) D { return layout.Dimensions{Size: gtx.Constraints.Min} }), | ||||
| 				layout.Rigid(deleteUnitBtnStyle.Layout)) | ||||
| 			) | ||||
| 		} | ||||
| 		clearUnitBtnStyle := material.IconButton(t.Theme, t.ClearUnitBtn, widgetForIcon(icons.ContentClear)) | ||||
| 		clearUnitBtnStyle.Color = primaryColor | ||||
| 		clearUnitBtnStyle.Background = transparent | ||||
| 		clearUnitBtnStyle.Inset = layout.UniformInset(unit.Dp(6)) | ||||
| 		return layout.Flex{Axis: layout.Horizontal}.Layout(gtx, | ||||
| 			layout.Flexed(1, func(gtx C) D { return layout.Dimensions{Size: gtx.Constraints.Min} }), | ||||
| 			layout.Rigid(deleteUnitBtnStyle.Layout), | ||||
| 			layout.Rigid(clearUnitBtnStyle.Layout), | ||||
| 			layout.Rigid(deleteUnitBtnStyle.Layout)) | ||||
| 			layout.Flexed(1, func(gtx C) D { return layout.Dimensions{Size: gtx.Constraints.Min} }), | ||||
| 		) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -213,7 +222,13 @@ func (t *Tracker) layoutUnitTypeChooser(gtx C) D { | ||||
| 		return layout.Flex{Axis: layout.Vertical}.Layout(gtx, | ||||
| 			layout.Rigid(hintText), | ||||
| 			layout.Flexed(1, func(gtx C) D { | ||||
| 				return t.ChooseUnitTypeList.Layout(gtx, len(allUnits), listElem) | ||||
| 				return layout.Stack{}.Layout(gtx, | ||||
| 					layout.Stacked(func(gtx C) D { | ||||
| 						return t.ChooseUnitTypeList.Layout(gtx, len(allUnits), listElem) | ||||
| 					}), | ||||
| 					layout.Expanded(func(gtx C) D { | ||||
| 						return t.ChooseUnitScrollBar.Layout(gtx, unit.Dp(10), len(allUnits), &t.ChooseUnitTypeList.Position) | ||||
| 					})) | ||||
| 			})) | ||||
| 	}) | ||||
| } | ||||
|  | ||||
		Reference in New Issue
	
	Block a user