From de093c3db5c0aeac017d5dfcd3410cd4dea0ed04 Mon Sep 17 00:00:00 2001 From: Luke aka SwissalpS <161979+SwissalpS@users.noreply.github.com> Date: Sun, 5 Jan 2025 18:18:27 +0100 Subject: [PATCH] fixes and improves plugin dragdropselected (#258) * fixes and improves plugin dragdropselected - Tighter and neater code - Removed weird selection during drag - Ctrl+drag works if gnome's "ctrl to show mouse" is turned off - Added sticky mode to help against finger fatigue - Copied selections can now be placed adjacent to the original selection - Press Esc to abort a begun action - Added toggles for settings gui to enable/disable plugin and sticky mode during runtime * move isInSelection to DocView.dnd_isInSelection also adding boundry contraction algo and an alternative argument signature also renamed the selection vars for continuity throughout code * disable blinking caret during moves now caret is reliably rendered, great UX thanks @Guldoman for the hint. * update manifest.json * select pasted text after action and some comment updates * update manifest.json * add multi-selection support and some other tweaks to tidy and improve code * add custom get_selection_text - drag-items on same line are separated with spaces - also added local functions to dnd-table which is returned on load * remove stray caret when abborting drag inside selection if user drags selection but then releases mouse with cursor still in selection, there used to be an extra caret remaining. This commit removes it. * preserve selection direction * comment and typo fix in another comment * update readme at this point I think we can drop the 'basic' * return true in on_* events * disable text input during drag operations * selection restore no longer needed here since we return true in on_mouse_pressed, this is no longer needed * restore selections on esc * better to re-select anyway I'm not sure, may have been another issue, but this seems safer to also make sure there are no stray markers * comment typo * move escape key detection to command This allows users to define to another key(-combo) but it disables escape out of autocomplete suggestions. * use function predicate this ensures other commands get executed * allow clearing selection in sticky mode by clicking in empty * whitespace cleanup * add commands for toggles * check button on release just in case * clear out unused variable * comments * use Doc:position_offset same result maybe not as fast the tighter and easier to understand code is hopefully worth it * credits * [dragdropselected] tweak commands adding horrid dashes in command, makes them look prettier in command-view * [dragdropselected] add ghost of selection optionally have a ghost of selected text follow pointer. default is off and only 1 line --- README.md | 1 + manifest.json | 12 +- plugins/dragdropselected.lua | 658 ++++++++++++++++++++++++++++------- 3 files changed, 539 insertions(+), 132 deletions(-) diff --git a/README.md b/README.md index d0268e32..f082d16f 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,7 @@ to follow with `extra.follow_branch`.* *Any entries marked with an asterisk are hosted on external repositories.* + diff --git a/manifest.json b/manifest.json index c99e6018..e76e51f2 100644 --- a/manifest.json +++ b/manifest.json @@ -368,11 +368,17 @@ "version": "0.2" }, { - "description": "Provides basic drag and drop of selected text (in same document)", + "description": "Provides drag and drop of selected text (in same document)", + "version": "20230616.094245", + "path": "plugins/dragdropselected.lua", "id": "dragdropselected", "mod_version": "3", - "path": "plugins/dragdropselected.lua", - "version": "0.2" + "name": "Drag n' Drop Selected", + "tags": [ "DnD", "mouse", "copy", "duplicate", "move", "selection", "drag", "drop" ], + "extra": { + "author": "SwissalpS", + "license": "MIT" + } }, { "description": "Adds a popup that displays the curve of Penner-styled easing functions.", diff --git a/plugins/dragdropselected.lua b/plugins/dragdropselected.lua index 8f41ec35..4267966b 100644 --- a/plugins/dragdropselected.lua +++ b/plugins/dragdropselected.lua @@ -1,177 +1,577 @@ -- mod-version:3 --[[ dragdropselected.lua - provides basic drag and drop of selected text (in same document) - version: 20200627_133351 - originally by SwissalpS + provides drag and drop of selected text (in same document) + - LMB+drag selected text to move it elsewhere + - or LMB on selection and then LMB at destination (if sticky is enabled) + - hold ctrl on release to copy selection + - press escape to abort + - supports multiple selections + version: 20230616_094245 by SwissalpS + thanks to: github.com/Guldoman for his valuable inputs while I was re-writing + this plugin for lite-xl. The plugin turned out a lot better and + thus the backport for lite also turned out better. + original: 20200627_133351 by SwissalpS - TODO: use OS drag and drop events - TODO: change mouse cursor when duplicating - TODO: add dragging image + TODO: use OS drag and drop events (unlikely to happen, more important is + dragging between views of same instance.) + TODO: change mouse cursor when duplicating (requires change in cpp/SDL2) --]] -local DocView = require "core.docview" local core = require "core" +local command = require "core.command" +local common = require "core.common" +local config = require "core.config" +local DocView = require "core.docview" local keymap = require "core.keymap" local style = require "core.style" --- helper function for on_mouse_pressed to determine if mouse down is in selection --- iLine is line number where mouse down happened --- iCol is column where mouse down happened --- iSelLine1 is line number where selection starts --- iSelCol1 is column where selection starts --- iSelLine2 is line number where selection ends --- iSelCol2 is column where selection ends -local function isInSelection(iLine, iCol, iSelLine1, iSelCol1, iSelLine2, iSelCol2) - if iLine < iSelLine1 then return false end - if iLine > iSelLine2 then return false end - if (iLine == iSelLine1) and (iCol < iSelCol1) then return false end - if (iLine == iSelLine2) and (iCol > iSelCol2) then return false end +local dnd = {} + +config.plugins.dragdropselected = common.merge({ + enabled = true, + useSticky = false, + -- The config specification used by the settings gui + config_spec = { + name = "Drag n' Drop Selected", + { + label = "Enabled", + description = "Activates DnD support within same file. (ctrl to copy)", + path = "enabled", + type = "toggle", + default = true + }, + { + label = "Use Sticky Drag", + description = "Allows to click selection then click insert location.\n" + .. "No actual dragging needed.", + path = "useSticky", + type = "toggle", + default = false, + } + } +}, config.plugins.dragdropselected) + + +-- helper function to determine if mouse is in selection +-- iLine is line number where mouse is +-- iCol is column where mouse is +-- iLine1 is line number where selection starts +-- iCol1 is column where selection starts +-- iLine2 is line number where selection ends +-- iCol2 is column where selection ends +function dnd.isInSelection(iLine, iCol, iLine1, iCol1, iLine2, iCol2) + if iLine < iLine1 then return false end + if iLine > iLine2 then return false end + if (iLine == iLine1) and (iCol < iCol1) then return false end + if (iLine == iLine2) and (iCol > iCol2) then return false end return true -end -- isInSelection +end -- dnd.isInSelection --- distance between two points -local function distance(x1, y1, x2, y2) - return math.sqrt((x2-x1)^2+(y2-y1)^2) -end -local min_drag = style.code_font:get_width(" ") +function DocView:dnd_collectSelections() + self.dnd_lSelections = {} + for _, iLine1, iCol1, iLine2, iCol2, bSwap in self.doc:get_selections(true) do + -- skip empty selections (jic Doc didn't skip them) + if iLine1 ~= iLine2 or iCol1 ~= iCol2 then + self.dnd_lSelections[#self.dnd_lSelections + 1] = + { iLine1, iCol1, iLine2, iCol2, bSwap } --- override DocView:on_mouse_moved -local on_mouse_moved = DocView.on_mouse_moved -function DocView:on_mouse_moved(x, y, ...) + end + end + if 0 == #self.dnd_lSelections then + self.dnd_lSelections = nil + end + return self.dnd_lSelections +end -- DocView:dnd_collectSelections - local sCursor = nil - -- make sure we only act if previously on_mouse_pressed was in selection - if - self.bClickedIntoSelection - and - ( -- we are already dragging or we moved enough to start dragging - not self.drag_start_loc or - distance(self.drag_start_loc[1],self.drag_start_loc[2], x, y) > min_drag - ) - then - self.drag_start_loc = nil +function dnd.getSelectedText(doc) + local iPrevious = 0 + local sOut, sPart + for _, iLine1, iCol1, iLine2, iCol2 in doc:get_selections(true) do + -- skip empty markers + if iLine1 ~= iLine2 or iCol1 ~= iCol2 then + sPart = doc:get_text(iLine1, iCol1, iLine2, iCol2) + -- double check that part is not empty + if '' ~= sPart then + if 0 == iPrevious then + sOut = sPart + else + sOut = sOut .. (iPrevious == iLine1 and ' ' or '\n') .. sPart + end + iPrevious = iLine2 + end -- not empty + end -- not empty + end -- loop selections + return sOut +end -- dnd.getSelectedText - -- show that we are dragging something - sCursor = 'hand' - - -- calculate line and column for current mouse position - local iLine, iCol = self:resolve_screen_position(x, y) - local iSelLine1 = self.dragged_selection[1] - local iSelCol1 = self.dragged_selection[2] - local iSelLine2 = self.dragged_selection[3] - local iSelCol2 = self.dragged_selection[4] - self.doc:set_selection(iSelLine1, iSelCol1, iSelLine2, iSelCol2) - if not isInSelection(iLine, iCol, iSelLine1, iSelCol1, iSelLine2, iSelCol2) then - -- show cursor only if outside selection - self.doc:add_selection(iLine, iCol) + +-- checks whether given coordinates are in a selection +-- iLine, iCol are position of mouse +-- bDuplicating triggers 'exclusive' check making checked area smaller +function DocView:dnd_isInSelections(iX, iY, bDuplicating) + self.dnd_lSelections = self.dnd_lSelections or self:dnd_collectSelections() + if not self.dnd_lSelections then return nil end + + local iLine, iCol = self:resolve_screen_position(iX, iY) + if config.plugins.dragdropselected.useSticky and not self.dnd_bDragging then + -- allow user to clear selection in sticky mode by clicking in empty area + -- to the right of selection + local iX2 = self:get_line_screen_position(iLine, #self.doc.lines[iLine]) + -- this does not exactly corespond with the graphical selected area + -- it means selection can't be grabbed by the "\n" at the end + if iX2 < iX then return nil end + end + + local iLine1, iCol1, iLine2, iCol2 + local i = #self.dnd_lSelections + repeat + iLine1, iCol1, iLine2, iCol2 = table.unpack(self.dnd_lSelections[i]) + if bDuplicating then + -- adjust boundries for duplication actions + -- this allows users to duplicate selection adjacent to selection + iCol1 = iCol1 + 1 + if #self.doc.lines[iLine1] < iCol1 then + iCol1 = 1 + iLine1 = iLine1 + 1 + end + iCol2 = iCol2 - 1 + if 0 == iCol2 then + iLine2 = iLine2 - 1 + iCol2 = #self.doc.lines[iLine2] + end + end -- if duplicating + + if dnd.isInSelection(iLine, iCol, iLine1, iCol1, iLine2, iCol2) then + return self.dnd_lSelections + end + + i = i - 1 + until 0 == i + return nil +end -- DocView:dnd_isInSelections + + +-- restore selections that existed when DnD was initiated +function DocView:dnd_setSelections() + if not self.dnd_lSelections or 0 == #self.dnd_lSelections then + return + end + + local iTotal = #self.dnd_lSelections + local i = iTotal + repeat + if i == iTotal then + self.doc:set_selection(table.unpack(self.dnd_lSelections[i])) + else + self.doc:add_selection(table.unpack(self.dnd_lSelections[i])) end - -- update scroll position - self:scroll_to_line(iLine, true) - end -- if previously clicked into selection + i = i - 1 + until 0 == i +end -- DocView:dnd_setSelections - -- hand off to 'old' on_mouse_moved() - on_mouse_moved(self, x, y, ...) - -- override cursor as needed - if sCursor then self.cursor = sCursor end +-- unset stashes and flag, reset cursor +-- helper for on_mouse_released and +-- when escape is pressed during drag (or not, not worth checking) +function dnd.reset(oDocView) + if not oDocView then + oDocView = core.active_view + if not oDocView:is(DocView) then return end + end + + if dnd.oGhost then dnd.oGhost:hide() end + if nil ~= oDocView.dnd_bBlink then + config.disable_blink = oDocView.dnd_bBlink + end + oDocView.dnd_lSelections = nil + oDocView.dnd_bDragging = nil + oDocView.dnd_bBlink = nil + oDocView.cursor = 'ibeam' + oDocView.dnd_sText = nil +end -- dnd.reset + + +local on_mouse_moved = DocView.on_mouse_moved +function DocView:on_mouse_moved(x, y, ...) + if not config.plugins.dragdropselected.enabled or not self.dnd_sText then + return on_mouse_moved(self, x, y, ...) + end + + -- not sure we need to do this or if we better not + DocView.super.on_mouse_moved(self, x, y, ...) + + if self.dnd_bDragging then + -- remove last caret showing insert location + self.doc:remove_selection(self.doc.last_selection) + -- move ghost, if available and activated + if dnd.oGhost and config.plugins.dragdropselected.useGhost then + dnd.oGhost:set_position(x + 22, y + 11) + if self.dnd_bMouseLeft then + self.dnd_bMouseLeft = nil + dnd.oGhost:show() + end + end + else + self.dnd_bDragging = true + -- show that we are dragging something + self.cursor = 'hand' + -- make sure selections are marked + self:dnd_setSelections() + -- initiate ghost, if available and activated + if dnd.oGhost and config.plugins.dragdropselected.useGhost then + dnd.oGhost:set_position(x + 22, y + 11) + dnd.oGhost:show() + end + end + -- calculate line and column for current mouse position + local iLine, iCol = self:resolve_screen_position(x, y) + -- show insert location + self.doc:add_selection(iLine, iCol) + -- update scroll position, if needed + self:scroll_to_line(iLine, true) + return true end -- DocView:on_mouse_moved --- override DocView:on_mouse_pressed + local on_mouse_pressed = DocView.on_mouse_pressed function DocView:on_mouse_pressed(button, x, y, clicks) local caught = DocView.super.on_mouse_pressed(self, button, x, y, clicks) if caught then return caught end - -- no need to proceed if not left button or has no selection - if - ('left' ~= button) - or (not self.doc:has_selection()) - or (1 < clicks) + + -- sticky mode support + if self.dnd_bDragging then + return true + end + + -- no need to proceed if: not enabled, not left button, no selection + -- or if this is a multi-click event + -- NOTE: can't use self.doc:has_selection(), that only looks at last one + if not config.plugins.dragdropselected.enabled + or 'left' ~= button + or 1 < clicks + or not self:dnd_isInSelections(x, y) then - return on_mouse_pressed(self, button, x, y, clicks) + dnd.reset(self) + return on_mouse_pressed(self, button, x, y, clicks) end - -- convert pixel coordinates to line and column coordinates - local iLine, iCol = self:resolve_screen_position(x, y) - -- get selection coordinates - local iSelLine1, iSelCol1, iSelLine2, iSelCol2 = self.doc:get_selection(true) - -- set flag for on_mouse_released and on_mouse_moved() methods to detect dragging - self.bClickedIntoSelection = isInSelection(iLine, iCol, iSelLine1, iSelCol1, - iSelLine2, iSelCol2) - if self.bClickedIntoSelection then - self.drag_start_loc = { x, y } - -- stash selection for inserting later - self.sDraggedText = self.doc:get_text(self.doc:get_selection()) - self.dragged_selection = { iSelLine1, iSelCol1, iSelLine2, iSelCol2 } - else - self.bClickedIntoSelection = nil - self.dragged_selection = nil - -- let 'old' on_mouse_pressed() do whatever it needs to do - on_mouse_pressed(self, button, x, y, clicks) + + -- stash selection for inserting later + self.dnd_sText = dnd.getSelectedText(self.doc) + -- prepare ghost, if available and used + if dnd.oGhost and config.plugins.dragdropselected.useGhost then + dnd.oGhost:set_label(dnd.split4ghost(self.dnd_sText)) end + -- disable blinking caret and stash user setting + self.dnd_bBlink = config.disable_blink + config.disable_blink = true + return true end -- DocView:on_mouse_pressed --- override DocView:on_mouse_released() + local on_mouse_released = DocView.on_mouse_released function DocView:on_mouse_released(button, x, y) + -- nothing to do if: not enabled, + -- never clicked into selection or not left button + if not config.plugins.dragdropselected.enabled + or 'left' ~= button + or not self.dnd_sText + then + return on_mouse_released(self, button, x, y) + end + local iLine, iCol = self:resolve_screen_position(x, y) - if self.bClickedIntoSelection then - local iSelLine1, iSelCol1, iSelLine2, iSelCol2 = table.unpack(self.dragged_selection) - if - not self.drag_start_loc - and - not isInSelection(iLine, iCol, iSelLine1, iSelCol1, iSelLine2, iSelCol2) - then - -- insert stashed selected text at current position - if iLine < iSelLine1 or (iLine == iSelLine1 and iCol < iSelCol1) then - -- delete first - self.doc:set_selection(iSelLine1, iSelCol1, iSelLine2, iSelCol2) - if not keymap.modkeys['ctrl'] then - self.doc:delete_to(0) - end - self.doc:set_selection(iLine, iCol) - self.doc:text_input(self.sDraggedText) - else - -- insert first - self.doc:set_selection(iLine, iCol) - self.doc:text_input(self.sDraggedText) - self.doc:set_selection(iSelLine1, iSelCol1, iSelLine2, iSelCol2) - if not keymap.modkeys['ctrl'] then - self.doc:delete_to(0) - end - self.doc:set_selection(iLine, iCol) - end - elseif self.drag_start_loc then - -- deselect only if the drag never happened + if not self.dnd_bDragging then + if not config.plugins.dragdropselected.useSticky then + -- not using sticky -> clear selection self.doc:set_selection(iLine, iCol) + dnd.reset(self) end - -- unset stash and flag(s) TODO: - self.sDraggedText = '' - self.bClickedIntoSelection = nil + return on_mouse_released(self, button, x, y) end - -- hand over to old handler - on_mouse_released(self, button, x, y) - + local bDuplicating = keymap.modkeys['ctrl'] + if self:dnd_isInSelections(x, y, bDuplicating) then + -- drag aborted by releasing mouse inside selection + self.doc:remove_selection(self.doc.last_selection) + else + -- have doc handle selection updates + self.doc:insert(iLine, iCol, self.dnd_sText) + -- add a marker so we know where to start selecting pasted text + self.doc:add_selection(iLine, iCol) + if not bDuplicating then + self.doc:delete_to(0) + end + -- get new location of inserted text + iLine, iCol = self.doc:get_selection_idx(self.doc.last_selection, true) + -- finally select inserted text + self.doc:set_selection( + iLine, iCol, self.doc:position_offset(iLine, iCol, #self.dnd_sText)) + end + -- unset stashes and flag + dnd.reset(self) + return on_mouse_released(self, button, x, y) end -- DocView:on_mouse_released --- override DocView:draw_caret() + local draw_caret = DocView.draw_caret function DocView:draw_caret(x, y) - if self.bClickedIntoSelection then - local iLine, iCol = self:resolve_screen_position(x, y) + if self.dnd_sText and config.plugins.dragdropselected.enabled then -- don't show carets inside selections - if - isInSelection( - iLine, iCol, - self.dragged_selection[1], self.dragged_selection[2], - self.dragged_selection[3], self.dragged_selection[4] - ) - then + if self:dnd_isInSelections(x, y, true) then return end end - draw_caret(self, x, y) + return draw_caret(self, x, y) end -- DocView:draw_caret() + + +-- disable text_input during drag operations +local on_text_input = DocView.on_text_input +function DocView:on_text_input(text) + if self.dnd_bDragging then + return true + end + return on_text_input(self, text) +end -- DocView:on_text_input + + +function dnd.abort(oDocView) + if not config.plugins.dragdropselected.enabled then return end + + if oDocView.dnd_bDragging then + -- ensure there are no stray markers by re-selecting + oDocView:dnd_setSelections() + end + dnd.reset(oDocView) +end -- dnd.abort + + +function dnd.abortPredicate() + if not config.plugins.dragdropselected.enabled + or not core.active_view:is(DocView) + or not core.active_view.dnd_bDragging + then return false end + + return true, core.active_view +end -- dnd.abortPredicate + + +function dnd.showStatus(s) + if not core.status_view then return end + + local tS = style.log['INFO'] + core.status_view:show_message(tS.icon, tS.color, s) +end -- dnd.showStatus + + +function dnd.toggleEnabled() + config.plugins.dragdropselected.enabled = + not config.plugins.dragdropselected.enabled + + dnd.showStatus("Drag n' Drop is " + .. (config.plugins.dragdropselected.enabled and 'en' or 'dis') + .. 'abled') + +end -- dnd.toggleEnabled + + +function dnd.toggleSticky() + config.plugins.dragdropselected.useSticky = + not config.plugins.dragdropselected.useSticky + + dnd.showStatus('Sticky mode is ' + .. (config.plugins.dragdropselected.useSticky and 'en' or 'dis') + .. 'abled') + +end -- dnd.toggleSticky + + +command.add(nil, { + ['drag-drop-selected:toggle-enabled'] = dnd.toggleEnabled, + ['drag-drop-selected:toggle-sticky'] = dnd.toggleSticky +}) + +command.add(dnd.abortPredicate, { ['drag-drop-selected:abort'] = dnd.abort }) +keymap.add({ ['escape'] = 'drag-drop-selected:abort' }) + + +local hasWidgets, Widget = pcall(require, "libraries.widget") +if not hasWidgets then return dnd end + + +-- add ghost settings and config +config.plugins.dragdropselected = common.merge({ + -- use ghost + useGhost = false, + -- maximum amount of lines to show in ghost + maxGhostLines = 1 +}, config.plugins.dragdropselected) +-- we need to insert this way as common.merge can't handle this +table.insert(config.plugins.dragdropselected.config_spec, { + label = "Use Ghost", + description = "Cursor has ghost of dragged selection attached.", + path = "useGhost", + type = "toggle", + default = true +}) +table.insert(config.plugins.dragdropselected.config_spec, { + label = "Maximum Ghost Lines", + description = "Maximum amount of lines to show in ghost.", + path = "maxGhostLines", + type = "number", + default = 1, + min = 1, + step = 1 +}) + + +---Split string sIn for use with ghost. Depending on how many lines user wants, +---return a string or a list of strings. +---@param sIn? string +---@return string | table +function dnd.split4ghost(sIn) + if 'string' ~= type(sIn) then return '' end + + local iMaxLines = math.max(1, config.plugins.dragdropselected.maxGhostLines) + if 1 == iMaxLines then return sIn end + + local lLines = {} + local iLast, iNext = 0 + repeat + iNext = string.find(sIn, '\n', iLast, true) + if not iNext then + lLines[#lLines + 1] = string.sub(sIn, iLast, -1) + break + end + + lLines[#lLines + 1] = string.sub(sIn, iLast, iNext) + if #lLines >= iMaxLines then + -- indicate to user that there is more selected than can be seen + lLines[#lLines + 1] = '…' + break + end + iLast = iNext + 1 + until false + + return lLines +end -- dnd.split4ghost + + +local docView_on_mouse_left = DocView.on_mouse_left +function DocView:on_mouse_left() + if dnd.oGhost and dnd.oGhost:is_visible() then + dnd.oGhost:hide() + self.dnd_bMouseLeft = true + end + return docView_on_mouse_left(self) +end -- DocView:on_mouse_left + + +---@class Ghost : widget +local Ghost = Widget:extend() + +---Constructor +function Ghost:new() + Ghost.super.new(self, nil, true) + + self.border = { width = 0 } + self.clickable = false + self.custom_size = { x = 0, y = 0 } + self.draggable = false + self.font = 'code_font' + local r, g, b, a = table.unpack(style.text) + self.foreground_color = { r, g, b, math.floor(a * .77 + .5) } + self.render_background = false + self.scrollable = false + self.type_name = 'dragdropselected.ghostWidget' +end -- Ghost:new + + +-- Ignore mouse movements. +function Ghost.on_mouse_moved() return false end + + +---@param width? integer +---@param height? integer +function Ghost:set_size(width, height) + Ghost.super.set_size(self, width, height) + self.custom_size.x = self.size.x + self.custom_size.y = self.size.y +end -- Ghost:set_size + + +---Set the label text and recalculate the widget size. +---@param text string | widget.styledtext +function Ghost:set_label(text) + Ghost.super.set_label(self, text) + + local font = self:get_font() + + if self.custom_size.x <= 0 then + if type(text) == "table" then + self.size.x, self.size.y = self:draw_styled_text(text, 0, 0, true) + else + self.size.x = font:get_width(self.label) + self.size.y = font:get_height() + end + end +end -- Ghost:set_label + + +function Ghost:update() + if not Ghost.super.update(self) then return false end + + if self.custom_size.x <= 0 then + -- update the size + self:set_label(self.label) + end + + return true +end -- Ghost:update + + +function Ghost:draw() + if not self:is_visible() then return false end + + if type(self.label) == "table" then + self:draw_styled_text(self.label, self.position.x, self.position.y) + else + renderer.draw_text( + self:get_font(), + self.label, + self.position.x, + self.position.y, + self.foreground_color or style.text + ) + end + + return true +end -- Ghost:draw + + +---@type Ghost +dnd.oGhost = Ghost() + + +-- Toggle ghost usage and show status. +function dnd.toggleGhost() + config.plugins.dragdropselected.useGhost = + not config.plugins.dragdropselected.useGhost + + dnd.showStatus('Ghost is ' + .. (config.plugins.dragdropselected.useGhost and 'en' or 'dis') + .. 'abled') + +end -- dnd.toggleGhost + + +command.add(nil, { + ['drag-drop-selected:toggle-ghost'] = dnd.toggleGhost +}) + +return dnd +