From 6c11f0eeb563b2e56781568700302983df9e2b0d Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 12 May 2024 10:35:14 +0530 Subject: [PATCH] Move listing code into its own file --- kittens/choose_fonts/list.go | 238 +++++++++++++++++++++++++++++++++++ kittens/choose_fonts/ui.go | 233 ++-------------------------------- 2 files changed, 246 insertions(+), 225 deletions(-) create mode 100644 kittens/choose_fonts/list.go diff --git a/kittens/choose_fonts/list.go b/kittens/choose_fonts/list.go new file mode 100644 index 000000000..020f3d910 --- /dev/null +++ b/kittens/choose_fonts/list.go @@ -0,0 +1,238 @@ +package choose_fonts + +import ( + "fmt" + "kitty/tools/tui/loop" + "kitty/tools/tui/readline" + "kitty/tools/utils" + "kitty/tools/utils/style" + "kitty/tools/wcswidth" + "strings" +) + +var _ = fmt.Print + +type FontList struct { + rl *readline.Readline + family_list FamilyList + fonts map[string][]ListedFont + resolved_faces_from_kitty_conf ResolvedFaces + handler *handler + variable_data_requested_for *utils.Set[string] +} + +func (self *FontList) initialize(h *handler) { + self.handler = h + self.rl = readline.New(h.lp, readline.RlInit{DontMarkPrompts: true, Prompt: "Family: "}) + self.variable_data_requested_for = utils.NewSet[string](256) +} + +func (self *FontList) draw_search_bar() { + lp := self.handler.lp + lp.SetCursorVisible(true) + lp.SetCursorShape(loop.BAR_CURSOR, true) + sz, err := lp.ScreenSize() + if err != nil { + return + } + lp.MoveCursorTo(1, int(sz.HeightCells)) + lp.ClearToEndOfLine() + self.rl.RedrawNonAtomic() +} + +const SEPARATOR = "║" + +func center_string(x string, width int) string { + l := wcswidth.Stringwidth(x) + spaces := int(float64(width-l) / 2) + return strings.Repeat(" ", utils.Max(0, spaces)) + x +} + +func (self *FontList) draw_family_summary(start_x int, sz loop.ScreenSize) (err error) { + lp := self.handler.lp + family := self.family_list.CurrentFamily() + if family == "" || int(sz.WidthCells) < start_x+2 { + return nil + } + lines := []string{ + lp.SprintStyled("fg=green bold", center_string(family, int(sz.WidthCells)-start_x)), + "", + } + width := int(sz.WidthCells) - start_x - 1 + add_line := func(x string) { + lines = append(lines, style.WrapTextAsLines(x, width, style.WrapOptions{})...) + } + fonts := self.fonts[family] + if len(fonts) == 0 { + return fmt.Errorf("The family: %s has no fonts", family) + } + if has_variable_data_for_font(fonts[0]) { + s := styles_in_family(family, fonts) + for _, sg := range s.style_groups { + styles := sg.name + ": " + strings.Join(sg.styles, ", ") + add_line(styles) + add_line("") + } + if s.has_variable_faces { + add_line(fmt.Sprintf("This font is %s allowing for finer style control", lp.SprintStyled("fg=magenta", "variable"))) + } + add_line(fmt.Sprintf("Press the %s key to choose this family", lp.SprintStyled("fg=yellow", "Enter"))) + } else { + lines = append(lines, "Reading font data, please wait…") + key := fonts[0].cache_key() + if !self.variable_data_requested_for.Has(key) { + self.variable_data_requested_for.Add(key) + go func() { + self.handler.set_worker_error(ensure_variable_data_for_fonts(fonts...)) + lp.WakeupMainThread() + }() + } + } + + for i, line := range lines { + if i >= int(sz.HeightCells)-1 { + break + } + lp.MoveCursorTo(start_x+1, i+1) + lp.QueueWriteString(line) + } + return +} + +func (self *FontList) on_wakeup() { + self.family_list.UpdateFamilies(utils.StableSortWithKey(utils.Keys(self.fonts), strings.ToLower)) + self.family_list.SelectFamily(self.resolved_faces_from_kitty_conf.Font_family.Family) +} + +func (self *FontList) draw_screen() (err error) { + lp := self.handler.lp + sz, err := lp.ScreenSize() + if err != nil { + return err + } + num_rows := max(0, int(sz.HeightCells)-1) + mw := self.family_list.max_width + 1 + green_fg, _, _ := strings.Cut(lp.SprintStyled("fg=green", "|"), "|") + lines := make([]string, 0, num_rows) + for _, l := range self.family_list.Lines(num_rows) { + line := l.text + if l.is_current { + line = strings.ReplaceAll(line, MARK_AFTER, green_fg) + line = lp.SprintStyled("fg=green", ">") + lp.SprintStyled("fg=green bold", line) + } else { + line = " " + line + } + lines = append(lines, line) + } + _, _, str := self.handler.render_lines.InRectangle(lines, 0, 0, 0, num_rows, &self.handler.mouse_state, self.on_click) + lp.QueueWriteString(str) + seps := strings.Repeat(SEPARATOR, num_rows) + seps = strings.TrimSpace(seps) + _, _, str = self.handler.render_lines.InRectangle(strings.Split(seps, ""), mw+1, 0, 0, num_rows, &self.handler.mouse_state) + lp.QueueWriteString(str) + + if self.family_list.Len() > 0 { + if err = self.draw_family_summary(mw+3, sz); err != nil { + return err + } + } + self.draw_search_bar() + return +} + +func (self *FontList) on_click(id string) error { + which, data, found := strings.Cut(id, ":") + if !found { + return fmt.Errorf("Not a valid click id: %s", id) + } + switch which { + case "family-chosen": + if self.handler.state == LISTING_FAMILIES { + if self.family_list.Select(data) { + self.handler.draw_screen() + } else { + self.handler.lp.Beep() + } + + } + } + return nil +} + +func (self *FontList) update_family_search() { + text := self.rl.AllText() + if self.family_list.UpdateSearch(text) { + self.handler.draw_screen() + } else { + self.draw_search_bar() + } +} + +func (self *FontList) next(delta int, allow_wrapping bool) { + if self.family_list.Next(delta, allow_wrapping) { + self.handler.draw_screen() + } else { + self.handler.lp.Beep() + } +} + +func (self *FontList) on_key_event(event *loop.KeyEvent) (err error) { + if event.MatchesPressOrRepeat("esc") { + event.Handled = true + if self.rl.AllText() != "" { + self.rl.ResetText() + self.update_family_search() + self.handler.draw_screen() + } else { + return fmt.Errorf("canceled by user") + } + return + } + ev := event + if ev.MatchesPressOrRepeat("down") { + self.next(1, true) + ev.Handled = true + return nil + } + if ev.MatchesPressOrRepeat("up") { + self.next(-1, true) + ev.Handled = true + return nil + } + if ev.MatchesPressOrRepeat("page_down") { + ev.Handled = true + sz, err := self.handler.lp.ScreenSize() + if err == nil { + self.next(int(sz.HeightCells)-3, false) + } + return nil + } + if ev.MatchesPressOrRepeat("page_up") { + ev.Handled = true + sz, err := self.handler.lp.ScreenSize() + if err == nil { + self.next(3-int(sz.HeightCells), false) + } + return nil + } + + if err = self.rl.OnKeyEvent(event); err != nil { + if err == readline.ErrAcceptInput { + return nil + } + return err + } + if event.Handled { + self.update_family_search() + } + self.draw_search_bar() + return +} + +func (self *FontList) on_text(text string, from_key_event bool, in_bracketed_paste bool) (err error) { + if err = self.rl.OnText(text, from_key_event, in_bracketed_paste); err != nil { + return err + } + self.update_family_search() + return +} diff --git a/kittens/choose_fonts/ui.go b/kittens/choose_fonts/ui.go index 5110edb48..05e549745 100644 --- a/kittens/choose_fonts/ui.go +++ b/kittens/choose_fonts/ui.go @@ -3,16 +3,11 @@ package choose_fonts import ( "fmt" "strconv" - "strings" "sync" "kitty/tools/tui" "kitty/tools/tui/graphics" "kitty/tools/tui/loop" - "kitty/tools/tui/readline" - "kitty/tools/utils" - "kitty/tools/utils/style" - "kitty/tools/wcswidth" ) var _ = fmt.Print @@ -44,12 +39,7 @@ type handler struct { text_style TextStyle graphics_manager graphics_manager - // Listing - rl *readline.Readline - family_list FamilyList - variable_data_requested_for *utils.Set[string] - fonts map[string][]ListedFont - resolved_faces_from_kitty_conf ResolvedFaces + listing FontList } func (h *handler) set_worker_error(err error) { @@ -64,226 +54,20 @@ func (h *handler) get_worker_error() error { return h.err_in_worker_thread } -// Listing families {{{ -func (h *handler) draw_search_bar() { - h.lp.SetCursorVisible(true) - h.lp.SetCursorShape(loop.BAR_CURSOR, true) - sz, err := h.lp.ScreenSize() - if err != nil { - return - } - h.lp.MoveCursorTo(1, int(sz.HeightCells)) - h.lp.ClearToEndOfLine() - h.rl.RedrawNonAtomic() -} - -const SEPARATOR = "║" - -func center_string(x string, width int) string { - l := wcswidth.Stringwidth(x) - spaces := int(float64(width-l) / 2) - return strings.Repeat(" ", utils.Max(0, spaces)) + x -} - -func (h *handler) draw_family_summary(start_x int, sz loop.ScreenSize) (err error) { - family := h.family_list.CurrentFamily() - if family == "" || int(sz.WidthCells) < start_x+2 { - return nil - } - lines := []string{ - h.lp.SprintStyled("fg=green bold", center_string(family, int(sz.WidthCells)-start_x)), - "", - } - width := int(sz.WidthCells) - start_x - 1 - add_line := func(x string) { - lines = append(lines, style.WrapTextAsLines(x, width, style.WrapOptions{})...) - } - fonts := h.fonts[family] - if len(fonts) == 0 { - return fmt.Errorf("The family: %s has no fonts", family) - } - if has_variable_data_for_font(fonts[0]) { - s := styles_in_family(family, fonts) - for _, sg := range s.style_groups { - styles := sg.name + ": " + strings.Join(sg.styles, ", ") - add_line(styles) - add_line("") - } - if s.has_variable_faces { - add_line(fmt.Sprintf("This font is %s allowing for finer style control", h.lp.SprintStyled("fg=magenta", "variable"))) - } - add_line(fmt.Sprintf("Press the %s key to choose this family", h.lp.SprintStyled("fg=yellow", "Enter"))) - } else { - lines = append(lines, "Reading font data, please wait…") - key := fonts[0].cache_key() - if !h.variable_data_requested_for.Has(key) { - h.variable_data_requested_for.Add(key) - go func() { - h.set_worker_error(ensure_variable_data_for_fonts(fonts...)) - h.lp.WakeupMainThread() - }() - } - } - - for i, line := range lines { - if i >= int(sz.HeightCells)-1 { - break - } - h.lp.MoveCursorTo(start_x+1, i+1) - h.lp.QueueWriteString(line) - } - return -} - -func (h *handler) draw_listing_screen() (err error) { - sz, err := h.lp.ScreenSize() - if err != nil { - return err - } - num_rows := max(0, int(sz.HeightCells)-1) - mw := h.family_list.max_width + 1 - green_fg, _, _ := strings.Cut(h.lp.SprintStyled("fg=green", "|"), "|") - lines := make([]string, 0, num_rows) - for _, l := range h.family_list.Lines(num_rows) { - line := l.text - if l.is_current { - line = strings.ReplaceAll(line, MARK_AFTER, green_fg) - line = h.lp.SprintStyled("fg=green", ">") + h.lp.SprintStyled("fg=green bold", line) - } else { - line = " " + line - } - lines = append(lines, line) - } - _, _, str := h.render_lines.InRectangle(lines, 0, 0, 0, num_rows, &h.mouse_state, h.handle_click) - h.lp.QueueWriteString(str) - seps := strings.Repeat(SEPARATOR, num_rows) - seps = strings.TrimSpace(seps) - _, _, str = h.render_lines.InRectangle(strings.Split(seps, ""), mw+1, 0, 0, num_rows, &h.mouse_state) - h.lp.QueueWriteString(str) - - if h.family_list.Len() > 0 { - if err = h.draw_family_summary(mw+3, sz); err != nil { - return err - } - } - h.draw_search_bar() - return -} - -func (h *handler) handle_click(id string) error { - which, data, found := strings.Cut(id, ":") - if !found { - return fmt.Errorf("Not a valid click id: %s", id) - } - switch which { - case "family-chosen": - if h.state == LISTING_FAMILIES { - if h.family_list.Select(data) { - h.draw_screen() - } else { - h.lp.Beep() - } - - } - } - return nil -} - -func (h *handler) update_family_search() { - text := h.rl.AllText() - if h.family_list.UpdateSearch(text) { - h.draw_screen() - } else { - h.draw_search_bar() - } -} - -func (h *handler) next(delta int, allow_wrapping bool) { - if h.family_list.Next(delta, allow_wrapping) { - h.draw_screen() - } else { - h.lp.Beep() - } -} - -func (h *handler) handle_listing_key_event(event *loop.KeyEvent) (err error) { - if event.MatchesPressOrRepeat("esc") { - event.Handled = true - if h.rl.AllText() != "" { - h.rl.ResetText() - h.update_family_search() - h.draw_screen() - } else { - return fmt.Errorf("canceled by user") - } - return - } - ev := event - if ev.MatchesPressOrRepeat("down") { - h.next(1, true) - ev.Handled = true - return nil - } - if ev.MatchesPressOrRepeat("up") { - h.next(-1, true) - ev.Handled = true - return nil - } - if ev.MatchesPressOrRepeat("page_down") { - ev.Handled = true - sz, err := h.lp.ScreenSize() - if err == nil { - h.next(int(sz.HeightCells)-3, false) - } - return nil - } - if ev.MatchesPressOrRepeat("page_up") { - ev.Handled = true - sz, err := h.lp.ScreenSize() - if err == nil { - h.next(3-int(sz.HeightCells), false) - } - return nil - } - - if err = h.rl.OnKeyEvent(event); err != nil { - if err == readline.ErrAcceptInput { - return nil - } - return err - } - if event.Handled { - h.update_family_search() - } - h.draw_search_bar() - return -} - -func (h *handler) handle_listing_text(text string, from_key_event bool, in_bracketed_paste bool) (err error) { - if err = h.rl.OnText(text, from_key_event, in_bracketed_paste); err != nil { - return err - } - h.update_family_search() - return -} - -// }}} - // Events {{{ func (h *handler) initialize() { h.lp.SetCursorVisible(false) h.lp.OnQueryResponse = h.on_query_response h.lp.QueryTerminal("font_size", "dpi_x", "dpi_y", "foreground", "background") - h.rl = readline.New(h.lp, readline.RlInit{DontMarkPrompts: true, Prompt: "Family: "}) - h.variable_data_requested_for = utils.NewSet[string](256) + h.listing.initialize(h) h.draw_screen() initialize_variable_data_cache() h.graphics_manager.initialize(h.lp) go func() { var r ListResult h.set_worker_error(kitty_font_backend.query("list_monospaced_fonts", nil, &r)) - h.fonts = r.Fonts - h.resolved_faces_from_kitty_conf = r.Resolved_faces + h.listing.fonts = r.Fonts + h.listing.resolved_faces_from_kitty_conf = r.Resolved_faces h.lp.WakeupMainThread() }() } @@ -342,7 +126,7 @@ func (h *handler) draw_screen() (err error) { case SCANNING_FAMILIES: h.lp.Println("Scanning system for fonts, please wait...") case LISTING_FAMILIES: - return h.draw_listing_screen() + return h.listing.draw_screen() } return } @@ -354,8 +138,7 @@ func (h *handler) on_wakeup() (err error) { switch h.state { case SCANNING_FAMILIES: h.state = LISTING_FAMILIES - h.family_list.UpdateFamilies(utils.StableSortWithKey(utils.Keys(h.fonts), strings.ToLower)) - h.family_list.SelectFamily(h.resolved_faces_from_kitty_conf.Font_family.Family) + h.listing.on_wakeup() } return h.draw_screen() } @@ -384,7 +167,7 @@ func (h *handler) on_key_event(event *loop.KeyEvent) (err error) { } switch h.state { case LISTING_FAMILIES: - return h.handle_listing_key_event(event) + return h.listing.on_key_event(event) } return } @@ -392,7 +175,7 @@ func (h *handler) on_key_event(event *loop.KeyEvent) (err error) { func (h *handler) on_text(text string, from_key_event bool, in_bracketed_paste bool) (err error) { switch h.state { case LISTING_FAMILIES: - return h.handle_listing_text(text, from_key_event, in_bracketed_paste) + return h.listing.on_text(text, from_key_event, in_bracketed_paste) } return }