options.go 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. package tea
  2. import (
  3. "context"
  4. "io"
  5. "github.com/muesli/termenv"
  6. )
  7. // ProgramOption is used to set options when initializing a Program. Program can
  8. // accept a variable number of options.
  9. //
  10. // Example usage:
  11. //
  12. // p := NewProgram(model, WithInput(someInput), WithOutput(someOutput))
  13. type ProgramOption func(*Program)
  14. // WithContext lets you specify a context in which to run the Program. This is
  15. // useful if you want to cancel the execution from outside. When a Program gets
  16. // cancelled it will exit with an error ErrProgramKilled.
  17. func WithContext(ctx context.Context) ProgramOption {
  18. return func(p *Program) {
  19. p.ctx = ctx
  20. }
  21. }
  22. // WithOutput sets the output which, by default, is stdout. In most cases you
  23. // won't need to use this.
  24. func WithOutput(output io.Writer) ProgramOption {
  25. return func(p *Program) {
  26. if o, ok := output.(*termenv.Output); ok {
  27. p.output = o
  28. } else {
  29. p.output = termenv.NewOutput(output, termenv.WithColorCache(true))
  30. }
  31. }
  32. }
  33. // WithInput sets the input which, by default, is stdin. In most cases you
  34. // won't need to use this. To disable input entirely pass nil.
  35. //
  36. // p := NewProgram(model, WithInput(nil))
  37. func WithInput(input io.Reader) ProgramOption {
  38. return func(p *Program) {
  39. p.input = input
  40. p.inputType = customInput
  41. }
  42. }
  43. // WithInputTTY opens a new TTY for input (or console input device on Windows).
  44. func WithInputTTY() ProgramOption {
  45. return func(p *Program) {
  46. p.inputType = ttyInput
  47. }
  48. }
  49. // WithoutSignalHandler disables the signal handler that Bubble Tea sets up for
  50. // Programs. This is useful if you want to handle signals yourself.
  51. func WithoutSignalHandler() ProgramOption {
  52. return func(p *Program) {
  53. p.startupOptions |= withoutSignalHandler
  54. }
  55. }
  56. // WithoutCatchPanics disables the panic catching that Bubble Tea does by
  57. // default. If panic catching is disabled the terminal will be in a fairly
  58. // unusable state after a panic because Bubble Tea will not perform its usual
  59. // cleanup on exit.
  60. func WithoutCatchPanics() ProgramOption {
  61. return func(p *Program) {
  62. p.startupOptions |= withoutCatchPanics
  63. }
  64. }
  65. // WithoutSignals will ignore OS signals.
  66. // This is mainly useful for testing.
  67. func WithoutSignals() ProgramOption {
  68. return func(p *Program) {
  69. p.ignoreSignals = true
  70. }
  71. }
  72. // WithAltScreen starts the program with the alternate screen buffer enabled
  73. // (i.e. the program starts in full window mode). Note that the altscreen will
  74. // be automatically exited when the program quits.
  75. //
  76. // Example:
  77. //
  78. // p := tea.NewProgram(Model{}, tea.WithAltScreen())
  79. // if _, err := p.Run(); err != nil {
  80. // fmt.Println("Error running program:", err)
  81. // os.Exit(1)
  82. // }
  83. //
  84. // To enter the altscreen once the program has already started running use the
  85. // EnterAltScreen command.
  86. func WithAltScreen() ProgramOption {
  87. return func(p *Program) {
  88. p.startupOptions |= withAltScreen
  89. }
  90. }
  91. // WithMouseCellMotion starts the program with the mouse enabled in "cell
  92. // motion" mode.
  93. //
  94. // Cell motion mode enables mouse click, release, and wheel events. Mouse
  95. // movement events are also captured if a mouse button is pressed (i.e., drag
  96. // events). Cell motion mode is better supported than all motion mode.
  97. //
  98. // To enable mouse cell motion once the program has already started running use
  99. // the EnableMouseCellMotion command. To disable the mouse when the program is
  100. // running use the DisableMouse command.
  101. //
  102. // The mouse will be automatically disabled when the program exits.
  103. func WithMouseCellMotion() ProgramOption {
  104. return func(p *Program) {
  105. p.startupOptions |= withMouseCellMotion // set
  106. p.startupOptions &^= withMouseAllMotion // clear
  107. }
  108. }
  109. // WithMouseAllMotion starts the program with the mouse enabled in "all motion"
  110. // mode.
  111. //
  112. // EnableMouseAllMotion is a special command that enables mouse click, release,
  113. // wheel, and motion events, which are delivered regardless of whether a mouse
  114. // button is pressed, effectively enabling support for hover interactions.
  115. //
  116. // Many modern terminals support this, but not all. If in doubt, use
  117. // EnableMouseCellMotion instead.
  118. //
  119. // To enable the mouse once the program has already started running use the
  120. // EnableMouseAllMotion command. To disable the mouse when the program is
  121. // running use the DisableMouse command.
  122. //
  123. // The mouse will be automatically disabled when the program exits.
  124. func WithMouseAllMotion() ProgramOption {
  125. return func(p *Program) {
  126. p.startupOptions |= withMouseAllMotion // set
  127. p.startupOptions &^= withMouseCellMotion // clear
  128. }
  129. }
  130. // WithoutRenderer disables the renderer. When this is set output and log
  131. // statements will be plainly sent to stdout (or another output if one is set)
  132. // without any rendering and redrawing logic. In other words, printing and
  133. // logging will behave the same way it would in a non-TUI commandline tool.
  134. // This can be useful if you want to use the Bubble Tea framework for a non-TUI
  135. // application, or to provide an additional non-TUI mode to your Bubble Tea
  136. // programs. For example, your program could behave like a daemon if output is
  137. // not a TTY.
  138. func WithoutRenderer() ProgramOption {
  139. return func(p *Program) {
  140. p.renderer = &nilRenderer{}
  141. }
  142. }
  143. // WithANSICompressor removes redundant ANSI sequences to produce potentially
  144. // smaller output, at the cost of some processing overhead.
  145. //
  146. // This feature is provisional, and may be changed or removed in a future version
  147. // of this package.
  148. func WithANSICompressor() ProgramOption {
  149. return func(p *Program) {
  150. p.startupOptions |= withANSICompressor
  151. }
  152. }
  153. // WithFilter supplies an event filter that will be invoked before Bubble Tea
  154. // processes a tea.Msg. The event filter can return any tea.Msg which will then
  155. // get handled by Bubble Tea instead of the original event. If the event filter
  156. // returns nil, the event will be ignored and Bubble Tea will not process it.
  157. //
  158. // As an example, this could be used to prevent a program from shutting down if
  159. // there are unsaved changes.
  160. //
  161. // Example:
  162. //
  163. // func filter(m tea.Model, msg tea.Msg) tea.Msg {
  164. // if _, ok := msg.(tea.QuitMsg); !ok {
  165. // return msg
  166. // }
  167. //
  168. // model := m.(myModel)
  169. // if model.hasChanges {
  170. // return nil
  171. // }
  172. //
  173. // return msg
  174. // }
  175. //
  176. // p := tea.NewProgram(Model{}, tea.WithFilter(filter));
  177. //
  178. // if _,err := p.Run(); err != nil {
  179. // fmt.Println("Error running program:", err)
  180. // os.Exit(1)
  181. // }
  182. func WithFilter(filter func(Model, Msg) Msg) ProgramOption {
  183. return func(p *Program) {
  184. p.filter = filter
  185. }
  186. }