aboutsummaryrefslogtreecommitdiff
path: root/data/plugins/treeview.lua
diff options
context:
space:
mode:
Diffstat (limited to 'data/plugins/treeview.lua')
-rw-r--r--data/plugins/treeview.lua197
1 files changed, 197 insertions, 0 deletions
diff --git a/data/plugins/treeview.lua b/data/plugins/treeview.lua
new file mode 100644
index 00000000..10536bd7
--- /dev/null
+++ b/data/plugins/treeview.lua
@@ -0,0 +1,197 @@
+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 View = require "core.view"
+
+
+local TreeView = View:extend()
+
+
+local function get_depth(filename)
+ local n = 0
+ for sep in filename:gmatch("[\\/]") do
+ n = n + 1
+ end
+ return n
+end
+
+
+function TreeView:new()
+ TreeView.super.new(self)
+ self.scrollable = true
+ self.focusable = false
+ self.visible = true
+ self.cache = {}
+end
+
+
+function TreeView:get_cached(item)
+ local t = self.cache[item.filename]
+ if not t then
+ t = {}
+ t.filename = item.filename
+ t.abs_filename = system.absolute_path(item.filename)
+ t.path, t.name = t.filename:match("^(.*)[\\/](.+)$")
+ t.depth = get_depth(t.filename)
+ t.type = item.type
+ self.cache[t.filename] = t
+ end
+ return t
+end
+
+
+function TreeView:get_name()
+ return "Project"
+end
+
+
+function TreeView:get_item_height()
+ return style.font:get_height() + style.padding.y
+end
+
+
+function TreeView:check_cache()
+ -- invalidate cache's skip values if project_files has changed
+ if core.project_files ~= self.last_project_files then
+ for _, v in pairs(self.cache) do
+ v.skip = nil
+ end
+ self.last_project_files = core.project_files
+ end
+end
+
+
+function TreeView:each_item()
+ return coroutine.wrap(function()
+ self:check_cache()
+ local ox, oy = self:get_content_offset()
+ local y = oy + style.padding.y
+ local w = self.size.x
+ local h = self:get_item_height()
+
+ local i = 1
+ while i <= #core.project_files do
+ local item = core.project_files[i]
+ local cached = self:get_cached(item)
+
+ coroutine.yield(cached, ox, y, w, h)
+ y = y + h
+ i = i + 1
+
+ if not cached.expanded then
+ if cached.skip then
+ i = cached.skip
+ else
+ local depth = cached.depth
+ while i <= #core.project_files do
+ local filename = core.project_files[i].filename
+ if get_depth(filename) <= depth then break end
+ i = i + 1
+ end
+ cached.skip = i
+ end
+ end
+ end
+ end)
+end
+
+
+function TreeView:on_mouse_moved(px, py)
+ self.hovered_item = nil
+ for item, x,y,w,h in self:each_item() do
+ if px > x and py > y and px <= x + w and py <= y + h then
+ self.hovered_item = item
+ break
+ end
+ end
+end
+
+
+function TreeView:on_mouse_pressed(button, x, y)
+ if not self.hovered_item then
+ return
+ elseif self.hovered_item.type == "dir" then
+ self.hovered_item.expanded = not self.hovered_item.expanded
+ else
+ core.try(function()
+ core.root_view:open_doc(core.open_doc(self.hovered_item.filename))
+ end)
+ end
+end
+
+
+function TreeView:update()
+ self.scroll.to.y = math.max(0, self.scroll.to.y)
+
+ -- update width
+ local dest = self.visible and config.treeview_size or 0
+ self:move_towards(self.size, "x", dest)
+
+ TreeView.super.update(self)
+end
+
+
+function TreeView:draw()
+ self:draw_background(style.background2)
+
+ local h = self:get_item_height()
+ local icon_width = style.icon_font:get_width("D")
+ local spacing = style.font:get_width(" ") * 2
+ local root_depth = get_depth(core.project_dir) + 1
+
+ local doc = core.active_view.doc
+ local active_filename = doc and system.absolute_path(doc.filename or "")
+
+ for item, x,y,w,h in self:each_item() do
+ local color = style.text
+
+ -- highlight active_view doc
+ if item.abs_filename == active_filename then
+ color = style.accent
+ end
+
+ -- hovered item background
+ if item == self.hovered_item then
+ renderer.draw_rect(x, y, w, h, style.line_highlight)
+ color = style.accent
+ end
+
+ -- icons
+ x = x + (item.depth - root_depth) * style.padding.x + style.padding.x
+ if item.type == "dir" then
+ local icon1 = item.expanded and "e" or "c"
+ local icon2 = item.expanded and "D" or "d"
+ common.draw_text(style.icon_font, color, icon1, nil, x, y, 0, h)
+ x = x + style.padding.x
+ common.draw_text(style.icon_font, color, icon2, nil, x, y, 0, h)
+ x = x + icon_width
+ else
+ x = x + style.padding.x
+ common.draw_text(style.icon_font, color, "f", nil, x, y, 0, h)
+ x = x + icon_width
+ end
+
+ -- text
+ x = x + spacing
+ x = common.draw_text(style.font, color, item.name, nil, x, y, 0, h)
+ end
+end
+
+
+-- init
+local view = TreeView()
+local node = core.root_view:get_active_node()
+view.size.x = config.treeview_size
+node:split("left", view, true)
+
+-- register commands and keymap
+command.add(nil, {
+ ["treeview:toggle"] = function()
+ view.visible = not view.visible
+ end,
+})
+
+keymap.add { ["ctrl+\\"] = "treeview:toggle" }