From 3165a47579d1b1b66288bda90ffd868f09b152c5 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 5 May 2026 10:49:56 +0530 Subject: [PATCH] More work on the DnD kitten --- kittens/dnd/drag.go | 58 +++++++++++++++++++++++++++++++++++---- kitty/dnd.c | 4 +-- tools/icons/file-types.go | 2 +- tools/tui/loop/api.go | 2 ++ 4 files changed, 57 insertions(+), 9 deletions(-) diff --git a/kittens/dnd/drag.go b/kittens/dnd/drag.go index e9d8cecc7..3fbeed2fd 100644 --- a/kittens/dnd/drag.go +++ b/kittens/dnd/drag.go @@ -13,6 +13,7 @@ import ( "github.com/emmansun/base64" "github.com/kovidgoyal/imaging" + "github.com/kovidgoyal/kitty/tools/icons" "github.com/kovidgoyal/kitty/tools/tui/loop" "github.com/kovidgoyal/kitty/tools/utils" "github.com/kovidgoyal/kitty/tools/utils/streaming_base64" @@ -53,11 +54,15 @@ type drag_status struct { } func find_drag_image(drag_sources map[string]*drag_source) image.Image { + all_paths := []string{} for mime, ds := range drag_sources { - if strings.HasPrefix(mime, "image/") && ds.path != "" { - q, err := imaging.Open(ds.path) - if err == nil { - return q + if ds.path != "" { + all_paths = append(all_paths, ds.path) + if strings.HasPrefix(mime, "image/") { + q, err := imaging.Open(ds.path) + if err == nil { + return q + } } } } @@ -67,6 +72,7 @@ func find_drag_image(drag_sources map[string]*drag_source) image.Image { for _, path := range q { if path != "" { uri_list = append(uri_list, path) + all_paths = append(all_paths, path) } } } @@ -77,7 +83,47 @@ func find_drag_image(drag_sources map[string]*drag_source) image.Image { return q } } - // TODO: Try to generate an image based preview using the machinery from the choose-files kitten + for _, path := range all_paths { + _ = path + // TODO: Try to generate an image based preview using the machinery from the choose-files kitten + } + return nil +} + +func (dnd *dnd) set_drag_image_text() (err error) { + icon := "" + from_path := func(path string) bool { + if st, err := os.Lstat(path); err == nil { + ans := strings.TrimSpace(icons.IconForFileWithMode(path, st.Mode(), true)) + if ans != "" { + icon = ans + return true + } + } + return false + } + for _, ds := range dnd.drag_sources { + if ds.path != "" && from_path(ds.path) { + break + } + } + if icon == "" { + if ds := dnd.drag_sources["text/uri-list"]; ds != nil && len(ds.data) > 0 { + if q, err := parse_uri_list(string(ds.data)); err == nil { + for _, path := range q { + if path != "" && from_path(path) { + break + } + } + } + } + } + if icon != "" { + cmd := DC{Type: 'p', X: -1, Xp: 6, Yp: 1, Payload: []byte(icon)} + dnd.lp.QueueDnDData(cmd) + cmd.Payload = nil + dnd.lp.QueueDnDData(cmd) + } return nil } @@ -87,7 +133,7 @@ func (dnd *dnd) set_drag_image() (err error) { img = find_drag_image(dnd.drag_sources) } if img == nil { - return + return dnd.set_drag_image_text() } num_channels := utils.IfElse(imaging.IsOpaque(img), 3, 4) sz := dnd.opts.DragThumbnailSize diff --git a/kitty/dnd.c b/kitty/dnd.c index 2ec0d7736..277e82161 100644 --- a/kitty/dnd.c +++ b/kitty/dnd.c @@ -1357,8 +1357,8 @@ drag_add_image(Window *w, unsigned idx, int fmt, int width, int height, int opac img.opacity = opacity; base64_init_stream_decoder(&img.base64_state); } - if (img.capacity < sz + img.sz) { - size_t newcap = MAX(img.capacity * 2, sz + img.sz); + if (img.capacity < MAX(32, sz + img.sz)) { + size_t newcap = MAX(img.capacity * 2, MAX(32, sz + img.sz)); uint8_t *tmp = realloc(img.data, newcap); if (!tmp) abrt(ENOMEM); img.data = tmp; diff --git a/tools/icons/file-types.go b/tools/icons/file-types.go index 213dd6da0..ec77b4d7d 100644 --- a/tools/icons/file-types.go +++ b/tools/icons/file-types.go @@ -1108,7 +1108,7 @@ func IconForFileWithMode(path string, mode fs.FileMode, follow_symlinks bool) st return string(FOLDER) case fs.ModeSymlink: if follow_symlinks { - if dest, err := os.Readlink(path); err == nil { + if dest, err := filepath.EvalSymlinks(path); err == nil { if st, err := os.Stat(dest); err == nil { if st.IsDir() { return string(SYMLINK_TO_DIR) diff --git a/tools/tui/loop/api.go b/tools/tui/loop/api.go index 1d102daa3..335fcf3bb 100644 --- a/tools/tui/loop/api.go +++ b/tools/tui/loop/api.go @@ -699,6 +699,8 @@ func (self *Loop) QueueDnDData(cmd DndCommand) IdType { dest := make([]byte, base64.RawStdEncoding.EncodedLen(len(payload))) base64.RawStdEncoding.Encode(dest, utils.UnsafeStringToBytes(payload)) payload = utils.UnsafeBytesToString(dest) + } else { + payload = string(cmd.Payload) } const chunk_size = 4096 var ans IdType