main.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. // Command mpckit is the desktop kit editor for the MPC Sample.
  2. package main
  3. import (
  4. "embed"
  5. "log"
  6. "path/filepath"
  7. "strconv"
  8. "strings"
  9. "github.com/wailsapp/wails/v3/pkg/application"
  10. "github.com/wailsapp/wails/v3/pkg/events"
  11. "git.monoexp.com/bajdev/mpckit/internal/app"
  12. )
  13. // The built Svelte app (frontend/dist), embedded in the binary.
  14. //
  15. //go:embed all:frontend/dist
  16. var assets embed.FS
  17. // Window sizes are in device-independent pixels, 16:9 like most displays.
  18. const (
  19. maxWidth, maxHeight = 1280, 720 // largest default size
  20. minWidth, minHeight = 960, 540 // smallest the layout is designed for
  21. screenShare = 0.9 // default size: this share of the screen's work area
  22. )
  23. // Events Go sends to the frontend.
  24. const (
  25. eventState = "state" // a new app.State after a change the frontend did not ask for
  26. eventError = "error" // a message to show
  27. )
  28. func init() {
  29. application.RegisterEvent[app.State](eventState)
  30. application.RegisterEvent[string](eventError)
  31. }
  32. func main() {
  33. d := &dialogs{}
  34. svc := app.NewService(d)
  35. a := application.New(application.Options{
  36. Name: "mpckit",
  37. Description: "Desktop kit editor for the MPC Sample",
  38. Services: []application.Service{
  39. application.NewService(svc),
  40. },
  41. Assets: application.AssetOptions{
  42. Handler: application.AssetFileServerFS(assets),
  43. Middleware: svc.AudioMiddleware,
  44. },
  45. Mac: application.MacOptions{
  46. ApplicationShouldTerminateAfterLastWindowClosed: true,
  47. },
  48. })
  49. d.a = a
  50. // Start hidden and size the window once the screens are known, so it
  51. // fits the display instead of being clamped by it.
  52. w := a.Window.NewWithOptions(application.WebviewWindowOptions{
  53. Title: "mpckit",
  54. Width: maxWidth,
  55. Height: maxHeight,
  56. MinWidth: minWidth,
  57. MinHeight: minHeight,
  58. Hidden: true,
  59. EnableFileDrop: true,
  60. BackgroundColour: application.NewRGB(0x15, 0x17, 0x1A),
  61. URL: "/",
  62. })
  63. a.Event.OnApplicationEvent(events.Common.ApplicationStarted, func(*application.ApplicationEvent) {
  64. if s := a.Screen.GetPrimary(); s != nil {
  65. width, height := fitSize(s.WorkArea.Width, s.WorkArea.Height)
  66. w.SetSize(width, height)
  67. }
  68. w.Center()
  69. w.Show()
  70. })
  71. // A WAV dropped on a pad (an element with data-file-drop-target and
  72. // data-slot) is assigned to that pad.
  73. w.OnWindowEvent(events.Common.WindowFilesDropped, func(e *application.WindowEvent) {
  74. ctx := e.Context()
  75. target := ctx.DropTargetDetails()
  76. if target == nil {
  77. return
  78. }
  79. slot, err := strconv.Atoi(target.Attributes["data-slot"])
  80. if err != nil {
  81. return
  82. }
  83. for _, f := range ctx.DroppedFiles() {
  84. if strings.EqualFold(filepath.Ext(f), ".wav") {
  85. st, err := svc.AssignSamplePath(slot, f)
  86. a.Event.Emit(eventState, st)
  87. if err != nil {
  88. a.Event.Emit(eventError, err.Error())
  89. }
  90. return
  91. }
  92. }
  93. a.Event.Emit(eventError, "Only WAV files can be assigned to a pad.")
  94. })
  95. if err := a.Run(); err != nil {
  96. log.Fatal(err)
  97. }
  98. }
  99. // fitSize returns a 16:9 window size that takes screenShare of the work
  100. // area (areaW x areaH), capped at maxWidth x maxHeight and no smaller than
  101. // minWidth x minHeight.
  102. func fitSize(areaW, areaH int) (int, int) {
  103. width := min(maxWidth, int(float64(areaW)*screenShare))
  104. height := width * 9 / 16
  105. if limit := int(float64(areaH) * screenShare); height > limit {
  106. height = limit
  107. width = height * 16 / 9
  108. }
  109. return max(width, minWidth), max(height, minHeight)
  110. }