From 15904620380464b13de0e971fcd1127be3f03f31 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 12 May 2024 12:59:44 +0530 Subject: [PATCH] Get preview to basically display --- kittens/choose_fonts/backend.go | 4 +- kittens/choose_fonts/backend.py | 32 ++++++++-------- kittens/choose_fonts/graphics.go | 35 ++++++++++++++++- kittens/choose_fonts/list.go | 64 ++++++++++++++++++++++++++++++-- kittens/choose_fonts/main.go | 3 +- kittens/choose_fonts/ui.go | 18 +++++++-- 6 files changed, 128 insertions(+), 28 deletions(-) diff --git a/kittens/choose_fonts/backend.go b/kittens/choose_fonts/backend.go index 0a3c5c372..c3ab24f62 100644 --- a/kittens/choose_fonts/backend.go +++ b/kittens/choose_fonts/backend.go @@ -160,8 +160,6 @@ func (k *kitty_font_backend_type) release() (err error) { err = fmt.Errorf("Timed out waiting for kitty font backend to exit for %v", timeout) } } - if k.failed { - os.Stderr.WriteString(k.stderr.String()) - } + os.Stderr.WriteString(k.stderr.String()) return } diff --git a/kittens/choose_fonts/backend.py b/kittens/choose_fonts/backend.py index 1bf52406c..0ea49e863 100644 --- a/kittens/choose_fonts/backend.py +++ b/kittens/choose_fonts/backend.py @@ -33,7 +33,13 @@ class TextStyle(TypedDict): background: str -FamilyKey = Tuple[str, ...] +def print(*a: Any) -> None: + import builtins + builtins.print(*a, file=sys.stderr) + + +OptNames = Literal['font_family', 'bold_font', 'italic_font', 'bold_italic_font'] +FamilyKey = Tuple[OptNames, ...] def opts_from_cmd(cmd: Dict[str, Any]) -> Tuple[Options, FamilyKey, float, float]: @@ -43,18 +49,14 @@ def opts_from_cmd(cmd: Dict[str, Any]) -> Tuple[Options, FamilyKey, float, float opts.foreground = to_color(ts['foreground']) opts.background = to_color(ts['background']) family_key = [] - if 'font_family' in cmd: - opts.font_family = parse_font_spec(cmd['font_family']) - family_key.append(cmd['font_family']) - if 'bold_font' in cmd: - opts.bold_font = parse_font_spec(cmd['bold_font']) - family_key.append(cmd['bold_font']) - if 'italic_font' in cmd: - opts.italic_font = parse_font_spec(cmd['italic_font']) - family_key.append(cmd['italic_font']) - if 'bold_italic_font' in cmd: - opts.bold_italic_font = parse_font_spec(cmd['bold_italic_font']) - family_key.append(cmd['bold_italic_font']) + def d(k: OptNames) -> None: + if k in cmd: + opts.font_family = parse_font_spec(cmd[k]) + family_key.append(k) + d('font_family') + d('bold_font') + d('italic_font') + d('bold_italic_font') return opts, tuple(family_key), ts['dpi_x'], ts['dpi_y'] @@ -91,12 +93,12 @@ def render_family_sample( ans[x] = cached else: with tempfile.NamedTemporaryFile(delete=False, suffix='.rgba', dir=output_dir) as tf: - tf.write(render_face_sample(desc, opts, dpi_x, dpi_y, width, height)) + bitmap = render_face_sample(desc, opts, dpi_x, dpi_y, width, height) + tf.write(bitmap) cache[key] = ans[x] = tf.name return ans -OptNames = Literal['font_family', 'bold_font', 'italic_font', 'bold_italic_font'] ResolvedFace = Dict[Literal['family', 'spec'], str] diff --git a/kittens/choose_fonts/graphics.go b/kittens/choose_fonts/graphics.go index 5f527342b..abb595660 100644 --- a/kittens/choose_fonts/graphics.go +++ b/kittens/choose_fonts/graphics.go @@ -12,16 +12,29 @@ var _ = fmt.Print type image struct { id, image_number uint32 + placement_id uint32 current_file string } +func (i image) graphics_command() *graphics.GraphicsCommand { + gc := &graphics.GraphicsCommand{} + if i.id > 0 { + gc.SetImageId(i.id) + } else { + gc.SetImageNumber(i.image_number) + } + return gc +} + type graphics_manager struct { main, bold, italic, bi, extra image + lp *loop.Loop images [5]*image } func (g *graphics_manager) initialize(lp *loop.Loop) { g.images = [5]*image{&g.main, &g.bold, &g.italic, &g.bi, &g.extra} + g.lp = lp payload := []byte("123") buf := strings.Builder{} gc := &graphics.GraphicsCommand{} @@ -41,6 +54,24 @@ func (g *graphics_manager) initialize(lp *loop.Loop) { lp.QueueWriteString(buf.String()) } +func (g *graphics_manager) clear_placements() { + gc := graphics.GraphicsCommand{} + gc.SetAction(graphics.GRT_action_delete).SetDelete(graphics.GRT_delete_visible) + gc.WriteWithPayloadToLoop(g.lp, nil) +} + +func (g *graphics_manager) display_image(slot int, path string, img_width, img_height int) { + img := g.images[slot] + if img.current_file != path { + gc := img.graphics_command() + gc.SetAction(graphics.GRT_action_transmit).SetFormat(graphics.GRT_format_rgba).SetDataWidth(uint64(img_width)).SetDataHeight(uint64(img_height)).SetTransmission(graphics.GRT_transmission_file) + gc.WriteWithPayloadToLoop(g.lp, []byte(path)) + } + gc := img.graphics_command() + gc.SetAction(graphics.GRT_action_display).SetCursorMovement(graphics.GRT_cursor_static) + gc.WriteWithPayloadToLoop(g.lp, nil) +} + func (g *graphics_manager) on_response(gc *graphics.GraphicsCommand) (err error) { if gc.ResponseMessage() != "OK" { return fmt.Errorf("Failed to load image with error: %s", gc.ResponseMessage()) @@ -54,7 +85,7 @@ func (g *graphics_manager) on_response(gc *graphics.GraphicsCommand) (err error) return } -func (g *graphics_manager) finalize(lp *loop.Loop) { +func (g *graphics_manager) finalize() { buf := strings.Builder{} gc := &graphics.GraphicsCommand{} gc.SetAction(graphics.GRT_action_delete).SetDelete(graphics.GRT_free_by_number) @@ -67,5 +98,5 @@ func (g *graphics_manager) finalize(lp *loop.Loop) { d(g.italic.image_number) d(g.bi.image_number) d(g.extra.image_number) - lp.QueueWriteString(buf.String()) + g.lp.QueueWriteString(buf.String()) } diff --git a/kittens/choose_fonts/list.go b/kittens/choose_fonts/list.go index 020f3d910..454620240 100644 --- a/kittens/choose_fonts/list.go +++ b/kittens/choose_fonts/list.go @@ -7,11 +7,18 @@ import ( "kitty/tools/utils" "kitty/tools/utils/style" "kitty/tools/wcswidth" + "math" "strings" + "sync" ) var _ = fmt.Print +type preview_cache_key struct { + family string + width, height int +} + type FontList struct { rl *readline.Readline family_list FamilyList @@ -19,10 +26,13 @@ type FontList struct { resolved_faces_from_kitty_conf ResolvedFaces handler *handler variable_data_requested_for *utils.Set[string] + preview_cache map[preview_cache_key]string + preview_cache_mutex sync.Mutex } func (self *FontList) initialize(h *handler) { self.handler = h + self.preview_cache = make(map[preview_cache_key]string) self.rl = readline.New(h.lp, readline.RlInit{DontMarkPrompts: true, Prompt: "Family: "}) self.variable_data_requested_for = utils.NewSet[string](256) } @@ -89,13 +99,61 @@ func (self *FontList) draw_family_summary(start_x int, sz loop.ScreenSize) (err } } - for i, line := range lines { - if i >= int(sz.HeightCells)-1 { + y := 0 + for _, line := range lines { + if y >= int(sz.HeightCells)-1 { break } - lp.MoveCursorTo(start_x+1, i+1) + lp.MoveCursorTo(start_x+1, y+1) lp.QueueWriteString(line) + y++ } + if self.handler.text_style.Background != "" { + return self.draw_preview(start_x, y, sz) + } + return +} + +func (self *FontList) draw_preview(x, y int, sz loop.ScreenSize) (err error) { + width_cells, height_cells := int(sz.WidthCells)-x-1, int(sz.HeightCells)-y + if height_cells < 3 { + return + } + y++ + self.handler.lp.MoveCursorTo(x+1, y+1) + self.handler.lp.QueueWriteString("Preview:") + y++ + height_cells -= 2 + height_cells = min(height_cells, int(math.Ceil(100./float64(width_cells)))) + self.handler.lp.MoveCursorTo(x+1, y+1) + key := preview_cache_key{ + family: self.family_list.CurrentFamily(), width: int(sz.CellWidth) * width_cells, height: int(sz.CellHeight) * height_cells, + } + if key.family == "" { + return + } + self.preview_cache_mutex.Lock() + defer self.preview_cache_mutex.Unlock() + img_path := self.preview_cache[key] + switch img_path { + case "": + self.preview_cache[key] = "requested" + go func() { + var r map[string]string + self.handler.set_worker_error(kitty_font_backend.query("render_family_samples", map[string]any{ + "text_style": self.handler.text_style, "font_family": key.family, "width": key.width, "height": key.height, + "output_dir": self.handler.temp_dir, + }, &r)) + self.preview_cache_mutex.Lock() + defer self.preview_cache_mutex.Unlock() + self.preview_cache[key] = r["font_family"] + self.handler.lp.WakeupMainThread() + }() + return + case "requested": + return + } + self.handler.graphics_manager.display_image(0, img_path, key.width, key.height) return } diff --git a/kittens/choose_fonts/main.go b/kittens/choose_fonts/main.go index db340a8ee..90975aa71 100644 --- a/kittens/choose_fonts/main.go +++ b/kittens/choose_fonts/main.go @@ -34,8 +34,7 @@ func main() (rc int, err error) { lp.OnInitialize = func() (string, error) { lp.AllowLineWrapping(false) lp.SetWindowTitle(`Choose a font for kitty`) - h.initialize() - return "", nil + return "", h.initialize() } lp.OnWakeup = h.on_wakeup lp.OnEscapeCode = h.on_escape_code diff --git a/kittens/choose_fonts/ui.go b/kittens/choose_fonts/ui.go index 05e549745..06afb80c8 100644 --- a/kittens/choose_fonts/ui.go +++ b/kittens/choose_fonts/ui.go @@ -2,6 +2,7 @@ package choose_fonts import ( "fmt" + "os" "strconv" "sync" @@ -38,6 +39,7 @@ type handler struct { render_lines tui.RenderLines text_style TextStyle graphics_manager graphics_manager + temp_dir string listing FontList } @@ -55,12 +57,14 @@ func (h *handler) get_worker_error() error { } // Events {{{ -func (h *handler) initialize() { +func (h *handler) initialize() (err error) { h.lp.SetCursorVisible(false) h.lp.OnQueryResponse = h.on_query_response h.lp.QueryTerminal("font_size", "dpi_x", "dpi_y", "foreground", "background") h.listing.initialize(h) - h.draw_screen() + if h.temp_dir, err = os.MkdirTemp("", "kitten-choose-fonts-*"); err != nil { + return + } initialize_variable_data_cache() h.graphics_manager.initialize(h.lp) go func() { @@ -70,12 +74,18 @@ func (h *handler) initialize() { h.listing.resolved_faces_from_kitty_conf = r.Resolved_faces h.lp.WakeupMainThread() }() + h.draw_screen() + return } func (h *handler) finalize() { + if h.temp_dir != "" { + os.RemoveAll(h.temp_dir) + h.temp_dir = "" + } h.lp.SetCursorVisible(true) h.lp.SetCursorShape(loop.BLOCK_CURSOR, true) - h.graphics_manager.finalize(h.lp) + h.graphics_manager.finalize() } func (h *handler) on_query_response(key, val string, valid bool) error { @@ -107,6 +117,7 @@ func (h *handler) on_query_response(key, val string, valid bool) error { h.text_style.Foreground = val case "background": h.text_style.Background = val + return h.draw_screen() } return nil } @@ -119,6 +130,7 @@ func (h *handler) draw_screen() (err error) { h.mouse_state.ApplyHoverStyles(h.lp) h.lp.EndAtomicUpdate() }() + h.graphics_manager.clear_placements() h.lp.ClearScreen() h.lp.AllowLineWrapping(false) h.mouse_state.ClearCellRegions()