local core = require "core" local command = require "core.command" local common = require "core.common" local View = require "core.view" local style = require "core.style" local BORDER_WIDTH = common.round(1 * SCALE) local UNDERLINE_WIDTH = common.round(2 * SCALE) local UNDERLINE_MARGIN = common.round(1 * SCALE) local noop = function() end local NagView = View:extend() function NagView:new() NagView.super.new(self) self.size.y = 0 self.force_focus = false self.queue = {} end function NagView:get_title() return self.title end function NagView:get_options_line_height() local max = 0 for _, opt in ipairs(self.options) do local lh = style.font:get_height(opt.text) if lh > max then max = lh end end return max end function NagView:get_line_height() return self.max_lh + 2 * BORDER_WIDTH + 2 * style.padding.y end function NagView:update() NagView.super.update(self) if core.active_view == self and self.title then self:move_towards(self.size, "y", self:get_line_height()) self:move_towards(self, "underline_progress", 1) else self:move_towards(self.size, "y", 0) end end function NagView:draw_overlay() local ox, oy = self:get_content_offset() oy = oy + self.size.y local w, h = core.root_view.size.x, core.root_view.size.y - oy core.root_view:defer_draw(function() renderer.draw_rect(ox, oy, w, h, style.nagbar_dim) end) end function NagView:change_hovered(i) if i ~= self.hovered_item then self.hovered_item = i self.underline_progress = 0 core.redraw = true end end function NagView:each_option() return coroutine.wrap(function() if not self.options then return end local opt, bw,bh,ox,oy bh = self.max_lh + 2 * BORDER_WIDTH + style.padding.y ox,oy = self:get_content_offset() ox = ox + self.size.x oy = oy + (self.size.y / 2) - (bh / 2) for i = #self.options, 1, -1 do opt = self.options[i] bw = opt.font:get_width(opt.text) + 2 * BORDER_WIDTH + style.padding.x ox = ox - bw - style.padding.x coroutine.yield(i, opt, ox,oy,bw,bh) end end) end function NagView:on_mouse_moved(mx, my, ...) NagView.super.on_mouse_moved(self, mx, my, ...) for i, _, x,y,w,h in self:each_option() do if mx >= x and my >= y and mx < x + w and my < y + h then self:change_hovered(i) break end end end function NagView:on_mouse_pressed(button, mx, my, clicks) if NagView.super.on_mouse_pressed(self, button, mx, my, clicks) then return end for i, _, x,y,w,h in self:each_option() do if mx >= x and my >= y and mx < x + w and my < y + h then self:change_hovered(i) command.perform "dialog:select" break end end end function NagView:on_text_input(text) if text:lower() == "y" then command.perform "dialog:select-yes" elseif text:lower() == "n" then command.perform "dialog:select-no" end end function NagView:draw() if self.size.y <= 0 or not self.title then return end self:draw_overlay() self:draw_background(style.nagbar) local ox, oy = self:get_content_offset() ox = ox + style.padding.x -- if there are other items, show it if #self.queue > 0 then local str = string.format("[%d]", #self.queue) ox = common.draw_text(style.font, style.nagbar_text, str, "left", ox, oy, self.size.x, self.size.y) ox = ox + style.padding.x end -- draw message common.draw_text(style.font, style.nagbar_text, self.message, "left", ox, oy, self.size.x, self.size.y) -- draw buttons for i, opt, bx,by,bw,bh in self:each_option() do local fw,fh = bw - 2 * BORDER_WIDTH, bh - 2 * BORDER_WIDTH local fx,fy = bx + BORDER_WIDTH, by + BORDER_WIDTH -- draw the button renderer.draw_rect(bx,by,bw,bh, style.nagbar_text) renderer.draw_rect(fx,fy,fw,fh, style.nagbar) if i == self.hovered_item then -- draw underline local uw = fw - 2 * UNDERLINE_MARGIN local halfuw = uw / 2 local lx = fx + UNDERLINE_MARGIN + halfuw - (halfuw * self.underline_progress) local ly = fy + fh - UNDERLINE_MARGIN - UNDERLINE_WIDTH uw = uw * self.underline_progress renderer.draw_rect(lx,ly,uw,UNDERLINE_WIDTH, style.nagbar_text) end common.draw_text(opt.font, style.nagbar_text, opt.text, "center", fx,fy,fw,fh) end end local function findindex(tbl, prop) for i, o in ipairs(tbl) do if o[prop] then return i end end end function NagView:next() local opts = table.remove(self.queue, 1) or {} self.title = opts.title self.message = opts.message self.options = opts.options self.on_selected = opts.on_selected if self.message and self.options then self.max_lh = math.max(style.font:get_height(self.message), self:get_options_line_height()) self:change_hovered(findindex(self.options, "default_yes")) end self.force_focus = self.message ~= nil core.set_active_view(self.message ~= nil and self or core.last_active_view) end function NagView:show(title, message, options, on_select) local opts = {} opts.title = assert(title, "No title") opts.message = assert(message, "No message") opts.options = assert(options, "No options") opts.on_selected = on_select or noop table.insert(self.queue, opts) if #self.queue > 0 and not self.title then self:next() end end command.add(NagView, { ["dialog:previous-entry"] = function() local v = core.active_view local hover = v.hovered_item or 1 v:change_hovered(hover == 1 and #v.options or hover - 1) end, ["dialog:next-entry"] = function() local v = core.active_view local hover = v.hovered_item or 1 v:change_hovered(hover == #v.options and 1 or hover + 1) end, ["dialog:select-yes"] = function() local v = core.active_view if v ~= core.nag_view then return end v:change_hovered(findindex(v.options, "default_yes")) command.perform "dialog:select" end, ["dialog:select-no"] = function() local v = core.active_view if v ~= core.nag_view then return end v:change_hovered(findindex(v.options, "default_no")) command.perform "dialog:select" end, ["dialog:select"] = function() local v = core.active_view if v.hovered_item then v.on_selected(v.options[v.hovered_item]) v:next() end end, }) return NagView