Merge pull request #303 from jgmdev/treeview-contextmenu
Added context menu to treeview.
This commit is contained in:
commit
d10865bcc4
|
@ -0,0 +1,218 @@
|
||||||
|
local core = require "core"
|
||||||
|
local common = require "core.common"
|
||||||
|
local command = require "core.command"
|
||||||
|
local config = require "core.config"
|
||||||
|
local keymap = require "core.keymap"
|
||||||
|
local style = require "core.style"
|
||||||
|
local Object = require "core.object"
|
||||||
|
|
||||||
|
local border_width = 1
|
||||||
|
local divider_width = 1
|
||||||
|
local DIVIDER = {}
|
||||||
|
|
||||||
|
local ContextMenu = Object:extend()
|
||||||
|
|
||||||
|
ContextMenu.DIVIDER = DIVIDER
|
||||||
|
|
||||||
|
function ContextMenu:new()
|
||||||
|
self.itemset = {}
|
||||||
|
self.show_context_menu = false
|
||||||
|
self.selected = -1
|
||||||
|
self.height = 0
|
||||||
|
self.position = { x = 0, y = 0 }
|
||||||
|
end
|
||||||
|
|
||||||
|
local function get_item_size(item)
|
||||||
|
local lw, lh
|
||||||
|
if item == DIVIDER then
|
||||||
|
lw = 0
|
||||||
|
lh = divider_width
|
||||||
|
else
|
||||||
|
lw = style.font:get_width(item.text)
|
||||||
|
if item.info then
|
||||||
|
lw = lw + style.padding.x + style.font:get_width(item.info)
|
||||||
|
end
|
||||||
|
lh = style.font:get_height() + style.padding.y
|
||||||
|
end
|
||||||
|
return lw, lh
|
||||||
|
end
|
||||||
|
|
||||||
|
function ContextMenu:register(predicate, items)
|
||||||
|
if type(predicate) == "string" then
|
||||||
|
predicate = require(predicate)
|
||||||
|
end
|
||||||
|
if type(predicate) == "table" then
|
||||||
|
local class = predicate
|
||||||
|
predicate = function() return core.active_view:is(class) end
|
||||||
|
end
|
||||||
|
|
||||||
|
local width, height = 0, 0 --precalculate the size of context menu
|
||||||
|
for i, item in ipairs(items) do
|
||||||
|
if item ~= DIVIDER then
|
||||||
|
item.info = keymap.reverse_map[item.command]
|
||||||
|
end
|
||||||
|
local lw, lh = get_item_size(item)
|
||||||
|
width = math.max(width, lw)
|
||||||
|
height = height + lh
|
||||||
|
end
|
||||||
|
width = width + style.padding.x * 2
|
||||||
|
items.width, items.height = width, height
|
||||||
|
table.insert(self.itemset, { predicate = predicate, items = items })
|
||||||
|
end
|
||||||
|
|
||||||
|
function ContextMenu:show(x, y)
|
||||||
|
self.items = nil
|
||||||
|
local items_list = { width = 0, height = 0 }
|
||||||
|
for _, items in ipairs(self.itemset) do
|
||||||
|
if items.predicate(x, y) then
|
||||||
|
items_list.width = math.max(items_list.width, items.items.width)
|
||||||
|
items_list.height = items_list.height + items.items.height
|
||||||
|
for _, subitems in ipairs(items.items) do
|
||||||
|
table.insert(items_list, subitems)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if #items_list > 0 then
|
||||||
|
self.items = items_list
|
||||||
|
local w, h = self.items.width, self.items.height
|
||||||
|
|
||||||
|
-- by default the box is opened on the right and below
|
||||||
|
if x + w >= core.root_view.size.x then
|
||||||
|
x = x - w
|
||||||
|
end
|
||||||
|
if y + h >= core.root_view.size.y then
|
||||||
|
y = y - h
|
||||||
|
end
|
||||||
|
|
||||||
|
self.position.x, self.position.y = x, y
|
||||||
|
self.show_context_menu = true
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function ContextMenu:hide()
|
||||||
|
self.show_context_menu = false
|
||||||
|
self.items = nil
|
||||||
|
self.selected = -1
|
||||||
|
self.height = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
function ContextMenu:each_item()
|
||||||
|
local x, y, w = self.position.x, self.position.y, self.items.width
|
||||||
|
local oy = y
|
||||||
|
return coroutine.wrap(function()
|
||||||
|
for i, item in ipairs(self.items) do
|
||||||
|
local _, lh = get_item_size(item)
|
||||||
|
if y - oy > self.height then break end
|
||||||
|
coroutine.yield(i, item, x, y, w, lh)
|
||||||
|
y = y + lh
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
function ContextMenu:on_mouse_moved(px, py)
|
||||||
|
if not self.show_context_menu then return end
|
||||||
|
|
||||||
|
self.selected = -1
|
||||||
|
for i, item, x, y, w, h in self:each_item() do
|
||||||
|
if px > x and px <= x + w and py > y and py <= y + h then
|
||||||
|
self.selected = i
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if self.selected >= 0 then
|
||||||
|
core.request_cursor("arrow")
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function ContextMenu:on_selected(item)
|
||||||
|
if type(item.command) == "string" then
|
||||||
|
command.perform(item.command)
|
||||||
|
else
|
||||||
|
item.command()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function ContextMenu:on_mouse_pressed(button, x, y, clicks)
|
||||||
|
local selected = (self.items or {})[self.selected]
|
||||||
|
local caught = false
|
||||||
|
|
||||||
|
self:hide()
|
||||||
|
if button == "left" then
|
||||||
|
if selected then
|
||||||
|
self:on_selected(selected)
|
||||||
|
caught = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if button == "right" then
|
||||||
|
caught = self:show(x, y)
|
||||||
|
end
|
||||||
|
return caught
|
||||||
|
end
|
||||||
|
|
||||||
|
-- copied from core.docview
|
||||||
|
function ContextMenu:move_towards(t, k, dest, rate)
|
||||||
|
if type(t) ~= "table" then
|
||||||
|
return self:move_towards(self, t, k, dest, rate)
|
||||||
|
end
|
||||||
|
local val = t[k]
|
||||||
|
if not config.transitions or math.abs(val - dest) < 0.5 then
|
||||||
|
t[k] = dest
|
||||||
|
else
|
||||||
|
rate = rate or 0.5
|
||||||
|
if config.fps ~= 60 or config.animation_rate ~= 1 then
|
||||||
|
local dt = 60 / config.fps
|
||||||
|
rate = 1 - common.clamp(1 - rate, 1e-8, 1 - 1e-8)^(config.animation_rate * dt)
|
||||||
|
end
|
||||||
|
t[k] = common.lerp(val, dest, rate)
|
||||||
|
end
|
||||||
|
if val ~= dest then
|
||||||
|
core.redraw = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function ContextMenu:update()
|
||||||
|
if self.show_context_menu then
|
||||||
|
self:move_towards("height", self.items.height)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function ContextMenu:draw()
|
||||||
|
if not self.show_context_menu then return end
|
||||||
|
core.root_view:defer_draw(self.draw_context_menu, self)
|
||||||
|
end
|
||||||
|
|
||||||
|
function ContextMenu:draw_context_menu()
|
||||||
|
if not self.items then return end
|
||||||
|
local bx, by, bw, bh = self.position.x, self.position.y, self.items.width, self.height
|
||||||
|
|
||||||
|
renderer.draw_rect(
|
||||||
|
bx - border_width,
|
||||||
|
by - border_width,
|
||||||
|
bw + (border_width * 2),
|
||||||
|
bh + (border_width * 2),
|
||||||
|
style.divider
|
||||||
|
)
|
||||||
|
renderer.draw_rect(bx, by, bw, bh, style.background3)
|
||||||
|
|
||||||
|
for i, item, x, y, w, h in self:each_item() do
|
||||||
|
if item == DIVIDER then
|
||||||
|
renderer.draw_rect(x, y, w, h, style.caret)
|
||||||
|
else
|
||||||
|
if i == self.selected then
|
||||||
|
renderer.draw_rect(x, y, w, h, style.selection)
|
||||||
|
end
|
||||||
|
|
||||||
|
common.draw_text(style.font, style.text, item.text, "left", x + style.padding.x, y, w, h)
|
||||||
|
if item.info then
|
||||||
|
common.draw_text(style.font, style.dim, item.info, "right", x, y, w - style.padding.x, h)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return ContextMenu
|
|
@ -1,223 +1,10 @@
|
||||||
-- mod-version:1 -- lite-xl 1.16
|
-- mod-version:1 -- lite-xl 1.16
|
||||||
local core = require "core"
|
local core = require "core"
|
||||||
local common = require "core.common"
|
|
||||||
local config = require "core.config"
|
|
||||||
local command = require "core.command"
|
local command = require "core.command"
|
||||||
local keymap = require "core.keymap"
|
local keymap = require "core.keymap"
|
||||||
local style = require "core.style"
|
local ContextMenu = require "core.contextmenu"
|
||||||
local Object = require "core.object"
|
|
||||||
local RootView = require "core.rootview"
|
local RootView = require "core.rootview"
|
||||||
|
|
||||||
local border_width = 1
|
|
||||||
local divider_width = 1
|
|
||||||
local DIVIDER = {}
|
|
||||||
|
|
||||||
local ContextMenu = Object:extend()
|
|
||||||
|
|
||||||
ContextMenu.DIVIDER = DIVIDER
|
|
||||||
|
|
||||||
function ContextMenu:new()
|
|
||||||
self.itemset = {}
|
|
||||||
self.show_context_menu = false
|
|
||||||
self.selected = -1
|
|
||||||
self.height = 0
|
|
||||||
self.position = { x = 0, y = 0 }
|
|
||||||
end
|
|
||||||
|
|
||||||
local function get_item_size(item)
|
|
||||||
local lw, lh
|
|
||||||
if item == DIVIDER then
|
|
||||||
lw = 0
|
|
||||||
lh = divider_width
|
|
||||||
else
|
|
||||||
lw = style.font:get_width(item.text)
|
|
||||||
if item.info then
|
|
||||||
lw = lw + style.padding.x + style.font:get_width(item.info)
|
|
||||||
end
|
|
||||||
lh = style.font:get_height() + style.padding.y
|
|
||||||
end
|
|
||||||
return lw, lh
|
|
||||||
end
|
|
||||||
|
|
||||||
function ContextMenu:register(predicate, items)
|
|
||||||
if type(predicate) == "string" then
|
|
||||||
predicate = require(predicate)
|
|
||||||
end
|
|
||||||
if type(predicate) == "table" then
|
|
||||||
local class = predicate
|
|
||||||
predicate = function() return core.active_view:is(class) end
|
|
||||||
end
|
|
||||||
|
|
||||||
local width, height = 0, 0 --precalculate the size of context menu
|
|
||||||
for i, item in ipairs(items) do
|
|
||||||
if item ~= DIVIDER then
|
|
||||||
item.info = keymap.reverse_map[item.command]
|
|
||||||
end
|
|
||||||
local lw, lh = get_item_size(item)
|
|
||||||
width = math.max(width, lw)
|
|
||||||
height = height + lh
|
|
||||||
end
|
|
||||||
width = width + style.padding.x * 2
|
|
||||||
items.width, items.height = width, height
|
|
||||||
table.insert(self.itemset, { predicate = predicate, items = items })
|
|
||||||
end
|
|
||||||
|
|
||||||
function ContextMenu:show(x, y)
|
|
||||||
self.items = nil
|
|
||||||
local items_list = { width = 0, height = 0 }
|
|
||||||
for _, items in ipairs(self.itemset) do
|
|
||||||
if items.predicate(x, y) then
|
|
||||||
items_list.width = math.max(items_list.width, items.items.width)
|
|
||||||
items_list.height = items_list.height + items.items.height
|
|
||||||
for _, subitems in ipairs(items.items) do
|
|
||||||
table.insert(items_list, subitems)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if #items_list > 0 then
|
|
||||||
self.items = items_list
|
|
||||||
local w, h = self.items.width, self.items.height
|
|
||||||
|
|
||||||
-- by default the box is opened on the right and below
|
|
||||||
if x + w >= core.root_view.size.x then
|
|
||||||
x = x - w
|
|
||||||
end
|
|
||||||
if y + h >= core.root_view.size.y then
|
|
||||||
y = y - h
|
|
||||||
end
|
|
||||||
|
|
||||||
self.position.x, self.position.y = x, y
|
|
||||||
self.show_context_menu = true
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
function ContextMenu:hide()
|
|
||||||
self.show_context_menu = false
|
|
||||||
self.items = nil
|
|
||||||
self.selected = -1
|
|
||||||
self.height = 0
|
|
||||||
end
|
|
||||||
|
|
||||||
function ContextMenu:each_item()
|
|
||||||
local x, y, w = self.position.x, self.position.y, self.items.width
|
|
||||||
local oy = y
|
|
||||||
return coroutine.wrap(function()
|
|
||||||
for i, item in ipairs(self.items) do
|
|
||||||
local _, lh = get_item_size(item)
|
|
||||||
if y - oy > self.height then break end
|
|
||||||
coroutine.yield(i, item, x, y, w, lh)
|
|
||||||
y = y + lh
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
function ContextMenu:on_mouse_moved(px, py)
|
|
||||||
if not self.show_context_menu then return end
|
|
||||||
|
|
||||||
self.selected = -1
|
|
||||||
for i, item, x, y, w, h in self:each_item() do
|
|
||||||
if px > x and px <= x + w and py > y and py <= y + h then
|
|
||||||
self.selected = i
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if self.selected >= 0 then
|
|
||||||
core.request_cursor("arrow")
|
|
||||||
end
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
function ContextMenu:on_selected(item)
|
|
||||||
if type(item.command) == "string" then
|
|
||||||
command.perform(item.command)
|
|
||||||
else
|
|
||||||
item.command()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function ContextMenu:on_mouse_pressed(button, x, y, clicks)
|
|
||||||
local selected = (self.items or {})[self.selected]
|
|
||||||
local caught = false
|
|
||||||
|
|
||||||
self:hide()
|
|
||||||
if button == "left" then
|
|
||||||
if selected then
|
|
||||||
self:on_selected(selected)
|
|
||||||
caught = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if button == "right" then
|
|
||||||
caught = self:show(x, y)
|
|
||||||
end
|
|
||||||
return caught
|
|
||||||
end
|
|
||||||
|
|
||||||
-- copied from core.docview
|
|
||||||
function ContextMenu:move_towards(t, k, dest, rate)
|
|
||||||
if type(t) ~= "table" then
|
|
||||||
return self:move_towards(self, t, k, dest, rate)
|
|
||||||
end
|
|
||||||
local val = t[k]
|
|
||||||
if not config.transitions or math.abs(val - dest) < 0.5 then
|
|
||||||
t[k] = dest
|
|
||||||
else
|
|
||||||
rate = rate or 0.5
|
|
||||||
if config.fps ~= 60 or config.animation_rate ~= 1 then
|
|
||||||
local dt = 60 / config.fps
|
|
||||||
rate = 1 - common.clamp(1 - rate, 1e-8, 1 - 1e-8)^(config.animation_rate * dt)
|
|
||||||
end
|
|
||||||
t[k] = common.lerp(val, dest, rate)
|
|
||||||
end
|
|
||||||
if val ~= dest then
|
|
||||||
core.redraw = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function ContextMenu:update()
|
|
||||||
if self.show_context_menu then
|
|
||||||
self:move_towards("height", self.items.height)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function ContextMenu:draw()
|
|
||||||
if not self.show_context_menu then return end
|
|
||||||
core.root_view:defer_draw(self.draw_context_menu, self)
|
|
||||||
end
|
|
||||||
|
|
||||||
function ContextMenu:draw_context_menu()
|
|
||||||
if not self.items then return end
|
|
||||||
local bx, by, bw, bh = self.position.x, self.position.y, self.items.width, self.height
|
|
||||||
|
|
||||||
renderer.draw_rect(
|
|
||||||
bx - border_width,
|
|
||||||
by - border_width,
|
|
||||||
bw + (border_width * 2),
|
|
||||||
bh + (border_width * 2),
|
|
||||||
style.divider
|
|
||||||
)
|
|
||||||
renderer.draw_rect(bx, by, bw, bh, style.background3)
|
|
||||||
|
|
||||||
for i, item, x, y, w, h in self:each_item() do
|
|
||||||
if item == DIVIDER then
|
|
||||||
renderer.draw_rect(x, y, w, h, style.caret)
|
|
||||||
else
|
|
||||||
if i == self.selected then
|
|
||||||
renderer.draw_rect(x, y, w, h, style.selection)
|
|
||||||
end
|
|
||||||
|
|
||||||
common.draw_text(style.font, style.text, item.text, "left", x + style.padding.x, y, w, h)
|
|
||||||
if item.info then
|
|
||||||
common.draw_text(style.font, style.dim, item.info, "right", x, y, w - style.padding.x, h)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local menu = ContextMenu()
|
local menu = ContextMenu()
|
||||||
local on_view_mouse_pressed = RootView.on_view_mouse_pressed
|
local on_view_mouse_pressed = RootView.on_view_mouse_pressed
|
||||||
local on_mouse_moved = RootView.on_mouse_moved
|
local on_mouse_moved = RootView.on_mouse_moved
|
||||||
|
@ -260,10 +47,10 @@ if require("plugins.scale") then
|
||||||
{ text = "Font +", command = "scale:increase" },
|
{ text = "Font +", command = "scale:increase" },
|
||||||
{ text = "Font -", command = "scale:decrease" },
|
{ text = "Font -", command = "scale:decrease" },
|
||||||
{ text = "Font Reset", command = "scale:reset" },
|
{ text = "Font Reset", command = "scale:reset" },
|
||||||
DIVIDER,
|
ContextMenu.DIVIDER,
|
||||||
{ text = "Find", command = "find-replace:find" },
|
{ text = "Find", command = "find-replace:find" },
|
||||||
{ text = "Replace", command = "find-replace:replace" },
|
{ text = "Replace", command = "find-replace:replace" },
|
||||||
DIVIDER,
|
ContextMenu.DIVIDER,
|
||||||
{ text = "Find Pattern", command = "find-replace:find-pattern" },
|
{ text = "Find Pattern", command = "find-replace:find-pattern" },
|
||||||
{ text = "Replace Pattern", command = "find-replace:replace-pattern" },
|
{ text = "Replace Pattern", command = "find-replace:replace-pattern" },
|
||||||
})
|
})
|
||||||
|
|
|
@ -6,9 +6,12 @@ local config = require "core.config"
|
||||||
local keymap = require "core.keymap"
|
local keymap = require "core.keymap"
|
||||||
local style = require "core.style"
|
local style = require "core.style"
|
||||||
local View = require "core.view"
|
local View = require "core.view"
|
||||||
|
local ContextMenu = require "core.contextmenu"
|
||||||
|
local RootView = require "core.rootview"
|
||||||
|
|
||||||
|
|
||||||
local default_treeview_size = 200 * SCALE
|
local default_treeview_size = 200 * SCALE
|
||||||
local tooltip_offset = style.font:get_height("A")
|
local tooltip_offset = style.font:get_height()
|
||||||
local tooltip_border = 1
|
local tooltip_border = 1
|
||||||
local tooltip_delay = 0.5
|
local tooltip_delay = 0.5
|
||||||
local tooltip_alpha = 255
|
local tooltip_alpha = 255
|
||||||
|
@ -210,7 +213,7 @@ end
|
||||||
|
|
||||||
function TreeView:on_mouse_pressed(button, x, y, clicks)
|
function TreeView:on_mouse_pressed(button, x, y, clicks)
|
||||||
local caught = TreeView.super.on_mouse_pressed(self, button, x, y, clicks)
|
local caught = TreeView.super.on_mouse_pressed(self, button, x, y, clicks)
|
||||||
if caught then
|
if caught or button ~= "left" then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local hovered_item = self.hovered_item
|
local hovered_item = self.hovered_item
|
||||||
|
@ -353,9 +356,10 @@ local treeview_node = node:split("left", view, {x = true}, true)
|
||||||
-- plugin to be independent of each other. In addition it is not the
|
-- plugin to be independent of each other. In addition it is not the
|
||||||
-- plugin module that plug itself in the active node but it is plugged here
|
-- plugin module that plug itself in the active node but it is plugged here
|
||||||
-- in the treeview node.
|
-- in the treeview node.
|
||||||
|
local toolbar_view = nil
|
||||||
local toolbar_plugin, ToolbarView = core.try(require, "plugins.toolbarview")
|
local toolbar_plugin, ToolbarView = core.try(require, "plugins.toolbarview")
|
||||||
if config.toolbarview ~= false and toolbar_plugin then
|
if config.toolbarview ~= false and toolbar_plugin then
|
||||||
local toolbar_view = ToolbarView()
|
toolbar_view = ToolbarView()
|
||||||
treeview_node:split("down", toolbar_view, {y = true})
|
treeview_node:split("down", toolbar_view, {y = true})
|
||||||
local min_toolbar_width = toolbar_view:get_min_width()
|
local min_toolbar_width = toolbar_view:get_min_width()
|
||||||
view:set_target_size("x", math.max(default_treeview_size, min_toolbar_width))
|
view:set_target_size("x", math.max(default_treeview_size, min_toolbar_width))
|
||||||
|
@ -366,12 +370,171 @@ if config.toolbarview ~= false and toolbar_plugin then
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Add a context menu to the treeview
|
||||||
|
local menu = ContextMenu()
|
||||||
|
|
||||||
-- register commands and keymap
|
local on_view_mouse_pressed = RootView.on_view_mouse_pressed
|
||||||
|
local on_mouse_moved = RootView.on_mouse_moved
|
||||||
|
local root_view_update = RootView.update
|
||||||
|
local root_view_draw = RootView.draw
|
||||||
|
|
||||||
|
function RootView:on_mouse_moved(...)
|
||||||
|
if menu:on_mouse_moved(...) then return end
|
||||||
|
on_mouse_moved(self, ...)
|
||||||
|
end
|
||||||
|
|
||||||
|
function RootView.on_view_mouse_pressed(button, x, y, clicks)
|
||||||
|
-- We give the priority to the menu to process mouse pressed events.
|
||||||
|
if button == "right" then
|
||||||
|
view.tooltip.alpha = 0
|
||||||
|
view.tooltip.x, view.tooltip.y = nil, nil
|
||||||
|
end
|
||||||
|
local handled = menu:on_mouse_pressed(button, x, y, clicks)
|
||||||
|
return handled or on_view_mouse_pressed(button, x, y, clicks)
|
||||||
|
end
|
||||||
|
|
||||||
|
function RootView:update(...)
|
||||||
|
root_view_update(self, ...)
|
||||||
|
menu:update()
|
||||||
|
end
|
||||||
|
|
||||||
|
function RootView:draw(...)
|
||||||
|
root_view_draw(self, ...)
|
||||||
|
menu:draw()
|
||||||
|
end
|
||||||
|
|
||||||
|
local function is_project_folder(path)
|
||||||
|
return common.basename(core.project_dir) == path
|
||||||
|
end
|
||||||
|
|
||||||
|
menu:register(function() return view.hovered_item end, {
|
||||||
|
{ text = "Open in System", command = "treeview:open-in-system" },
|
||||||
|
ContextMenu.DIVIDER
|
||||||
|
})
|
||||||
|
|
||||||
|
menu:register(
|
||||||
|
function()
|
||||||
|
return view.hovered_item
|
||||||
|
and not is_project_folder(view.hovered_item.filename)
|
||||||
|
end,
|
||||||
|
{
|
||||||
|
{ text = "Rename", command = "treeview:rename" },
|
||||||
|
{ text = "Delete", command = "treeview:delete" },
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
menu:register(
|
||||||
|
function()
|
||||||
|
return view.hovered_item and view.hovered_item.type == "dir"
|
||||||
|
end,
|
||||||
|
{
|
||||||
|
{ text = "New File", command = "treeview:new-file" },
|
||||||
|
{ text = "New Folder", command = "treeview:new-folder" },
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
-- Register the TreeView commands and keymap
|
||||||
command.add(nil, {
|
command.add(nil, {
|
||||||
["treeview:toggle"] = function()
|
["treeview:toggle"] = function()
|
||||||
view.visible = not view.visible
|
view.visible = not view.visible
|
||||||
end,
|
end,
|
||||||
|
|
||||||
|
["treeview:rename"] = function()
|
||||||
|
local old_filename = view.hovered_item.filename
|
||||||
|
core.command_view:set_text(old_filename)
|
||||||
|
core.command_view:enter("Rename", function(filename)
|
||||||
|
os.rename(old_filename, filename)
|
||||||
|
core.reschedule_project_scan()
|
||||||
|
core.log("Renamed \"%s\" to \"%s\"", old_filename, filename)
|
||||||
|
end, common.path_suggest)
|
||||||
|
end,
|
||||||
|
|
||||||
|
["treeview:new-file"] = function()
|
||||||
|
local dir_name = view.hovered_item.filename
|
||||||
|
if not is_project_folder(dir_name) then
|
||||||
|
core.command_view:set_text(dir_name .. "/")
|
||||||
|
end
|
||||||
|
core.command_view:enter("Filename", function(filename)
|
||||||
|
local doc_filename = core.project_dir .. PATHSEP .. filename
|
||||||
|
local file = io.open(doc_filename, "a+")
|
||||||
|
file:write("")
|
||||||
|
file:close()
|
||||||
|
core.root_view:open_doc(core.open_doc(doc_filename))
|
||||||
|
core.reschedule_project_scan()
|
||||||
|
core.log("Created %s", doc_filename)
|
||||||
|
end, common.path_suggest)
|
||||||
|
end,
|
||||||
|
|
||||||
|
["treeview:new-folder"] = function()
|
||||||
|
local dir_name = view.hovered_item.filename
|
||||||
|
if not is_project_folder(dir_name) then
|
||||||
|
core.command_view:set_text(dir_name .. "/")
|
||||||
|
end
|
||||||
|
core.command_view:enter("Folder Name", function(filename)
|
||||||
|
local dir_path = core.project_dir .. PATHSEP .. filename
|
||||||
|
common.mkdirp(dir_path)
|
||||||
|
core.reschedule_project_scan()
|
||||||
|
core.log("Created %s", dir_path)
|
||||||
|
end, common.path_suggest)
|
||||||
|
end,
|
||||||
|
|
||||||
|
["treeview:delete"] = function()
|
||||||
|
local filename = view.hovered_item.abs_filename
|
||||||
|
local relfilename = view.hovered_item.filename
|
||||||
|
local file_info = system.get_file_info(filename)
|
||||||
|
local file_type = file_info.type == "dir" and "Directory" or "File"
|
||||||
|
-- Ask before deleting
|
||||||
|
local opt = {
|
||||||
|
{ font = style.font, text = "Yes", default_yes = true },
|
||||||
|
{ font = style.font, text = "No" , default_no = true }
|
||||||
|
}
|
||||||
|
core.nag_view:show(
|
||||||
|
string.format("Delete %s", file_type),
|
||||||
|
string.format(
|
||||||
|
"Are you sure you want to delete the %s?\n%s: %s",
|
||||||
|
file_type:lower(), file_type, relfilename
|
||||||
|
),
|
||||||
|
opt,
|
||||||
|
function(item)
|
||||||
|
if item.text == "Yes" then
|
||||||
|
if file_info.type == "dir" then
|
||||||
|
local deleted, error, path = common.rm(filename, true)
|
||||||
|
if not deleted then
|
||||||
|
core.error("Error: %s - \"%s\" ", error, path)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local removed, error = os.remove(filename)
|
||||||
|
if not removed then
|
||||||
|
core.error("Error: %s - \"%s\"", error, filename)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
core.reschedule_project_scan()
|
||||||
|
core.log("Deleted \"%s\"", filename)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end,
|
||||||
|
|
||||||
|
["treeview:open-in-system"] = function()
|
||||||
|
local hovered_item = view.hovered_item
|
||||||
|
|
||||||
|
if PLATFORM == "Windows" then
|
||||||
|
system.exec("start " .. hovered_item.abs_filename)
|
||||||
|
elseif string.find(PLATFORM, "Mac") then
|
||||||
|
system.exec(string.format("open %q", hovered_item.abs_filename))
|
||||||
|
elseif PLATFORM == "Linux" then
|
||||||
|
system.exec(string.format("xdg-open %q", hovered_item.abs_filename))
|
||||||
|
end
|
||||||
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
keymap.add { ["ctrl+\\"] = "treeview:toggle" }
|
keymap.add { ["ctrl+\\"] = "treeview:toggle" }
|
||||||
|
|
||||||
|
-- Return the treeview with toolbar and contextmenu to allow
|
||||||
|
-- user or plugin modifications
|
||||||
|
view.toolbar = toolbar_view
|
||||||
|
view.contextmenu = menu
|
||||||
|
|
||||||
|
return view
|
||||||
|
|
Loading…
Reference in New Issue