feat: embed license in executable and add menu item to show it

This commit is contained in:
5684185+vsariola@users.noreply.github.com
2025-06-23 18:45:13 +03:00
parent 6f1db6b392
commit fb0fa4af92
10 changed files with 48 additions and 19 deletions

View File

@ -5,6 +5,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [Unreleased]
### Added
- Help menu, with a menu item to show the license ([#196][i196])
- Show CPU load percentage in the song panel ([#192][i192])
- Theme can be user configured, in theme.yml. This theme.yml should be placed in
the usual sointu config directory (i.e.
@ -337,3 +338,4 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
[i176]: https://github.com/vsariola/sointu/issues/176
[i186]: https://github.com/vsariola/sointu/issues/186
[i192]: https://github.com/vsariola/sointu/issues/192
[i196]: https://github.com/vsariola/sointu/issues/196

View File

@ -1,7 +1,8 @@
MIT License
Copyright (c) 2018 Dominik Ries
(c) 2020 Veikko Sariola
(c) 2020-2025 Veikko Sariola, moitias, qm210, LeStahl,
petersalomonsen, anticore
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -1,6 +1,7 @@
package sointu
import (
_ "embed"
"errors"
)
@ -92,6 +93,9 @@ type (
}
)
//go:embed LICENSE
var License string
func (s *Score) SongPos(songRow int) SongPos {
if s.RowsPerPattern == 0 {
return SongPos{OrderRow: 0, PatternRow: 0}

View File

@ -92,6 +92,7 @@ type (
Item MIDIDevice
*Model
}
ShowLicense Model
)
// Action methods
@ -123,6 +124,9 @@ func (a Action) Do() {
}
func (a Action) Enabled() bool {
if a.doer == nil {
return false // no doer, not allowed
}
if a.enabler == nil {
return true // no enabler, always allowed
}
@ -588,6 +592,9 @@ func (m *ExportFloat) Do() { m.dialog = ExportFloatExplorer }
func (m *Model) ExportInt16() Action { return MakeEnabledAction((*ExportInt16)(m)) }
func (m *ExportInt16) Do() { m.dialog = ExportInt16Explorer }
func (m *Model) ShowLicense() Action { return MakeEnabledAction((*ShowLicense)(m)) }
func (m *ShowLicense) Do() { m.dialog = License }
func (m *Model) SelectMidiInput(item MIDIDevice) Action {
return MakeEnabledAction(SelectMidiInput{Item: item, Model: m})
}

View File

@ -92,13 +92,15 @@ func (d *Dialog) handleKeys(gtx C) {
for d.BtnCancel.Clicked(gtx) {
d.cancel.Do()
}
if d.alt.Enabled() {
if d.alt.Enabled() && d.cancel.Enabled() {
d.handleKeysForButton(gtx, &d.BtnAlt, &d.BtnCancel, &d.BtnOk)
d.handleKeysForButton(gtx, &d.BtnCancel, &d.BtnOk, &d.BtnAlt)
d.handleKeysForButton(gtx, &d.BtnOk, &d.BtnAlt, &d.BtnCancel)
} else {
} else if d.ok.Enabled() {
d.handleKeysForButton(gtx, &d.BtnOk, &d.BtnCancel, &d.BtnCancel)
d.handleKeysForButton(gtx, &d.BtnCancel, &d.BtnOk, &d.BtnOk)
} else {
d.handleKeysForButton(gtx, &d.BtnCancel, &d.BtnCancel, &d.BtnCancel)
}
}
@ -117,18 +119,17 @@ func (d *DialogStyle) Layout(gtx C) D {
layout.Rigid(Label(d.Theme, &d.Theme.Dialog.Text, d.Text).Layout),
layout.Rigid(func(gtx C) D {
return layout.E.Layout(gtx, func(gtx C) D {
gtx.Constraints.Min.X = gtx.Dp(unit.Dp(120))
if d.dialog.alt.Enabled() {
return layout.Flex{Axis: layout.Horizontal, Spacing: layout.SpaceBetween}.Layout(gtx,
layout.Rigid(d.OkStyle.Layout),
layout.Rigid(d.AltStyle.Layout),
layout.Rigid(d.CancelStyle.Layout),
)
fl := layout.Flex{Axis: layout.Horizontal, Spacing: layout.SpaceBetween}
ok := layout.Rigid(d.OkStyle.Layout)
alt := layout.Rigid(d.AltStyle.Layout)
cancel := layout.Rigid(d.CancelStyle.Layout)
if d.dialog.alt.Enabled() && d.dialog.cancel.Enabled() {
return fl.Layout(gtx, ok, alt, cancel)
}
return layout.Flex{Axis: layout.Horizontal, Spacing: layout.SpaceBetween}.Layout(gtx,
layout.Rigid(d.OkStyle.Layout),
layout.Rigid(d.CancelStyle.Layout),
)
if d.dialog.ok.Enabled() {
return fl.Layout(gtx, ok, cancel)
}
return fl.Layout(gtx, cancel)
})
}),
)

View File

@ -185,6 +185,8 @@ func (t *Tracker) KeyEvent(e key.Event, gtx C) {
t.SplitTrack().Do()
case "SplitInstrument":
t.SplitInstrument().Do()
case "ShowLicense":
t.ShowLicense().Do()
// Booleans
case "PanicToggle":
t.Panic().Toggle()

View File

@ -32,7 +32,6 @@ func (l LabelWidget) Layout(gtx C) D {
textColor := textColorMacro.Stop()
t := widget.Label{
Alignment: l.Alignment,
MaxLines: 1,
}
if l.ShadowColor.A > 0 {
shadowColorMacro := op.Record(gtx.Ops)

View File

@ -307,6 +307,7 @@ type MenuBar struct {
fileMenuItems []MenuItem
editMenuItems []MenuItem
midiMenuItems []MenuItem
helpMenuItems []MenuItem
panicHint string
PanicBtn *Clickable
@ -314,8 +315,8 @@ type MenuBar struct {
func NewMenuBar(model *tracker.Model) *MenuBar {
ret := &MenuBar{
Clickables: make([]Clickable, 3),
Menus: make([]Menu, 3),
Clickables: make([]Clickable, 4),
Menus: make([]Menu, 4),
PanicBtn: new(Clickable),
panicHint: makeHint("Panic", " (%s)", "PanicToggle"),
}
@ -341,6 +342,9 @@ func NewMenuBar(model *tracker.Model) *MenuBar {
Doer: model.SelectMidiInput(input),
})
}
ret.helpMenuItems = []MenuItem{
{IconBytes: icons.ActionCopyright, Text: "License", ShortcutText: keyActionMap["ShowLicense"], Doer: model.ShowLicense()},
}
return ret
}
@ -356,11 +360,12 @@ func (t *MenuBar) Layout(gtx C, tr *Tracker) D {
fileFC := layout.Rigid(tr.layoutMenu(gtx, "File", &t.Clickables[0], &t.Menus[0], unit.Dp(200), t.fileMenuItems...))
editFC := layout.Rigid(tr.layoutMenu(gtx, "Edit", &t.Clickables[1], &t.Menus[1], unit.Dp(200), t.editMenuItems...))
midiFC := layout.Rigid(tr.layoutMenu(gtx, "MIDI", &t.Clickables[2], &t.Menus[2], unit.Dp(200), t.midiMenuItems...))
helpFC := layout.Rigid(tr.layoutMenu(gtx, "?", &t.Clickables[3], &t.Menus[3], unit.Dp(200), t.helpMenuItems...))
panicFC := layout.Flexed(1, func(gtx C) D { return layout.E.Layout(gtx, panicBtn.Layout) })
if len(t.midiMenuItems) > 0 {
return flex.Layout(gtx, fileFC, editFC, midiFC, panicFC)
return flex.Layout(gtx, fileFC, editFC, midiFC, helpFC, panicFC)
}
return flex.Layout(gtx, fileFC, editFC, panicFC)
return flex.Layout(gtx, fileFC, editFC, helpFC, panicFC)
}
type PlayBar struct {

View File

@ -20,6 +20,7 @@ import (
"gioui.org/op/paint"
"gioui.org/text"
"gioui.org/x/explorer"
"github.com/vsariola/sointu"
"github.com/vsariola/sointu/tracker"
)
@ -39,6 +40,7 @@ type (
SaveChangesDialog *Dialog
WaveTypeDialog *Dialog
LicenseDialog *Dialog
ModalDialog layout.Widget
InstrumentEditor *InstrumentEditor
@ -79,6 +81,7 @@ func NewTracker(model *tracker.Model) *Tracker {
SaveChangesDialog: NewDialog(model.SaveSong(), model.DiscardSong(), model.Cancel()),
WaveTypeDialog: NewDialog(model.ExportInt16(), model.ExportFloat(), model.Cancel()),
LicenseDialog: NewDialog(tracker.MakeAction(nil), tracker.MakeAction(nil), model.Cancel()),
InstrumentEditor: NewInstrumentEditor(model),
OrderEditor: NewOrderEditor(model),
TrackEditor: NewNoteEditor(model),
@ -291,6 +294,10 @@ func (t *Tracker) showDialog(gtx C) {
t.explorerCreateFile(func(wc io.WriteCloser) {
t.WriteWav(wc, t.Dialog() == tracker.ExportInt16Explorer)
}, filename)
case tracker.License:
dstyle := ConfirmDialog(gtx, t.Theme, t.LicenseDialog, "License", sointu.License)
dstyle.CancelStyle.Text = "Close"
dstyle.Layout(gtx)
}
}

View File

@ -155,6 +155,7 @@ const (
ExportInt16Explorer
QuitChanges
QuitSaveExplorer
License
)
const maxUndo = 64