diff options
Diffstat (limited to 'data/plugins/treeview.lua')
| -rw-r--r-- | data/plugins/treeview.lua | 197 |
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" } |
