mirror of
https://github.com/vsariola/sointu.git
synced 2025-07-20 05:54:34 -04:00
feat(tracker/gioui): "Ask Help", "Report Bug" and "Manual" menuitems
This commit is contained in:
parent
fb0fa4af92
commit
5f43bc3067
@ -5,7 +5,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
### Added
|
### Added
|
||||||
- Help menu, with a menu item to show the license ([#196][i196])
|
- Help menu, with a menu item to show the license in a dialog, and also menu
|
||||||
|
items to open manual, Github Discussions & Github Issues in a browser
|
||||||
|
([#196][i196])
|
||||||
- Show CPU load percentage in the song panel ([#192][i192])
|
- 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
|
- Theme can be user configured, in theme.yml. This theme.yml should be placed in
|
||||||
the usual sointu config directory (i.e.
|
the usual sointu config directory (i.e.
|
||||||
|
@ -185,6 +185,12 @@ func (t *Tracker) KeyEvent(e key.Event, gtx C) {
|
|||||||
t.SplitTrack().Do()
|
t.SplitTrack().Do()
|
||||||
case "SplitInstrument":
|
case "SplitInstrument":
|
||||||
t.SplitInstrument().Do()
|
t.SplitInstrument().Do()
|
||||||
|
case "ShowManual":
|
||||||
|
t.ShowManual().Do()
|
||||||
|
case "AskHelp":
|
||||||
|
t.AskHelp().Do()
|
||||||
|
case "ReportBug":
|
||||||
|
t.ReportBug().Do()
|
||||||
case "ShowLicense":
|
case "ShowLicense":
|
||||||
t.ShowLicense().Do()
|
t.ShowLicense().Do()
|
||||||
// Booleans
|
// Booleans
|
||||||
|
@ -37,15 +37,15 @@ type SongPanel struct {
|
|||||||
PlayBar *PlayBar
|
PlayBar *PlayBar
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSongPanel(model *tracker.Model) *SongPanel {
|
func NewSongPanel(tr *Tracker) *SongPanel {
|
||||||
ret := &SongPanel{
|
ret := &SongPanel{
|
||||||
BPM: NewNumericUpDownState(),
|
BPM: NewNumericUpDownState(),
|
||||||
RowsPerPattern: NewNumericUpDownState(),
|
RowsPerPattern: NewNumericUpDownState(),
|
||||||
RowsPerBeat: NewNumericUpDownState(),
|
RowsPerBeat: NewNumericUpDownState(),
|
||||||
Step: NewNumericUpDownState(),
|
Step: NewNumericUpDownState(),
|
||||||
SongLength: NewNumericUpDownState(),
|
SongLength: NewNumericUpDownState(),
|
||||||
Scope: NewOscilloscope(model),
|
Scope: NewOscilloscope(tr.Model),
|
||||||
MenuBar: NewMenuBar(model),
|
MenuBar: NewMenuBar(tr),
|
||||||
PlayBar: NewPlayBar(),
|
PlayBar: NewPlayBar(),
|
||||||
|
|
||||||
WeightingTypeBtn: new(Clickable),
|
WeightingTypeBtn: new(Clickable),
|
||||||
@ -313,7 +313,7 @@ type MenuBar struct {
|
|||||||
PanicBtn *Clickable
|
PanicBtn *Clickable
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMenuBar(model *tracker.Model) *MenuBar {
|
func NewMenuBar(tr *Tracker) *MenuBar {
|
||||||
ret := &MenuBar{
|
ret := &MenuBar{
|
||||||
Clickables: make([]Clickable, 4),
|
Clickables: make([]Clickable, 4),
|
||||||
Menus: make([]Menu, 4),
|
Menus: make([]Menu, 4),
|
||||||
@ -321,29 +321,32 @@ func NewMenuBar(model *tracker.Model) *MenuBar {
|
|||||||
panicHint: makeHint("Panic", " (%s)", "PanicToggle"),
|
panicHint: makeHint("Panic", " (%s)", "PanicToggle"),
|
||||||
}
|
}
|
||||||
ret.fileMenuItems = []MenuItem{
|
ret.fileMenuItems = []MenuItem{
|
||||||
{IconBytes: icons.ContentClear, Text: "New Song", ShortcutText: keyActionMap["NewSong"], Doer: model.NewSong()},
|
{IconBytes: icons.ContentClear, Text: "New Song", ShortcutText: keyActionMap["NewSong"], Doer: tr.NewSong()},
|
||||||
{IconBytes: icons.FileFolder, Text: "Open Song", ShortcutText: keyActionMap["OpenSong"], Doer: model.OpenSong()},
|
{IconBytes: icons.FileFolder, Text: "Open Song", ShortcutText: keyActionMap["OpenSong"], Doer: tr.OpenSong()},
|
||||||
{IconBytes: icons.ContentSave, Text: "Save Song", ShortcutText: keyActionMap["SaveSong"], Doer: model.SaveSong()},
|
{IconBytes: icons.ContentSave, Text: "Save Song", ShortcutText: keyActionMap["SaveSong"], Doer: tr.SaveSong()},
|
||||||
{IconBytes: icons.ContentSave, Text: "Save Song As...", ShortcutText: keyActionMap["SaveSongAs"], Doer: model.SaveSongAs()},
|
{IconBytes: icons.ContentSave, Text: "Save Song As...", ShortcutText: keyActionMap["SaveSongAs"], Doer: tr.SaveSongAs()},
|
||||||
{IconBytes: icons.ImageAudiotrack, Text: "Export Wav...", ShortcutText: keyActionMap["ExportWav"], Doer: model.Export()},
|
{IconBytes: icons.ImageAudiotrack, Text: "Export Wav...", ShortcutText: keyActionMap["ExportWav"], Doer: tr.Export()},
|
||||||
}
|
}
|
||||||
if canQuit {
|
if canQuit {
|
||||||
ret.fileMenuItems = append(ret.fileMenuItems, MenuItem{IconBytes: icons.ActionExitToApp, Text: "Quit", ShortcutText: keyActionMap["Quit"], Doer: model.RequestQuit()})
|
ret.fileMenuItems = append(ret.fileMenuItems, MenuItem{IconBytes: icons.ActionExitToApp, Text: "Quit", ShortcutText: keyActionMap["Quit"], Doer: tr.RequestQuit()})
|
||||||
}
|
}
|
||||||
ret.editMenuItems = []MenuItem{
|
ret.editMenuItems = []MenuItem{
|
||||||
{IconBytes: icons.ContentUndo, Text: "Undo", ShortcutText: keyActionMap["Undo"], Doer: model.Undo()},
|
{IconBytes: icons.ContentUndo, Text: "Undo", ShortcutText: keyActionMap["Undo"], Doer: tr.Undo()},
|
||||||
{IconBytes: icons.ContentRedo, Text: "Redo", ShortcutText: keyActionMap["Redo"], Doer: model.Redo()},
|
{IconBytes: icons.ContentRedo, Text: "Redo", ShortcutText: keyActionMap["Redo"], Doer: tr.Redo()},
|
||||||
{IconBytes: icons.ImageCrop, Text: "Remove unused data", ShortcutText: keyActionMap["RemoveUnused"], Doer: model.RemoveUnused()},
|
{IconBytes: icons.ImageCrop, Text: "Remove unused data", ShortcutText: keyActionMap["RemoveUnused"], Doer: tr.RemoveUnused()},
|
||||||
}
|
}
|
||||||
for input := range model.MIDI.InputDevices {
|
for input := range tr.MIDI.InputDevices {
|
||||||
ret.midiMenuItems = append(ret.midiMenuItems, MenuItem{
|
ret.midiMenuItems = append(ret.midiMenuItems, MenuItem{
|
||||||
IconBytes: icons.ImageControlPoint,
|
IconBytes: icons.ImageControlPoint,
|
||||||
Text: input.String(),
|
Text: input.String(),
|
||||||
Doer: model.SelectMidiInput(input),
|
Doer: tr.SelectMidiInput(input),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
ret.helpMenuItems = []MenuItem{
|
ret.helpMenuItems = []MenuItem{
|
||||||
{IconBytes: icons.ActionCopyright, Text: "License", ShortcutText: keyActionMap["ShowLicense"], Doer: model.ShowLicense()},
|
{IconBytes: icons.AVLibraryBooks, Text: "Manual", ShortcutText: keyActionMap["ShowManual"], Doer: tr.ShowManual()},
|
||||||
|
{IconBytes: icons.ActionHelp, Text: "Ask help", ShortcutText: keyActionMap["AskHelp"], Doer: tr.AskHelp()},
|
||||||
|
{IconBytes: icons.ActionBugReport, Text: "Report bug", ShortcutText: keyActionMap["ReportBug"], Doer: tr.ReportBug()},
|
||||||
|
{IconBytes: icons.ActionCopyright, Text: "License", ShortcutText: keyActionMap["ShowLicense"], Doer: tr.ShowLicense()},
|
||||||
}
|
}
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,9 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"image"
|
"image"
|
||||||
"io"
|
"io"
|
||||||
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"gioui.org/app"
|
"gioui.org/app"
|
||||||
@ -58,6 +60,10 @@ type (
|
|||||||
*tracker.Model
|
*tracker.Model
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ShowManual Tracker
|
||||||
|
AskHelp Tracker
|
||||||
|
ReportBug Tracker
|
||||||
|
|
||||||
C = layout.Context
|
C = layout.Context
|
||||||
D = layout.Dimensions
|
D = layout.Dimensions
|
||||||
)
|
)
|
||||||
@ -85,7 +91,6 @@ func NewTracker(model *tracker.Model) *Tracker {
|
|||||||
InstrumentEditor: NewInstrumentEditor(model),
|
InstrumentEditor: NewInstrumentEditor(model),
|
||||||
OrderEditor: NewOrderEditor(model),
|
OrderEditor: NewOrderEditor(model),
|
||||||
TrackEditor: NewNoteEditor(model),
|
TrackEditor: NewNoteEditor(model),
|
||||||
SongPanel: NewSongPanel(model),
|
|
||||||
|
|
||||||
Zoom: 6,
|
Zoom: 6,
|
||||||
|
|
||||||
@ -93,6 +98,7 @@ func NewTracker(model *tracker.Model) *Tracker {
|
|||||||
|
|
||||||
filePathString: model.FilePath(),
|
filePathString: model.FilePath(),
|
||||||
}
|
}
|
||||||
|
t.SongPanel = NewSongPanel(t)
|
||||||
t.KeyNoteMap = MakeKeyboard[key.Name](model.Broker())
|
t.KeyNoteMap = MakeKeyboard[key.Name](model.Broker())
|
||||||
t.PopupAlert = NewPopupAlert(model.Alerts())
|
t.PopupAlert = NewPopupAlert(model.Alerts())
|
||||||
var warn error
|
var warn error
|
||||||
@ -352,3 +358,32 @@ func (t *Tracker) layoutTop(gtx layout.Context) layout.Dimensions {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *Tracker) ShowManual() tracker.Action { return tracker.MakeEnabledAction((*ShowManual)(t)) }
|
||||||
|
func (t *ShowManual) Do() { (*Tracker)(t).openUrl("https://github.com/vsariola/sointu/wiki") }
|
||||||
|
|
||||||
|
func (t *Tracker) AskHelp() tracker.Action { return tracker.MakeEnabledAction((*AskHelp)(t)) }
|
||||||
|
func (t *AskHelp) Do() {
|
||||||
|
(*Tracker)(t).openUrl("https://github.com/vsariola/sointu/discussions/categories/help-needed")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Tracker) ReportBug() tracker.Action { return tracker.MakeEnabledAction((*ReportBug)(t)) }
|
||||||
|
func (t *ReportBug) Do() { (*Tracker)(t).openUrl("https://github.com/vsariola/sointu/issues") }
|
||||||
|
|
||||||
|
func (t *Tracker) openUrl(url string) {
|
||||||
|
var err error
|
||||||
|
// following https://gist.github.com/hyg/9c4afcd91fe24316cbf0
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "linux":
|
||||||
|
err = exec.Command("xdg-open", url).Start()
|
||||||
|
case "windows":
|
||||||
|
err = exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start()
|
||||||
|
case "darwin":
|
||||||
|
err = exec.Command("open", url).Start()
|
||||||
|
default:
|
||||||
|
err = fmt.Errorf("unsupported platform for opening urls %s", runtime.GOOS)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Alerts().Add(err.Error(), tracker.Error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user