From fb0fa4af92d11ce66ddaba27b8ab0544e1c1f775 Mon Sep 17 00:00:00 2001 From: "5684185+vsariola@users.noreply.github.com" <5684185+vsariola@users.noreply.github.com> Date: Mon, 23 Jun 2025 18:45:13 +0300 Subject: [PATCH] feat: embed license in executable and add menu item to show it --- CHANGELOG.md | 2 ++ LICENSE | 3 ++- song.go | 4 ++++ tracker/action.go | 7 +++++++ tracker/gioui/dialog.go | 27 ++++++++++++++------------- tracker/gioui/keybindings.go | 2 ++ tracker/gioui/label.go | 1 - tracker/gioui/songpanel.go | 13 +++++++++---- tracker/gioui/tracker.go | 7 +++++++ tracker/model.go | 1 + 10 files changed, 48 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 34cdfbf..66b8ac1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/LICENSE b/LICENSE index 0be3a8b..a504e5c 100644 --- a/LICENSE +++ b/LICENSE @@ -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 diff --git a/song.go b/song.go index 11b3cdd..2bf81da 100644 --- a/song.go +++ b/song.go @@ -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} diff --git a/tracker/action.go b/tracker/action.go index 4a55e6e..6faf6a7 100644 --- a/tracker/action.go +++ b/tracker/action.go @@ -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}) } diff --git a/tracker/gioui/dialog.go b/tracker/gioui/dialog.go index d012fca..70ed777 100644 --- a/tracker/gioui/dialog.go +++ b/tracker/gioui/dialog.go @@ -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) }) }), ) diff --git a/tracker/gioui/keybindings.go b/tracker/gioui/keybindings.go index 5e6e93a..da32728 100644 --- a/tracker/gioui/keybindings.go +++ b/tracker/gioui/keybindings.go @@ -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() diff --git a/tracker/gioui/label.go b/tracker/gioui/label.go index fa97e22..73cd332 100644 --- a/tracker/gioui/label.go +++ b/tracker/gioui/label.go @@ -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) diff --git a/tracker/gioui/songpanel.go b/tracker/gioui/songpanel.go index 9f79b9b..9de5b45 100644 --- a/tracker/gioui/songpanel.go +++ b/tracker/gioui/songpanel.go @@ -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 { diff --git a/tracker/gioui/tracker.go b/tracker/gioui/tracker.go index 28065ee..da1bc12 100644 --- a/tracker/gioui/tracker.go +++ b/tracker/gioui/tracker.go @@ -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) } } diff --git a/tracker/model.go b/tracker/model.go index 9e34336..0f8d493 100644 --- a/tracker/model.go +++ b/tracker/model.go @@ -155,6 +155,7 @@ const ( ExportInt16Explorer QuitChanges QuitSaveExplorer + License ) const maxUndo = 64