diff --git a/data/core/logview.lua b/data/core/logview.lua index d7142fb5..fe18a7c6 100644 --- a/data/core/logview.lua +++ b/data/core/logview.lua @@ -1,14 +1,44 @@ local core = require "core" +local common = require "core.common" local style = require "core.style" local View = require "core.view" +local function lines(text) + if text == "" then return 0 end + local l = 1 + for _ in string.gmatch(text, "\n") do + l = l + 1 + end + return l +end + + +local item_height_result = {} + + +local function get_item_height(item) + local h = item_height_result[item] + if not h then + h = {} + local l = 1 + lines(item.text) + lines(item.info or "") + h.normal = style.font:get_height() + style.padding.y + h.expanded = l * style.font:get_height() + style.padding.y + h.current = h.normal + h.target = h.current + item_height_result[item] = h + end + return h +end + + local LogView = View:extend() function LogView:new() LogView.super.new(self) self.last_item = core.log_items[#core.log_items] + self.expanding = {} self.scrollable = true self.yoffset = 0 end @@ -19,6 +49,55 @@ function LogView:get_name() end +local function is_expanded(item) + local item_height = get_item_height(item) + return item_height.target == item_height.expanded +end + + +function LogView:expand_item(item) + item = get_item_height(item) + item.target = item.target == item.expanded and item.normal or item.expanded + table.insert(self.expanding, item) +end + + +function LogView:each_item() + local x, y = self:get_content_offset() + y = y + style.padding.y + self.yoffset + return coroutine.wrap(function() + for i = #core.log_items, 1, -1 do + local item = core.log_items[i] + local h = get_item_height(item).current + coroutine.yield(i, item, x, y, self.size.x, h) + y = y + h + end + end) +end + + +function LogView:on_mouse_moved(px, py, ...) + LogView.super.on_mouse_moved(self, px, py, ...) + local hovered = false + 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 + hovered = true + self.hovered_item = item + break + end + end + if not hovered then self.hovered_item = nil end +end + + +function LogView:on_mouse_pressed(button, mx, my, clicks) + if LogView.super.on_mouse_pressed(self, button, mx, my, clicks) then return end + if self.hovered_item then + self:expand_item(self.hovered_item) + end +end + + function LogView:update() local item = core.log_items[#core.log_items] if self.last_item ~= item then @@ -27,6 +106,14 @@ function LogView:update() self.yoffset = -(style.font:get_height() + style.padding.y) end + local expanding = self.expanding[1] + if expanding then + self:move_towards(expanding, "current", expanding.target) + if expanding.current == expanding.target then + table.remove(self.expanding, 1) + end + end + self:move_towards("yoffset", 0) LogView.super.update(self) @@ -45,28 +132,57 @@ local function draw_text_multiline(font, text, x, y, color) end +local function draw_text_elipsis(font, color, text, x, y, w, h, elipsis_style) + elipsis_style = elipsis_style or "end" + local c = font:get_width("_") + local approxc = math.floor(w / c) + if #text > approxc then + if elipsis_style == "end" then + text = string.sub(text, 1, approxc - 3) .. "..." + elseif elipsis_style == "middle" then + local mid = math.floor(#text / 2) + text = string.sub(text, 1, mid - 3) .. "..." .. string.sub(text, mid) + end + end + return common.draw_text(font, color, text, "left", x, y, w, h) +end + + function LogView:draw() self:draw_background(style.background) - local ox, oy = self:get_content_offset() local th = style.font:get_height() - local y = oy + style.padding.y + self.yoffset - - for i = #core.log_items, 1, -1 do - local x = ox + style.padding.x - local item = core.log_items[i] - local time = os.date(nil, item.time) - x = renderer.draw_text(style.font, time, x, y, style.dim) + local lh = th + style.padding.y -- for one line + for _, item, x, y, w, h in self:each_item() do + core.push_clip_rect(x, y, w, h) x = x + style.padding.x - local subx = x - x, y = draw_text_multiline(style.font, item.text, x, y, style.text) - renderer.draw_text(style.font, " at " .. item.at, x, y, style.dim) - y = y + th - if item.info then - subx, y = draw_text_multiline(style.font, item.info, subx, y, style.dim) + + local time = os.date(nil, item.time) + x = common.draw_text(style.font, style.dim, time, "left", x, y, w, lh) + x = x + style.padding.x + + x = common.draw_text(style.code_font, style.dim, is_expanded(item) and "-" or "+", "left", x, y, w, lh) + x = x + style.padding.x + w = w - (x - self:get_content_offset()) + + if is_expanded(item) then + y = y + common.round(style.padding.y / 2) + draw_text_multiline(style.font, item.text, x, y, style.text) + y = y + th + + local at = "at " .. common.home_encode(item.at) + draw_text_elipsis(style.font, style.dim, at, x, y, w, lh, "middle") + y = y + th + + if item.info then + draw_text_multiline(style.font, item.info, x, y, style.dim) + end + else + draw_text_elipsis(style.font, style.text, item.text, x, y, w, lh) y = y + th end - y = y + style.padding.y + + core.pop_clip_rect() end end