ui.go 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. package choose_fonts
  2. import (
  3. "fmt"
  4. "os"
  5. "strconv"
  6. "sync"
  7. "kitty/tools/tui"
  8. "kitty/tools/tui/graphics"
  9. "kitty/tools/tui/loop"
  10. "kitty/tools/utils"
  11. )
  12. var _ = fmt.Print
  13. type State int
  14. const (
  15. SCANNING_FAMILIES State = iota
  16. LISTING_FAMILIES
  17. CHOOSING_FACES
  18. )
  19. type TextStyle struct {
  20. Font_sz float64 `json:"font_size"`
  21. Dpi_x float64 `json:"dpi_x"`
  22. Dpi_y float64 `json:"dpi_y"`
  23. Foreground string `json:"foreground"`
  24. Background string `json:"background"`
  25. }
  26. type pane interface {
  27. initialize(*handler) error
  28. draw_screen() error
  29. on_wakeup() error
  30. on_key_event(event *loop.KeyEvent) error
  31. on_text(text string, from_key_event bool, in_bracketed_paste bool) error
  32. on_click(id string) error
  33. }
  34. type handler struct {
  35. opts *Options
  36. lp *loop.Loop
  37. state State
  38. err_mutex sync.Mutex
  39. err_in_worker_thread error
  40. mouse_state tui.MouseState
  41. render_count uint
  42. render_lines tui.RenderLines
  43. text_style TextStyle
  44. graphics_manager graphics_manager
  45. temp_dir string
  46. listing FontList
  47. faces faces
  48. face_pane face_panel
  49. if_pane if_panel
  50. final_pane final_pane
  51. panes []pane
  52. current_pane pane
  53. }
  54. func (h *handler) set_worker_error(err error) {
  55. h.err_mutex.Lock()
  56. defer h.err_mutex.Unlock()
  57. h.err_in_worker_thread = err
  58. }
  59. func (h *handler) get_worker_error() error {
  60. h.err_mutex.Lock()
  61. defer h.err_mutex.Unlock()
  62. return h.err_in_worker_thread
  63. }
  64. // Events {{{
  65. func (h *handler) initialize() (err error) {
  66. h.lp.SetCursorVisible(false)
  67. h.lp.OnQueryResponse = h.on_query_response
  68. h.lp.QueryTerminal("font_size", "dpi_x", "dpi_y", "foreground", "background")
  69. h.panes = []pane{&h.listing, &h.faces, &h.face_pane, &h.if_pane, &h.final_pane}
  70. for _, pane := range h.panes {
  71. if err = pane.initialize(h); err != nil {
  72. return err
  73. }
  74. }
  75. // dont use /tmp as it may be mounted in RAM, Le Sigh
  76. if h.temp_dir, err = os.MkdirTemp(utils.CacheDir(), "kitten-choose-fonts-*"); err != nil {
  77. return
  78. }
  79. initialize_variable_data_cache()
  80. h.graphics_manager.initialize(h.lp)
  81. go func() {
  82. var r ListResult
  83. h.set_worker_error(kitty_font_backend.query("list_monospaced_fonts", nil, &r))
  84. h.listing.fonts = r.Fonts
  85. h.listing.resolved_faces_from_kitty_conf = r.Resolved_faces
  86. h.lp.WakeupMainThread()
  87. }()
  88. h.draw_screen()
  89. return
  90. }
  91. func (h *handler) finalize() {
  92. if h.temp_dir != "" {
  93. os.RemoveAll(h.temp_dir)
  94. h.temp_dir = ""
  95. }
  96. h.lp.SetCursorVisible(true)
  97. h.lp.SetCursorShape(loop.BLOCK_CURSOR, true)
  98. h.graphics_manager.finalize()
  99. }
  100. func (h *handler) on_query_response(key, val string, valid bool) error {
  101. if !valid {
  102. return fmt.Errorf("Terminal does not support querying the: %s", key)
  103. }
  104. set_float := func(k, v string, dest *float64) error {
  105. if fs, err := strconv.ParseFloat(v, 64); err == nil {
  106. *dest = fs
  107. } else {
  108. return fmt.Errorf("Invalid response from terminal to %s query: %#v", k, v)
  109. }
  110. return nil
  111. }
  112. switch key {
  113. case "font_size":
  114. if err := set_float(key, val, &h.text_style.Font_sz); err != nil {
  115. return err
  116. }
  117. case "dpi_x":
  118. if err := set_float(key, val, &h.text_style.Dpi_x); err != nil {
  119. return err
  120. }
  121. case "dpi_y":
  122. if err := set_float(key, val, &h.text_style.Dpi_y); err != nil {
  123. return err
  124. }
  125. case "foreground":
  126. h.text_style.Foreground = val
  127. case "background":
  128. h.text_style.Background = val
  129. return h.draw_screen()
  130. }
  131. return nil
  132. }
  133. func (h *handler) draw_screen() (err error) {
  134. h.render_count++
  135. h.lp.StartAtomicUpdate()
  136. defer func() {
  137. h.mouse_state.UpdateHoveredIds()
  138. h.mouse_state.ApplyHoverStyles(h.lp)
  139. h.lp.EndAtomicUpdate()
  140. }()
  141. h.graphics_manager.clear_placements()
  142. h.lp.ClearScreenButNotGraphics()
  143. h.lp.AllowLineWrapping(false)
  144. h.mouse_state.ClearCellRegions()
  145. if h.current_pane == nil {
  146. h.lp.Println("Scanning system for fonts, please wait...")
  147. } else {
  148. return h.current_pane.draw_screen()
  149. }
  150. return
  151. }
  152. func (h *handler) on_wakeup() (err error) {
  153. if err = h.get_worker_error(); err != nil {
  154. return
  155. }
  156. if h.current_pane == nil {
  157. h.current_pane = &h.listing
  158. }
  159. return h.listing.on_wakeup()
  160. }
  161. func (h *handler) on_mouse_event(event *loop.MouseEvent) (err error) {
  162. rc := h.render_count
  163. redraw_needed := false
  164. if h.mouse_state.UpdateState(event) {
  165. redraw_needed = true
  166. }
  167. if event.Event_type == loop.MOUSE_CLICK && event.Buttons&loop.LEFT_MOUSE_BUTTON != 0 {
  168. if err = h.mouse_state.ClickHoveredRegions(); err != nil {
  169. return
  170. }
  171. }
  172. if redraw_needed && rc == h.render_count {
  173. err = h.draw_screen()
  174. }
  175. return
  176. }
  177. func (h *handler) on_key_event(event *loop.KeyEvent) (err error) {
  178. if event.MatchesPressOrRepeat("ctrl+c") {
  179. event.Handled = true
  180. return fmt.Errorf("canceled by user")
  181. }
  182. if h.current_pane != nil {
  183. err = h.current_pane.on_key_event(event)
  184. }
  185. return
  186. }
  187. func (h *handler) on_text(text string, from_key_event bool, in_bracketed_paste bool) (err error) {
  188. if h.current_pane != nil {
  189. err = h.current_pane.on_text(text, from_key_event, in_bracketed_paste)
  190. }
  191. return
  192. }
  193. func (h *handler) on_escape_code(etype loop.EscapeCodeType, payload []byte) error {
  194. switch etype {
  195. case loop.APC:
  196. gc := graphics.GraphicsCommandFromAPC(payload)
  197. if gc != nil {
  198. return h.graphics_manager.on_response(gc)
  199. }
  200. }
  201. return nil
  202. }
  203. // }}}