app.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. // Package app provides app implementations for working with Fyne graphical interfaces.
  2. // The fastest way to get started is to call app.New() which will normally load a new desktop application.
  3. // If the "ci" tag is passed to go (go run -tags ci myapp.go) it will run an in-memory application.
  4. package app // import "fyne.io/fyne/v2/app"
  5. import (
  6. "os"
  7. "strconv"
  8. "sync/atomic"
  9. "time"
  10. "fyne.io/fyne/v2"
  11. "fyne.io/fyne/v2/internal"
  12. "fyne.io/fyne/v2/internal/app"
  13. intRepo "fyne.io/fyne/v2/internal/repository"
  14. "fyne.io/fyne/v2/storage/repository"
  15. "golang.org/x/sys/execabs"
  16. )
  17. // Declare conformity with App interface
  18. var _ fyne.App = (*fyneApp)(nil)
  19. type fyneApp struct {
  20. driver fyne.Driver
  21. icon fyne.Resource
  22. uniqueID string
  23. cloud fyne.CloudProvider
  24. lifecycle fyne.Lifecycle
  25. settings *settings
  26. storage fyne.Storage
  27. prefs fyne.Preferences
  28. running uint32 // atomic, 1 == running, 0 == stopped
  29. exec func(name string, arg ...string) *execabs.Cmd
  30. }
  31. func (a *fyneApp) CloudProvider() fyne.CloudProvider {
  32. return a.cloud
  33. }
  34. func (a *fyneApp) Icon() fyne.Resource {
  35. if a.icon != nil {
  36. return a.icon
  37. }
  38. return a.Metadata().Icon
  39. }
  40. func (a *fyneApp) SetIcon(icon fyne.Resource) {
  41. a.icon = icon
  42. }
  43. func (a *fyneApp) UniqueID() string {
  44. if a.uniqueID != "" {
  45. return a.uniqueID
  46. }
  47. if a.Metadata().ID != "" {
  48. return a.Metadata().ID
  49. }
  50. fyne.LogError("Preferences API requires a unique ID, use app.NewWithID() or the FyneApp.toml ID field", nil)
  51. a.uniqueID = "missing-id-" + strconv.FormatInt(time.Now().Unix(), 10) // This is a fake unique - it just has to not be reused...
  52. return a.uniqueID
  53. }
  54. func (a *fyneApp) NewWindow(title string) fyne.Window {
  55. return a.driver.CreateWindow(title)
  56. }
  57. func (a *fyneApp) Run() {
  58. if atomic.CompareAndSwapUint32(&a.running, 0, 1) {
  59. a.driver.Run()
  60. return
  61. }
  62. }
  63. func (a *fyneApp) Quit() {
  64. for _, window := range a.driver.AllWindows() {
  65. window.Close()
  66. }
  67. a.driver.Quit()
  68. a.settings.stopWatching()
  69. atomic.StoreUint32(&a.running, 0)
  70. }
  71. func (a *fyneApp) Driver() fyne.Driver {
  72. return a.driver
  73. }
  74. // Settings returns the application settings currently configured.
  75. func (a *fyneApp) Settings() fyne.Settings {
  76. return a.settings
  77. }
  78. func (a *fyneApp) Storage() fyne.Storage {
  79. return a.storage
  80. }
  81. func (a *fyneApp) Preferences() fyne.Preferences {
  82. if a.UniqueID() == "" {
  83. fyne.LogError("Preferences API requires a unique ID, use app.NewWithID() or the FyneApp.toml ID field", nil)
  84. }
  85. return a.prefs
  86. }
  87. func (a *fyneApp) Lifecycle() fyne.Lifecycle {
  88. return a.lifecycle
  89. }
  90. func (a *fyneApp) newDefaultPreferences() fyne.Preferences {
  91. p := fyne.Preferences(newPreferences(a))
  92. if pref, ok := p.(interface{ load() }); ok && a.uniqueID != "" {
  93. pref.load()
  94. }
  95. return p
  96. }
  97. // New returns a new application instance with the default driver and no unique ID (unless specified in FyneApp.toml)
  98. func New() fyne.App {
  99. if meta.ID == "" {
  100. internal.LogHint("Applications should be created with a unique ID using app.NewWithID()")
  101. }
  102. return NewWithID(meta.ID)
  103. }
  104. func makeStoreDocs(id string, p fyne.Preferences, s *store) *internal.Docs {
  105. if id != "" {
  106. if pref, ok := p.(interface{ load() }); ok {
  107. pref.load()
  108. }
  109. err := os.MkdirAll(s.a.storageRoot(), 0755) // make the space before anyone can use it
  110. if err != nil {
  111. fyne.LogError("Failed to create app storage space", err)
  112. }
  113. root, _ := s.docRootURI()
  114. return &internal.Docs{RootDocURI: root}
  115. } else {
  116. return &internal.Docs{} // an empty impl to avoid crashes
  117. }
  118. }
  119. func newAppWithDriver(d fyne.Driver, id string) fyne.App {
  120. newApp := &fyneApp{uniqueID: id, driver: d, exec: execabs.Command, lifecycle: &app.Lifecycle{}}
  121. fyne.SetCurrentApp(newApp)
  122. newApp.prefs = newApp.newDefaultPreferences()
  123. newApp.settings = loadSettings()
  124. store := &store{a: newApp}
  125. store.Docs = makeStoreDocs(id, newApp.prefs, store)
  126. newApp.storage = store
  127. if !d.Device().IsMobile() {
  128. newApp.settings.watchSettings()
  129. }
  130. repository.Register("http", intRepo.NewHTTPRepository())
  131. repository.Register("https", intRepo.NewHTTPRepository())
  132. return newApp
  133. }
  134. // marker interface to pass system tray to supporting drivers
  135. type systrayDriver interface {
  136. SetSystemTrayMenu(*fyne.Menu)
  137. SetSystemTrayIcon(resource fyne.Resource)
  138. }